协慌网

登录 贡献 社区

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

List<? super T>List<? extends T>

我曾经用过List<? extends T> ,但是它不允许我在列表中添加元素list.add(e) ,而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
  1. 阅读 - 根据上述可能的分配,可以保证从List foo3读取哪种类型的对象:

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

    • 您不能添加Integer因为foo3可能指向List<Double>
    • 您不能添加Double因为foo3可能指向List<Integer>
    • 您不能添加Number因为foo3可能指向List<Integer>

您不能将任何对象添加到List<? extends T>因为您不能保证它真正指向的是哪种List ,因此不能保证该List允许该对象。唯一的 “保证” 是,你只能从它读,你会得到一个T或子类T

super

现在考虑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
  1. 阅读 - 根据上述可能的分配,从List foo3读取时,可以保证接收什么类型的对象:

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

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

聚醚砜

记住PECS“生产者扩展,消费者超级”

  • “生产者扩展” - 如果需要一个List来产生T值(要从列表中读取T ),则需要使用? extends T进行声明? extends T ,例如List<? extends Integer> 。但是您不能添加到此列表。

  • “Consumer Super” - 如果您需要一个使用T值的List (您想将T写入列表中),则需要使用? super T进行声明? super T ,例如List<? super Integer> 。但是不能保证您可以从该列表中读取什么类型的对象。

  • 如果既需要读取列表又要写入列表,则需要完全不使用通配符来声明它,例如List<Integer>

注意Java Generics FAQ 中的这个例子 。请注意,源列表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)); 
  } 
}

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

想象一下拥有这种等级制度

在此处输入图片说明

1. 延伸

通过写

List<? extends C2> list;

您说list将能够引用(例如) ArrayList类型的对象,该对象的通用类型是C2的 7 个子类型之一(包括C2 ):

  1. C2: new ArrayList<C2>(); ,(可以存储 C2 或子类型的对象)或
  2. D1: new ArrayList<D1>(); ,(可以存储 D1 或子类型的对象)或
  3. 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的对象,该对象的泛型类型是C2的 7 个超类型之一(包括C2 ):

  • 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(){});因为无论我们引用的是哪种列表,都允许使用C2
  • 您可以list.add(new D1(){});因为无论我们引用的是哪种列表,都允许使用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 写入到一个列表,则该列表必须是 A X 的列表或一个事物的名单,我的 X 可以向上转型为我写他们即 X 的任何 ...

List<? super   X>

如果我得到一个列表,并且想从该列表中读取一个 X,则最好是 X 列表或在我读出它们时可以向上投射到 X 的事物列表,即扩展 X 的任何东西

List<? extends X>

希望这可以帮助。