【问题标题】:Advanced Java idiom for 2+ classes implementing a generic interface用于实现通用接口的 2 个以上类的高级 Java 习惯用法
【发布时间】:2015-12-05 22:33:50
【问题描述】:

我遇到了以下情况,想知道我实现它的方式在可重用性和速度方面是否良好,我也有兴趣拥有一个实际可编译的解决方案,因为下面的解决方案无法编译(我希望有人找到了罪魁祸首,并对此有一个简单而优雅的想法)。

有两个 Java 类“Vec3F”和“Vec3”实现了浮点和双精度类型的基本向量数学。两者都实现了如下接口:

public final class Vec3 implements Vec<Vec3> {
//..
    public double distance(Vec3 other) { /*..*/ }
}

public interface Vec<V> {

    double distance(V other);   
}    

我这样做是为了让一些算法适用于这两种类型的向量实现,问题就来了:

public class Toolbox {

public static <T> double getAllDistances(List<Vec<T>> points) {
    Vec<T> prevPoint = points.get(0);
    Vec<T> point;
    double sum = 0.0;
    int len = points.size();
    for (int i=1;i<len; i++) {
        point = points.get(i);
        //-> this doesn't compile: 
        //The method distance(T) in the type Vec<T> is not applicable for the arguments (Vec<T>)    
        sum+=point.distance(prevPoint);
        prevPoint = point;
    }
    return sum;
}
}

我知道我可以实现两次“getAllDistances”,但这是我想要避免的。我希望有一个 Toolbox 类,它可以根据接口中声明的方法执行一些元算法。 我也想避免改变例如的方法实现distance(Vec3 other) 让接口通过(因为它直接使用例如 other.x*other.x 以避免调用任何 getter)。

我很高兴对此有一些想法,并希望问题足够清晰和具体,在此先感谢!

【问题讨论】:

  • 这不是CRTP的工作吗?
  • 您需要澄清类型代表什么。 “点”真的是 T 的向量还是 T 对象? Vec和Vec3是什么关系?距离(V other)中的“其他”是一个点吗? Vec3F 在哪里?
  • Vec3F 实现了与 Vec3 相同的接口,但使用 float x,y,z;而不是双 x,y,z;在它的所有方法中。其他在 Vec3 中实现时为“Vec3”类型,在浮点 Vec3F 类实现距离时为“Vec3F”类型。

标签: java algorithm generics meta typesafe


【解决方案1】:

我想我找到了一种类型安全的方法:

public static <T extends Vec<T>> double getAllDistances(List<T> points) {
    T prevPoint = points.get(0);
    T point;
    double sum = 0.0;
    int len = points.size();
    for (int i=1;i<len; i++) {
        point = points.get(i);

        sum+=point.distance(prevPoint);
        prevPoint = point;
    }
    return sum;
}

这通过从参数中的 List > 移开并将其定义为 T 的边界来解决编译器问题。 除了冗长的方法声明之外,它看起来非常简单直观。

【讨论】:

  • 另外,不要犹豫在TVec&lt;T&gt; 之间转换的丑陋解决方案。你知道它们是同一个东西,所以它是安全的。
【解决方案2】:

您可能希望将 Vec 接口定义为:

public interface Vec<V extends Vec<V>>

这可能很难看,因为乍一看似乎是非法的前向引用,但它完全有效且正常(实际上在java.lang.Enum 中使用)。

只要有Vec&lt;T&gt; 实例,就必须将T 而非Vec&lt;T&gt; 传递给distance 方法。所以你需要像这样定义你的 getAllDistances 方法:

public static <T extends Vec<T>> double getAllDistances(List<T> points) {
    T prevPoint = points.get(0);
    T point;

【讨论】:

  • 接口定义确实有点难看。但是,如果没有这个定义,它也可以工作。它到底是做什么用的?
  • 它将 V 限制为仅实现 Vec。这似乎是合乎逻辑的事情,但正如你所说,这不是绝对必要的。
  • 在这种情况下确实不需要 V 的界限。这类似于Collections.sort(List)。所需要的只是元素类型可以 compareTo/calcDistanceTo 相同的类型。
  • 它很漂亮,但不知何故很难把绑定的东西弄好,大多数时候我碰巧经常封装它们,谢谢!
  • 如果我将 Vec > 添加到接口的定义中,则始终需要在方法中使用 > 如果 Vec 在参数中。我仍然没有真正得到正确的使用,或者仅仅是使用它的“好处”。
猜你喜欢
  • 2010-09-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-18
  • 1970-01-01
  • 2018-06-23
  • 2022-11-28
  • 1970-01-01
相关资源
最近更新 更多