具有继承性和协变性的Java泛型

具有继承性和协变性的Java泛型,java,generics,wildcard,covariance,Java,Generics,Wildcard,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 {

在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) {}
}
从我所看到的,我想做的是协方差,但在目前的情况下我不能做,因为我需要使用通配符。但是我已经读到,你不能用泛型和通配符来做协方差

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

谢谢你的帮助

但我读过,你不能用泛型和 通配符

此语句不是关于协变返回类型的,但它意味着您不能这样做:

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

这是非法的。

您必须将
B
设为泛型,但您可以对泛型参数进行绑定,使其至少是一个
元素

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

interface C<T extends Element> extends B<T> {}
接口B扩展了{}
接口C扩展了B{}
那么你能得到的最接近的是:

 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;}
}
D类实现了B{
@凌驾
公共T doSomething(T foo){returnnull;}
}
类E扩展了D实现了C{
@凌驾
公共ChildElement doSomething(ChildElement foo){return null;}
}

编译。您描述的接口不能是协变的。您希望/期望此代码发生什么情况

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

这必须正确编译-但是如果
E#doSomething
期望一个
ChildElement
我们在这里会失败。

除非你实现
a
而不是
a
,否则这是不可能的。这就是我想的。。所以我需要在任何情况下打破依赖关系?如果一个类实现了B(即使是通过C间接实现的),那么它必须允许a,这很好!这正是我所需要的,非常感谢,我对Java中泛型的所有规则都有些迷茫。因此,我们在这里所做的基本上是使用带边界的泛型而不是简单的参数来启用类E中的ChildElement?是的-边界捕获了这样一个概念,即接口子类B和C应该只应用于元素的类/子类,但将规范推迟到具体的类。仅供参考,协方差仅在重写其签名包含类本身类型的方法时起作用-然后您可以使用子类的类型进行重写。但它不适用于泛型参数。因此,返回ChildElement是合法的,但不能让ChildElement替代E中方法参数中的元素?@Hawknight是的,这是重写约束,方法的签名必须匹配,但允许协变返回类型。感谢您的澄清。因此,协方差在Java中仅被接受为协方差返回类型@Hawknight实际上协方差意味着,如果
超类
是超类,
子类
是子类,那么使用is-a关系,您可以通过引用
超类
对象来引用
子类
对象:
超类obj=new Subclass()
(数组也是如此。但对于泛型,情况并非如此:
List
of
Number
-s不是
List
of
Integer
-s,如上例所示。
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;}
}
class OtherElement extends Element{}
Element oe = new OtherElement();
E e = new E();
B b = e;
b.doSomething(oe);