Java类层次结构

时间:2014-02-11 17:12:26

标签: java oop

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 {

    }

为什么使用此代码进行编译问题?根据面向对象的编程,这个

应该没有任何问题

4 个答案:

答案 0 :(得分:2)

即使CA的子类,List<C> 不是 List<A>的子类,因此也是编译问题。

这是仿制药之间常见的混淆,但简单来说,问题就在上面。

尝试将功能签名修改为

public void process(List<? extends A> a)

<? extends A>事件是有界通配符。这些通配符的使用仅适用于这些类型的问题,您需要将任何类上限与您需要的A类相匹配。阅读更多相关信息here

但请记住,这会对您的List <? extends A>参数施加一些限制。例如,您无法在列表中添加新元素null除外),因为Java不知道<? extends A>的实际类型(可以是BC或你有什么,因此它完全阻止你做那种事。

答案 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中不允许这样做