我该如何实例化一个泛型类型?

3

我正在为Java项目制作一些数学课程。我有一个Vector3类,但我还需要Vector4和Vector2,但显然我不想复制黏贴我的代码3次。

所以我做了一个Vector类,它将是所有向量的母类。我可以只有Vector类没有子类,但我更喜欢拥有这些子类,因为我可以在它们上面添加特定的东西,比如在Vector3中添加欧拉角运算,而且我也想使用Vector4作为Quaternion的母类。无论如何,这是我的简化Vector类:

public class Vector {

    public double[] values;

    public Vector(double[] values) {
        this.values = values;
    }

    public int getLength() { 
        return values.length; 
    }

    public static <T extends Vector> T multiply(T vector, double k) {

        double[] values = new double[vector.getLength()];
        for(int i = 0; i < values.length; i++)
            values[i] = k* vector.values[i];
        return new T(values);
    }
}

public class Vector3 extends Vector {
    public Vector3(double[] values) {
        super(values);
    }
}

问题在于编译器不允许我实例化一个T:"类型参数T不能直接实例化"。但我需要这个T,因为我需要返回的向量与发送的向量是相同类型的。
如果我执行 new Vector2(4,2).multiply(42),我需要得到一个Vector2而不是Vector。我也可以在Vector2中编写一个multiply方法,调用Vector的multiply然后将值复制到Vector2中,但是,1.这很糟糕,2.这将导致子向量之间存在大量重复代码,3.我需要性能,所以这不是理想方法。
我知道我可以使用反射来解决问题,但这些方法对性能至关重要,所以必须尽可能简单。
我还考虑过修改参数中的向量,以便我不必实例化新向量,但这是一个非常糟糕的主意,因为它可能会导致奇怪的行为。
欢迎任何帮助。
2个回答

3
最简单的方法似乎是在 Vector 类上有一个实例方法:
Vector make(double[] values) {
  return new Vector(values);
}

然后在每个子类中覆盖它,利用协变返回类型:

class Vector3 extends Vector {
  //...

  @Override Vector3 make(double[] values) {
    return new Vector3(values);
  }

  //...
}

然后您可以在乘法方法中调用它。

return vector.make(values);

但是,老实说,我不会尝试将向量的长度编码到类型中。当你需要一个由57032个元素组成的向量时,你肯定不想为此创建一个特定的类吧?如果您有两个不同的 Vector 子类具有相同数量的元素:它们是否兼容(例如用于加法)?

处理向量更自然的语言(例如MATLAB)不会将其构建到类型中;问问自己,这里是否真的需要它。


你可以通过基类“Vector”来表示一个向量57032。然而,除了子类之外,还可以在基类中包含特定的操作(例如欧拉角操作),如果向量长度不正确,则抛出“IllegalStateException”。 - Izruo
@Izruo 是的,你可以使用基类。但是一旦你转到了基类,就很难回到特定的类了。 - Andy Turner
我认为你误解了,我写的两个句子是完全独立的。第一个基于多个类的想法(显然没有必要使用具有57032个元素的Vector3),而第二个则基于仅一个类的思想(“而不是子类”)。 - Izruo
@Izruo 我只是在评论你的第一句话。 - Andy Turner
这个解决方案可行,但我必须将Vector抽象化才能应用它。所以我不能再实例化它了,这不好,因为我想能够创建任意大小的向量而不必创建一个新类。 我尝试使用一个带有make方法的接口。但它强制我用IVector(新接口)替换T extends Vector。所以在multiply方法中使用的所有方法都必须在接口中(例如getLength())。这不是理想的,但也不是那么糟糕。 - KubbyDev
@KubbyDev,你不需要让它抽象化:你可以简单地让它返回 new Vector(values) - Andy Turner

2
如果性能很关键,您可能会考虑让乘法方法改变向量的状态而不是创建一个新的向量。在我看来,只要这是一种确定性和有文档记录的行为,就不奇怪。
但是对于一个不可变的向量类,您需要克隆向量。
public class Vector implements Cloneable {
    // not a good idea to make it public, if you don't want any changes here
    private double[] values;

    public static <T extends Vector> T multiply(T vector, double k) {
        Vector temp = vector.clone();
        for(int i = 0; i < temp.values.length; i++)
            temp.values[i] = k * temp.values[i];
        // the clone method guarantees that 'temp' is of type T,
        // but since it is not generic, the compiler cannot check it
        @SuppressWarnings("unchecked") 
        T result = (T)temp;
        return result;
    }

    protected Vector clone() {
        try {
            Vector vector = (Vector)super.clone();
            vector.values = Arrays.copyOf(values, values.length);
            return vector;
        } catch (final CloneNotSupportedException exc) {
            // this is a weird design choice of `Object.clone()`, too,
            // but back then, we did not have annotations or anything equivalent
            throw new AssertionError("we forgot to implement java.lang.Cloneable", exc);
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接