I'm making some maths classes for a project in Java. I have a Vector3 class, but I will also need Vector4 and Vector2, but obviously I don't want to copy paste my code 3 times.
So what I did is a Vector class that will be the mother class of all vectors. I could just have the Vector class with no child, but I prefer having these child classes because I can add specific things on them, like euler angles operations in Vector3 and also I want to use Vector4 as a mother class for Quaternion. Anyways, here is my simplified 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);
}
}
The problem is that the compiler won't let me instantiate a T: "Type parameter T cannot be instantiated directly". But I need this T because I need the returned vector to be the same type as the sent one.
If I do new Vector2(4,2).multiply(42), I need to get a Vector2 and not a Vector. I could also make a multiply method in Vector2 that calls the Vector multiply and then copies the values in a Vector2 but 1. it's awful, 2. that would imply a lot of copy paste between the child vectors, 3. I need performance, so that's not ideal.
I know I can use reflection to solve the problem, but these methods are performance critical so I have to keep it as simple as possible.
I also thought about altering the vector in parameter so I don't have to instantiate a new one, but that's a really bad idea because it can cause weird behavior.
Any help is appreciated.
If it is performance critical you might actually think about having the multiply method altering the state of the vector instead of creating a new one. In my opinion, it is not weird, as long as it is a deterministic and documented behavior.
For an immutable vector class, however, you need to clone
the vector.
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);
}
}
}