<?之间的区别 超级T>和<? 在Java中扩展T>

List<? super T>什么区别List<? super T> List<? super T>List<? extends T> List<? extends T>

我曾经使用List<? extends T> List<? extends T> ,但它不允许我添加元素给它list.add(e) ,而List<? super T> List<? super T>


extends

List<? extends Number> foo3的通配符声明 List<? extends Number> foo3意味着任何这些都是合法的作业:

List<? extends Number> foo3 = new ArrayList<Number>();  // Number "extends" Number (in this context)
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>();  // Double extends Number
  • 阅读 - 鉴于上述可能的任务,你保证从List foo3读取什么类型的对象:

  • 你可以阅读一个Number ,因为任何可能被分配到列表中的foo3含有Number或子类Number
  • 您无法读取Integer因为foo3可能指向List<Double>
  • 您无法读取Double因为foo3可能指向List<Integer>
  • 写作 - 给定上述可能的赋值,可以将List foo3类型的对象添加到List foo3 ,这对于所有上述可能的ArrayList赋值都是合法的:

  • 您不能添加Integer因为foo3可能指向List<Double>
  • 您不能添加Double因为foo3可能指向List<Integer>
  • 你不能添加一个Number因为foo3可能指向一个List<Integer>
  • 你不能添加任何对象到List<? extends T> List<? extends T>因为你不能保证它真正指向什么样的List ,所以你不能保证该List允许该对象。 唯一的“保证”是,你只能从它读,你会得到一个T的或子类T

    super

    现在考虑List <? super T> List <? super T>

    List<? super Integer> foo3的通配符声明 List<? super Integer> foo3意味着这些都是合法的任务:

    List<? super Integer> foo3 = new ArrayList<Integer>();  // Integer is a "superclass" of Integer (in this context)
    List<? super Integer> foo3 = new ArrayList<Number>();   // Number is a superclass of Integer
    List<? super Integer> foo3 = new ArrayList<Object>();   // Object is a superclass of Integer
    
  • 阅读 - 考虑到上述可能的任务,当您从List foo3读取时,您保证会收到什么类型的对象:

  • 您无法保证Integer因为foo3可能指向List<Number>List<Object>
  • 你不能保证一个Number因为foo3可能指向一个List<Object>
  • 唯一的保证是,你会得到一个实例Object或子类Object (但你不知道什么是子类)。
  • 写作 - 给定上述可能的赋值,可以将List foo3类型的对象添加到List foo3 ,这对于所有上述可能的ArrayList赋值都是合法的:

  • 您可以添加一个Integer因为在上面的任何列表中都允许有Integer
  • 您可以添加一个Integer子类的实例,因为在上述任何一个列表中都允许有一个Integer子类的实例。
  • 您不能添加Double因为foo3可能指向ArrayList<Integer>
  • 你不能添加一个Number因为foo3可能指向一个ArrayList<Integer>
  • 你不能添加一个Object因为foo3可能指向一个ArrayList<Integer>
  • 佩奇

    记住PECS: “生产者延伸,消费者超级”

  • “Producer Extends” - 如果你需要一个List来产生T值(你想从列表中读取T s),你需要用? extends T ? extends T ,例如List<? extends Integer> List<? extends Integer> 。 但是你不能添加到这个列表中。

  • “消费者超级” - 如果您需要一个List来消费T值(您想将T s写入列表中),您需要声明它? super T ? super T ,例如List<? super Integer> List<? super Integer> 。 但是不能保证你可以从这个列表中读取什么类型的对象。

  • 如果你需要读取和写入列表,你需要声明它完全没有通配符,例如List<Integer>

  • 请注意Java泛型常见问题中的这个例子。 注意源列表src (生产列表)使用的extends ,目标列表dest (消费列表)使用super

    public class Collections { 
      public static <T> void copy(List<? super T> dest, List<? extends T> src) {
          for (int i = 0; i < src.size(); i++) 
            dest.set(i, src.get(i)); 
      } 
    }
    

    另请参阅如何添加到List <? 扩展Number>数据结构?


    想象一下这个层次结构

    在这里输入图像描述

    1.延长

    通过写作

        List<? extends C2> list;
    

    您是说, list将能够引用类型(例如)的目的ArrayList其一般类型是的7种亚型之一C2C2附带):

  • C2: new ArrayList<C2>(); ,(可以存储C2或子类型的对象)或
  • D1: new ArrayList<D1>(); ,(可以存储D1或子类型的对象)或
  • D2: new ArrayList<D2>(); ,(可以存储D2或子类型的对象)或...
  • 等等。 七种不同的情况:

        1) new ArrayList<C2>(): can store C2 D1 D2 E1 E2 E3 E4
        2) new ArrayList<D1>(): can store    D1    E1 E2  
        3) new ArrayList<D2>(): can store       D2       E3 E4
        4) new ArrayList<E1>(): can store          E1             
        5) new ArrayList<E2>(): can store             E2             
        6) new ArrayList<E3>(): can store                E3             
        7) new ArrayList<E4>(): can store                   E4             
    

    对于每种可能的情况,我们都有一组“可存储”类型:这里用图形表示7(红色)

    在这里输入图像描述

    正如你所看到的,没有一种安全类型对于每一种情况都是常见的:

  • 你不能list.add(new C2(){}); 因为它可能是list = new ArrayList<D1>();
  • 你不能list.add(new D1(){}); 因为它可能是list = new ArrayList<D2>();
  • 等等。

    2.超级

    通过写作

        List<? super C2> list;
    

    您是说, list将能够引用类型(例如)的目的ArrayList其一般类型是的7个超类型之一C2C2附带):

  • A1: new ArrayList<A1>(); ,(可存储A1或子类型的对象)或
  • A2: new ArrayList<A2>(); ,(可以存储A2或子类型的对象)或
  • A3: new ArrayList<A3>(); ,(可以存储A3或子类型的对象)或...
  • 等等。 七种不同的情况:

        1) new ArrayList<A1>(): can store A1          B1 B2       C1 C2    D1 D2 E1 E2 E3 E4
        2) new ArrayList<A2>(): can store    A2          B2       C1 C2    D1 D2 E1 E2 E3 E4
        3) new ArrayList<A3>(): can store       A3          B3       C2 C3 D1 D2 E1 E2 E3 E4
        4) new ArrayList<A4>(): can store          A4       B3 B4    C2 C3 D1 D2 E1 E2 E3 E4
        5) new ArrayList<B2>(): can store                B2       C1 C2    D1 D2 E1 E2 E3 E4
        6) new ArrayList<B3>(): can store                   B3       C2 C3 D1 D2 E1 E2 E3 E4
        7) new ArrayList<C2>(): can store                            C2    D1 D2 E1 E2 E3 E4
    

    对于每种可能的情况,我们都有一组“可存储”类型:这里用图形表示7(红色)

    在这里输入图像描述

    正如你所看到的,这里我们有七种安全类型,它们在每种情况下都是通用的: C2D1D2E1E2E3E4

  • 你可以list.add(new C2(){}); 因为无论我们引用的List是什么类型, C2都是允许的
  • 你可以list.add(new D1(){}); 因为无论我们引用的List是什么类型, D1都是允许的
  • 等等。 您可能注意到这些类型与从C2类型开始的层次结构相对应。

    笔记

    这里是完整的层次结构,如果你想做一些测试

    interface A1{}
    interface A2{}
    interface A3{}
    interface A4{}
    
    interface B1 extends A1{}
    interface B2 extends A1,A2{}
    interface B3 extends A3,A4{}
    interface B4 extends A4{}
    
    interface C1 extends B2{}
    interface C2 extends B2,B3{}
    interface C3 extends B3{}
    
    interface D1 extends C1,C2{}
    interface D2 extends C2{}
    
    interface E1 extends D1{}
    interface E2 extends D1{}
    interface E3 extends D2{}
    interface E4 extends D2{}
    

    我喜欢@Bert F的回答,但这是我的大脑看到它的方式。

    我手里有一个X. 如果我想我的X写入一个List,那么这个List需要是X的列表或X的列表,我可以在X中写入它们的任何超类 ...

    List<? super   X>
    

    如果我得到一个列表,并且我想从列表中读取一个X,那么最好是X列表或X列表,当我将它们读出时,它们可以上传到X,即任何扩展 X

    List<? extends X>
    

    希望这可以帮助。

    链接地址: http://www.djcxy.com/p/91975.html

    上一篇: Difference between <? super T> and <? extends T> in Java

    下一篇: Hashmap concurrency issue