<?super T>和<?extends T>

问题

118882328和List<? extends T>和有什么区别?

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


#1 热门回答(1048 赞)

###延伸

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读取什么类型的对象:你可以读取一个数字,因为可以分配给foo3的任何列表都包含Number或Number的子类。你无法读取整数,因为foo3可能指向List <Double>。你无法读取Double,因为foo3可能指向List <Integer>。

  • 写入 - 鉴于上述可能的分配,你可以向List foo3添加哪种类型的对象,这对于上述所有可能的ArrayList赋值都是合法的:你不能添加整数,因为foo3可能指向List <Double>。你不能添加Double,因为foo3可能指向List <Integer>。你无法添加数字,因为foo3可能指向List <Integer>。
    你不能添加任何对象到List<? extends T>因为你不能保证什么样的Listit真正指向,所以你不能保证该对象在theList中被允许。唯一的"保证"是你只能读取它,你将获得aTT的子类。

super

现在考虑List <? super T>

通配符声明为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读取时,你保证会接收哪种类型的对象:由于foo3可能指向List <Number>或List <Object>,因此无法保证整数。你无法保证数字,因为foo3可能指向List <Object>。唯一的保证是你将获得一个Object的实例或Object的子类(但你不知道是什么子类)。

  • 写入 - 鉴于上述可能的赋值,你可以向List foo3添加哪种类型的对象,这对于上述所有可能的ArrayList赋值都是合法的:你可以添加一个Integer,因为在上述任何列表中都允许使用Integer。你可以添加Integer子类的实例,因为在上述任何列表中都允许使用Integer子类的实例。你不能添加一个Double,因为foo3可能指向一个ArrayList <Integer>。你无法添加数字,因为foo3可能指向ArrayList <Integer>。你无法添加Object,因为foo3可能指向ArrayList <Integer>。

PECS

RememberPECS:"Producer Extends,Consumer Super"

  • "Producer Extends" - 如果你需要一个List来产生T值(你想从列表中读取Ts),你需要声明它吗?扩展T,例如名单<?扩展整数>。但你无法添加到此列表中。
  • "Consumer Super" - 如果你需要一个List消耗T值(你想把Ts写入列表),你需要声明它吗?超级T,例如名单<?超级整数>。但是无法保证你可以从此列表中读取哪种类型的对象。
  • 如果你需要同时读取和写入列表,则需要在没有通配符的情况下完全声明它,例如:列表<Integer>的。

###例子

Notethis example from the Java Generics FAQ。请注意源listsrc(生成列表)如何使用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)); 
  } 
}

另见How can I add to List<? extends Number> data structures?


#2 热门回答(165 赞)

想象一下这个层次结构

enter image description here

#1。扩展

通过写作

List<? extends C2> list;

你是说list将能够引用类型的对象(例如)ArrayList,其泛型类型是7子类型之一ofC2(C2included):

  • 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(红色)集

enter image description here

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

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

等等。

#2。超级

通过写作

List<? super C2> list;

你是说41414919将能够引用类型的对象(例如)ArrayList,其泛型类型是7超类型之一ofC2(C2included):

  • 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(红色)集

enter image description here

正如你所看到的,这里我们有sevensafe类型,每个案例都是常见的:C2,D1,D2,E1,E2,E3,E4

  • 你可以list.add(new C2(){});因为,无论我们引用哪种List,都允许使用C2
  • 你可以list.add(new D1(){});因为,无论我们引用哪种List,都允许使用D1

等等。你可能注意到这些类型对应于从typeC2开始的层次结构。

#Notes

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

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{}

#3 热门回答(50 赞)

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

我手里拿着一个X.如果我想将83985671my写入一个List,那么该List需要是一个X列表或一个我的X可以向上编写的事物列表,因为我将它写入,即any超类of X ...

List<? super   X>

如果我得到一个List并且我想要从该列表中读取an X,那么最好是X的列表或者可以在我读出它时向上转换为X的列表,即扩展的任何内容X

List<? extends X>

希望这可以帮助。