如何实例化泛型类型?

How do I instantiate a generic type?

我正在为 Java 中的一个项目做一些数学运算 classes。我有一个 Vector3 class,但我还需要 Vector4 和 Vector2,但显然我不想复制粘贴我的代码 3 次。

所以我所做的是一个 Vector class,它将成为所有向量的母亲 class。我可以只使用没有 child 的 Vector class,但我更喜欢使用这些 child class,因为我可以在它们上面添加特定的东西,比如 Vector3 中的欧拉角操作而且我想使用 Vector4 作为四元数的母 class。无论如何,这是我简化的 Vector class:

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:"Type parameter T cannot be instantiated directly"。但是我需要这个 T 因为我需要返回的向量与发送的向量类型相同。

如果我执行 new Vector2(4,2).multiply(42),我需要得到一个 Vector2 而不是一个 Vector。我还可以在 Vector2 中创建一个乘法方法,调用 Vector multiply,然后复制 Vector2 中的值,但是 1. 这太糟糕了,2. 这意味着 child 向量之间有很多复制粘贴,3. 我需要性能,所以这并不理想。

我知道我可以使用反射来解决问题,但这些方法对性能至关重要,所以我必须尽可能简单。

我还考虑过更改参数中的向量,这样我就不必实例化一个新的向量,但这是一个非常糟糕的主意,因为它会导致奇怪的行为。

感谢任何帮助。

如果它对性能至关重要,您实际上可能会考虑让 multiply 方法改变向量的状态,而不是创建一个新的向量。在我看来,这并不奇怪,只要它是确定性的和记录在案的行为即可。

但是,对于不可变向量 class,您需要 clone 向量。

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);
        }
    }
}

最简单的方法似乎是在 Vector class:

上有一个实例方法
Vector make(double[] values) {
  return new Vector(values);
}

然后在每个子 class 中重写,使用协变 return 类型:

class Vector3 extends Vector {
  //...

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

  //...
}

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

return vector.make(values);

但老实说,我不会尝试将向量的长度编码为类型。当你需要一个包含 57032 个元素的向量时会发生什么?您肯定不想为此创建一个特定的 class ,对吗?如果 Vector 的两个不同子 class 具有相同数量的元素,会发生什么情况:它们是否兼容(例如添加)?

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