为什么<!-?扩展Parent-> Java的通用集合中的默认行为不是吗?

时间:2018-07-12 09:35:41

标签: java generics

考虑:

public class Parent {

}


public class Child extends Parent {

}


    ArrayList<Parent> ps= new ArrayList<Child>(); \\wont compile
    ArrayList<? extends Parent> ps2= new ArrayList<Child>(); \\works

为什么在使用<? extends Parent>时默认不假设<Parent>?我的意思是我无法想到一个用例,假设每个子项都是父项,您会想到任何意外行为吗?

编辑:

一个更有用的示例:

 public static final void main(String[] args) {
     ArrayList<Child> children=new ArrayList<Child>();
     children.add(new Child());
     children.add(new Child());
     computeSomething1(children); \\doesnt compile
     computeSomething2(children); \\compiles
 }

 public static int computeSomething1(ArrayList<Parent> ps) {
     return 1;
 }
 public static int computeSomething2(ArrayList<? extends Parent> ps) {
     return 1;
 }

1 个答案:

答案 0 :(得分:2)

如果java这样做了,您很容易得到污染列表。假设Java遵循您的建议,并允许您将List<Child>分配给类型List<Parent>的变量。然后,这将是可能的:

static class Parent {}
static class Child extends Parent {}
static class IllegitimateChild extend Parent {}

public static void main(String args[]) {
    List<Child> children = new ArrayList<>();
    computeSomething(children);
    Child c = children.get(0); //WTF - ClassCastException?? IllegitimateChild is not a Child
}

public static void computeSomething(List<Parent> items) {
    parents.add(new IllegitimateChild());
}

要解决这个问题,如果需要的话,java使您显式声明一个有界通配符。这样就可以在编译时捕获此类错误。

public static void main(String[] args) {
    List<? extends Parent> items = new ArrayList<Child>();

    items.add(new IllegitimateChild()); // Compiler error
    items.add(new Child()); // Compiler error
}

以上两种编译器错误都是Java所说的:“此列表中的元素类型对我来说是未知的(?),因此我不允许您将其放在此处,因为它可能违反了知道此类型的其他引用此列表的合同。” ? extends Parent而不只是?的事实实际上确实有助于编译器推断有关方法的 return type 的信息(例如,它知道它可以分配调用{{ 1}}转换为items.get(0)变量,即使它不知道具体类型也是如此。