Can I require classes implementing an interface to have a certain static field or method and access/invoke that field or method through a generic type argument?
I have an interface, Arithmetical<T>
, which specifies several functions like T plus(T o)
and T times(T o)
. I have as well a Vector<N extends Arithmetical<N>>
class, which is intended for vectors (of variable dimension) with components of type N
. I ran into an issue, however, when trying to implement the dot product.
I want to implement the method N dot(Vector<N> o)
. For this, I plan to start with whatever N
's zero is and iterate through both Vector<N>
s' List<N>
s, adding the product of each pair of elements to my total. Is there a way to specify in Arithmetical<T>
that all implementing classes must have a static (and preferably final) field ZERO
and start dot(Vector<N> o)
's body with something along the lines of N sum = N.ZERO;
?
If not, what other approaches might there be to this problem? I want to allow 0-dimensional vectors, so I can't just begin by multiplying the vectors' first components. Is there a way to instantiate an object of a generic type, so I can merely specify a T zero()
method in Arithmetical<T>
?
I have a reason for not using Java's numerical types—I want to have vectors with complex components.
Here's Arithmetical:
public interface Arithmetical<T> {
public T plus(T o);
public T minus(T o);
public T negate();
public T times(T o);
public T over(T o);
public T inverse();
// Can I put a line here that requires class Complex (below) to define ZERO?
}
Vector:
public class Vector<N extends Arithmetical<N>> {
private List<N> components;
public Vector<N>(List<N> cs) {
this.components = new ArrayList<N>(cs);
}
public N dot(Vector<N> o) {
// Here's where I need help.
}
}
And Complex:
public class Complex implements Arithmetical<Complex> {
public static final Complex ZERO = new Complex(0, 0); // Can I access this value through N if <N extends Arithmetical<N>>?
private double real;
private double imag;
public Complex(double r, double i) {
this.real = r;
this.imag = i;
}
/* Implementation of Arithmetical<Complex> (and some more stuff) not shown... */
}
I'm quite new to Java (and programming in general); I will likely not understand complex (ha) explanations and workarounds.
Thanks!
(Python is a suggested tag... Huh.)
You need a "zero" for every possible implementation type. A constant in the interface won't do, because a constant cannot be overridden and must remain the same.
The solution is to add a new method to your Arithmetical
interface:
public T zero();
Each implementation is forced to implement this and return its own version of zero. In this case, you're using it as a starting point for adding; it's the additive identity.
The Complex
class implementation would look like this.
@Override
public Complex zero() {
return ZERO;
}
If your instances are mutable, then don't use a constant; just return new Complex(0, 0)
.
Another idea is to borrow from what Stream
s do when reduce
-ing items and combining them to one single item -- take an identity value that represents the initial state, i.e. no items collected yet -- zero.
public N dot(Vector<N> o, N identity) {
N dotProduct = identity;
// Perform operations on each item in your collection
// to accumulate and return a dot product.
}
The caller will have to supply the identity value.
Complex dotProduct = vectorOfComplex.dotProduct(otherVector, new Complex(0, 0));
Can I put a line here that requires class Complex (below) to define ZERO?
No. The best you can do is to define an interface, for example:
interface ZeroProvider<A extends Arithmetical<A>> {
A zero();
}
and then supply a compatible instance of that where you need to provide a zero, for example:
class ComplexZeroProvider implements ZeroProvider<Complex> {
public Complex zero() { return new Complex(0, 0); }
}
There's something you can do sometimes using reflection in situations like this. If you put the following method in the Vector
class, it will invoke a static method N.zero()
(with caveats, below):
protected N zero() {
try {
Type s = getClass().getGenericSuperclass();
@SuppressWarnings("unchecked")
Class<N> n = (Class<N>) ((ParameterizedType) s).getActualTypeArguments()[0];
Method zero = n.getMethod("zero");
return n.cast(zero.invoke(null));
} catch (RuntimeException | ReflectiveOperationException x) {
// probably better to make a custom exception type
throw new IllegalArgumentException("illegal type argument", x);
}
}
However, it's important to understand what this is actually doing. This is getting the type argument from the class file of the direct superclass of this
. In other words, there must actually be a superclass of this
with an actual type argument (which is a class).
The usual idiom then is that you'd create all of your vectors like this:
new Vector<Complex>() {}
instead of this:
new Vector<Complex>()
Or you'd declare subclasses like this:
public class Vector<N> {
// ...
public static class OfComplex extends Vector<Complex> {
}
}
Since you need an actual superclass with a type argument which is a class, instantiations like in the following examples will fail:
new Vector<Complex>()
new Vector() // never use this anyway
new Vector() {} // never use this anyway
// also, you can't do stuff like this:
public Vector<T> copy() {
return new Vector<T>(this) {};
}
In your case I think the suggestions in the other answers are better, but I wanted to post this answer along with the proper explanation and caveats which are sometimes not included. There are cases where this technique is actually good, mainly when you have pretty tight restrictions on how the class in question is extended. Guava TypeToken
will also do some of the reflection for you.
Also, this is the best Java can do at doing exactly what you're asking for (at the moment), so it's worthwhile to point out just as a comparison.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With