public class Abc {
public void process(List<A> a) {
}
public static void main(String[] args) {
Abc a = new Abc();
List<A> alist = new ArrayList<A>();
List<B> blist = new ArrayList<B>();
List<C> clist = new ArrayList<C>();
a.process(clist);
}
}
class A {
}
class B extends A {
}
class C extends B {
}
为什么使用此代码进行编译问题?根据面向对象的编程,这个
应该没有任何问题答案 0 :(得分:2)
即使C
是A
的子类,List<C>
不是 List<A>
的子类,因此也是编译问题。
这是仿制药之间常见的混淆,但简单来说,问题就在上面。
尝试将功能签名修改为
public void process(List<? extends A> a)
<? extends A>
事件是有界通配符。这些通配符的使用仅适用于这些类型的问题,您需要将任何类上限与您需要的A
类相匹配。阅读更多相关信息here。
但请记住,这会对您的List <? extends A>
参数施加一些限制。例如,您无法在列表中添加新元素(null
除外),因为Java不知道<? extends A>
的实际类型(可以是B
,C
或你有什么,因此它完全阻止你做那种事。
答案 1 :(得分:2)
香蕉是水果。香蕉列表不是水果列表。
如果是的话,你可以将一个苹果放入香蕉列表中。这将是非常反直觉的。
值得阅读co和contra-variance,或许可以查看this SO answer关于此类问题和PECS助记符。在上面的示例中,您的列表是提供商,因此您的方法签名应采用List<? extends A>
答案 2 :(得分:1)
这称为协方差,并不安全。
问题在于,如果这是合法的,那么Process
课程可以将B
放入您的List<C>
答案 3 :(得分:0)
添加其他答案。
如果在Java中允许上述内容,那么您可以编写以下内容:
public void process(List<A> a) {
a.Add(new A());
}
public static void main(String[] args) {
Abc a = new Abc();
List<A> alist = new ArrayList<A>();
List<B> blist = new ArrayList<B>();
List<C> clist = new ArrayList<C>();
a.process(clist);
for ( C c : clist){
System.out.println(c.Name);
}
}
class A {
}
class B extends A {
}
class C extends B {
String Name = "Class C";
}
由于进程方法的主体期望类型A的List,因此向列表添加类型A的对象似乎是完全有效的事情。
然而,引用此列表的其他代码段可能认为这是C的列表,因为它最初被定义为。
现在,如果他们尝试访问仅存在于C中的属性(例如上例中的Name)对象,则代码将中断。
在上面的代码中,如果允许子类化,我们将根据程序的类型系统达到无效状态。因此,Java中不允许这样做