在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) {}
}
这种情况是可行的,但我希望能够为E类做这件事:
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;}
}
其进行编译。
但我读到过,你不能用泛型和通配符。
这句话不是关于协变返回类型的,但它意味着你不能这样做:
List<Number> l1 = new ArrayList<>();
List<Integer> l2 = new ArrayList<>();
l1 = l2; //illegal
但它也是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) {}
}
这是违法的。
您所描述的接口不能是协变的。您希望/期望此代码发生什么?
class OtherElement extends Element{}
Element oe = new OtherElement();
E e = new E();
B b = e;
b.doSomething(oe);
这必须正确编译,但如果E#doSomething
期望ChildElement
,那么这里就会失败。