Java 具有继承和协变的泛型

Java Generics with inheritance and covariance

我很难理解在 Java 中使用泛型时如何正确使用协变和通配符。

我正在努力避免使用强制转换。

这是我的情况:

class Element {}

class ChildElement extends Element {}

interface A<T> {
    T doSomething(T foo);
}

interface B extends A<Element> {}

interface C extends B {}

class D implements B {

    @Override
    public Element doSomething(Element foo) {} 
}

class E extends D implements C {
    @Override
    public Element doSomething(Element foo) {}
}

这种情况可行,但我希望能够为 class E :

执行此操作
class E extends D implements C {

    @Override
    public ChildElement doSomething(ChildEment foo) {}
}

据我所见,我想做的是协方差,但在目前的情况下我做不到,因为我需要使用通配符。但我读到你不能用泛型和通配符进行协变。

这个问题有什么解决办法吗?我想保持每个 classes 之间的强依赖性。

感谢您的帮助!

But I've read that you can't do covariance with generics and wildcards.

这个声明不是关于协变return类型的,但是这意味着你不能这样做:

List<Number> l1 = new ArrayList<>();
List<Integer> l2 = new ArrayList<>();
l1 = l2; //illegal

但是T它也是T的任何子类。所以像这样:

interface Interface<T> {
    T method();
}

class SomeClass implements Interface<Number>{
    @Override
    public Float method() {
        return 1F;
    }
}

工作正常。但是子类(实现)和超类(接口)的方法签名必须匹配。所以在你的例子中

class E extends D implements C {

    @Override
    public ChildElement doSomething(ChildEment foo) {}
}

这是非法的。

您必须使 B 通用,但您可以对通用参数设置一个界限,使其至少为 Element:

interface B<T extends Element> extends A<T> {}

interface C<T extends Element> extends B<T> {}

那么你能得到的最接近的是:

 class D<T extends Element> implements B<T> {
    @Override
    public T doSomething(T foo) { return null;}
}

class E extends D<ChildElement> implements C<ChildElement> {
    @Override
    public ChildElement doSomething(ChildElement foo) { return null;}
}

编译。

您描述的接口不能是协变的。您 want/expect 使用此代码会发生什么情况?

class OtherElement extends Element{}
Element oe = new OtherElement();
E e = new E();
B b = e;
b.doSomething(oe);

这编译正确,因为它必须 - 但如果 E#doSomething 预期 ChildElement 我们会在这里失败。