<之间的区别?超级T>和<?扩展 Java 中的 T>1. 扩展2. 超级笔记

2022-08-31 03:59:16

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

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


答案 1

extends

通配符声明 意味着以下任何一项都是合法分配: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

    • 您可以读取数字,因为可以指定的任何列表都包含 的一个或一个子类。foo3NumberNumber
    • 您无法读取 ,因为可能指向 .Integerfoo3List<Double>
    • 您无法读取 ,因为可能指向 .Doublefoo3List<Integer>
  2. 写作 - 鉴于上述可能的赋值,您可以添加哪种类型的对象对于上述所有可能的赋值都是合法的:List foo3ArrayList

    • 您无法添加,因为可能指向 .Integerfoo3List<Double>
    • 您无法添加,因为可能指向 .Doublefoo3List<Integer>
    • 您无法添加,因为可能指向 .Numberfoo3List<Integer>

您无法向 List 添加任何对象<? 扩展 T> 因为您无法保证它真正指向哪种类型的 List,因此您无法保证该对象在该列表中被允许。唯一的“保证”是你只能从中读取,你会得到一个TT的子类。

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
  1. 阅读 - 给定上述可能的赋值,当您从 中读取时,可以保证接收哪种类型的对象:List foo3

    • 不能保证因为可能指向 或 。Integerfoo3List<Number>List<Object>
    • 不能保证因为可能指向 .Numberfoo3List<Object>
    • 一的保证是您将获得对象或子类的实例(但您不知道哪个子类)。Object
  2. 写作 - 鉴于上述可能的赋值,您可以添加哪种类型的对象对于上述所有可能的赋值都是合法的:List foo3ArrayList

    • 您可以添加 一个,因为在上述任何列表中都允许 。IntegerInteger
    • 您可以添加 子类 的实例,因为上述任何列表中都允许的子类的实例。IntegerInteger
    • 您无法添加,因为可能指向 .Doublefoo3ArrayList<Integer>
    • 您无法添加,因为可能指向 .Numberfoo3ArrayList<Integer>
    • 您无法添加,因为可能指向 .Objectfoo3ArrayList<Integer>

胸 肌

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

  • “Producer Extends” - 如果您需要 a 来生成值(您想从列表中读取 s),则需要使用 声明它,例如 .但您无法添加到此列表中。ListTT? extends TList<? extends Integer>

  • “Consumer Super” - 如果你需要一个来消费的值(你想把s写进列表),你需要用 来声明它,例如。但是不能保证您可以从此列表中读取哪种类型的对象。ListTT? super TList<? super Integer>

  • 如果您需要同时读取和写入列表,则需要在没有通配符的情况下完全声明它,例如.List<Integer>

请注意 Java 泛型常见问题解答中的此示例。请注意源列表(生产列表)如何使用 ,而目标列表(使用列表)如何使用:srcextendsdestsuper

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> 数据结构?


答案 2

想象一下,有这个层次结构

enter image description here

1. 扩展

通过写作

    List<? extends C2> list;

您是说将能够引用类型(例如)的对象,其泛型类型是( include)的7个子类型之一:listArrayListC2C2

  1. C2:、(可以存储 C2 或子类型的对象)或new ArrayList<C2>();
  2. D1:、(可以存储 D1 或子类型的对象)或new ArrayList<D1>();
  3. D2:,(可以存储D2或子类型的对象)或...new ArrayList<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;

您是说将能够引用类型(例如)的对象,其泛型类型是( include)的7种超类型之一:listArrayListC2C2

  • A1:、(可以存储 A1 或子类型的对象)或new ArrayList<A1>();
  • A2:、(可以存储 A2 或子类型的对象)或new ArrayList<A2>();
  • A3:,(可以存储A3或子类型的对象)或...new ArrayList<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

如您所见,这里我们有七种安全类型,它们在每种情况下都是通用的:,,,,,,。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{}