代码之家  ›  专栏  ›  技术社区  ›  Sergey Mikhanov

无法向通配符泛型类型的Java集合添加值

  •  23
  • Sergey Mikhanov  · 技术社区  · 15 年前

    为什么不编译此代码( Parent 是接口吗?

    List<? extends Parent> list = ...
    Parent p = factory.get();   // returns concrete implementation
    list.set(0, p);   // fails here: set(int, ? extends Parent) cannot be applied to (int, Parent)
    
    3 回复  |  直到 6 年前
        1
  •  43
  •   Jon Skeet    15 年前

    这样做是为了安全。想象一下如果它起作用:

    List<Child> childList = new ArrayList<Child>();
    childList.add(new Child());
    
    List<? extends Parent> parentList = childList;
    parentList.set(0, new Parent());
    
    Child child = childList.get(0); // No! It's not a child! Type safety is broken...
    

    意义 List<? extends Parent> 是“这是一个扩展了 Parent . 我们不知道是哪种类型-可能是 List<Parent> A List<Child> ,或者 List<GrandChild> “这样就可以安全地取走任何物品 外面的 List<T> API和转换自 T 起源 但是打电话不安全 在里面 列表& T; API转换自 起源 T …因为转换可能无效。

        2
  •  16
  •   Michael    6 年前
    List<? super Parent>
    

    PECS-“生产者-延伸,消费者-超级”。你的 List 是的消费者 Parent 物体。

        3
  •  2
  •   irreputable    15 年前

    这是我的理解。

    假设我们有一个具有2个方法的泛型类型

    type L<T>
        T get();
        void set(T);
    

    假设我们有超大号的 P ,它有子类型 C1, C2 ... Cn . (为了方便起见,我们说 是自身的子类型,实际上是 Ci )

    现在我们也得到了 n 混凝土类型 L<C1>, L<C2> ... L<Cn> ,就好像我们已经手动编写了 n 类型:

    type L_Ci_
        Ci get();
        void set(Ci);
    

    我们不需要手动编写,这就是重点。有 这些类型之间的关系

    L<Ci> oi = ...;
    L<Cj> oj = oi; // doesn't compile. L<Ci> and L<Cj> are not compatible types. 
    

    对于C++模板来说,这就是故事的结尾。它基本上是宏扩展——基于一个“模板”类,它生成许多具体的类,它们之间没有类型关系。

    对于Java,还有更多。我们还有一种类型 L<? extends P> 它是任何 L<Ci>

    L<Ci> oi = ...;
    L<? extends P> o = oi; // ok, assign subtype to supertype
    

    什么样的方法应该存在 L&L.;?扩展P & GT; ?作为一个超级类型,它的任何方法都必须由其子类型引起轰动。这种方法可以工作:

    type L<? extends P>
        P get();
    

    因为在它的任何子类型中 L&L.C.G. 有一种方法 Ci get() ,与 P get() -重写方法具有相同的签名和协变返回类型。

    这不适用于 set() 但是-我们找不到类型 X ,这样 void set(X) 可以被重写 void set(Ci) 对于任何 CI . 因此 集() 方法不存在于 L&L.;?扩展P & GT; .

    还有一个 L<? super P> 相反。它有 set(P) 但没有 get() . 如果 Si 是超类型的 , L&L.;?超级P & GT; 是超类型的 L<Si> .

    type L<? super P>
        void set(P);
    
    type L<Si>
        Si get();
        void set(Si);
    

    set(Si) “超越” 集(p) 不是通常意义上的,但是编译器可以看到 集(p) 是上的有效调用 集(SI)