访问泛型

时间:2018-03-14 21:01:43

标签: java generics inheritance interface

我是否可以要求实现接口的类具有某个静态字段或方法,并通过泛型类型参数访问/调用该字段或方法?

我有一个界面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.

3 个答案:

答案 0 :(得分:6)

你需要一个&#34;零&#34;对于每种可能的实现类型。界面中的常量不会发生,因为常量不能被覆盖并且必须保持不变。

解决方案是在Arithmetical界面添加新方法:

public T zero();

每个实现都被迫实现它并返回自己的零版本。在这种情况下,您将其作为添加的起点;它是附加的身份。

Complex类实现看起来像这样。

@Override
public Complex zero() {
    return ZERO;
}

如果你的实例是可变的,那么就不要使用常数;只需返回new Complex(0, 0)

另一个想法是借用Streamreduce项目时将其与单个项目相结合的方式 - 获取表示初始状态的标识值,即尚未收集任何项目 - 零。

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可以完成你所要求的(目前),所以值得指出的只是比较。