我是否可以要求实现接口的类具有某个静态字段或方法,并通过泛型类型参数访问/调用该字段或方法?
我有一个界面Arithmetical<T>
,它指定了T plus(T o)
和T times(T o)
等多个功能。我还有一个Vector<N extends Arithmetical<N>>
类,它用于具有类型N
的组件的向量(具有可变维度)。但是,在尝试实施dot product时,我遇到了一个问题。
我想实现方法N dot(Vector<N> o)
。为此,我计划从N
的零开始,并遍历两个Vector<N>
s'List<N>
s,将每对元素的乘积添加到我的总数中。有没有办法在Arithmetical<T>
中指定所有实现类必须具有静态(最好是最终)字段ZERO
并且开始dot(Vector<N> o)
的主体,其内容为{{1} }}?
如果没有,那么这个问题还有什么方法呢?我想允许0维向量,所以我不能仅仅通过将向量的第一个组件相乘来开始。有没有办法实例化泛型类型的对象,所以我只能在N sum = N.ZERO;
中指定T zero()
方法?
我有理由不使用Java的数字类型 - 我希望有复杂组件的向量。
这是Arithmetical:
Arithmetical<T>
载体:
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?
}
复杂:
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.
}
}
我对Java(以及一般的编程)很陌生;我可能不会理解复杂的(ha)解释和解决方法。
谢谢!
(Python是建议的标签...... Huh.)
答案 0 :(得分:6)
你需要一个&#34;零&#34;对于每种可能的实现类型。界面中的常量不会发生,因为常量不能被覆盖并且必须保持不变。
解决方案是在Arithmetical
界面添加新方法:
public T zero();
每个实现都被迫实现它并返回自己的零版本。在这种情况下,您将其作为添加的起点;它是附加的身份。
Complex
类实现看起来像这样。
@Override
public Complex zero() {
return ZERO;
}
如果你的实例是可变的,那么就不要使用常数;只需返回new Complex(0, 0)
。
另一个想法是借用Stream
在reduce
项目时将其与单个项目相结合的方式 - 获取表示初始状态的标识值,即尚未收集任何项目 - 零。
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.
}
来电者必须提供身份价值。
Complex dotProduct = vectorOfComplex.dotProduct(otherVector, new Complex(0, 0));
答案 1 :(得分:3)
我可以在这里放一行,需要使用Class Complex(下面)来定义ZERO吗?
没有。您可以做的最好的事情是定义一个接口,例如:
interface ZeroProvider<A extends Arithmetical<A>> {
A zero();
}
然后提供一个需要提供零的兼容实例,例如:
class ComplexZeroProvider implements ZeroProvider<Complex> {
public Complex zero() { return new Complex(0, 0); }
}
答案 2 :(得分:1)
在这种情况下,你有时可以使用反射做些什么。如果您将以下方法放在Vector
类中,它将调用静态方法N.zero()
(下面有警告):
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);
}
}
但是,了解这实际上是做什么很重要。这是从直接超类this
的类文件中获取类型参数。换句话说,实际上必须有一个带有实际类型参数的this
超类(它是一个类)。
通常的习惯用法就是你要创建所有这些矢量:
new Vector<Complex>() {}
而不是:
new Vector<Complex>()
或者你要声明这样的子类:
public class Vector<N> {
// ...
public static class OfComplex extends Vector<Complex> {
}
}
由于您需要一个类型参数为类的实际超类,因此以下示例中的实例化将失败:
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) {};
}
在你的情况下,我认为其他答案中的建议更好,但我想发布这个答案以及有时不包含的正确解释和警告。在某些情况下,这种技术实际上是好的,主要是当你对所讨论的类如何扩展有非常严格的限制时。 Guava TypeToken
也会为你做一些反思。
此外,这是最好的Java可以完成你所要求的(目前),所以值得指出的只是比较。