代码之家  ›  专栏  ›  技术社区  ›  Ofek Ron

为什么<?extends parent>不是Java中泛型集合的默认行为吗?[副本]

  •  0
  • Ofek Ron  · 技术社区  · 7 年前

    考虑:

    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 回复  |  直到 7 年前
        1
  •  2
  •   user31601    7 年前

    如果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 而不仅仅是 是吗? 只会真正帮助编译器推断 返回类型 方法的(例如,它知道它可以分配调用的结果 items.get(0) 给一个 Parent 变量,即使它不知道具体的类型)。