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

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

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

324898 次浏览

Super是一个下界,延伸是一个上限。

根据http://download.oracle.com/javase/tutorial/extra/generics/morefun.html

解决方案是使用一种形式的我们还没有看到的有界通配符:带有下界的通配符。的语法?超T表示未知类型是T(或T)的超类型自己;记住,超类型关系是自反的)。它是对偶的我们所经历的有界通配符使用,我们在哪里使用?扩展T到表示未知类型T的子类型

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 NumberList<? extends Number> foo3 = new ArrayList<Double>();  // Double extends Number
  1. 阅读-给定上述可能的赋值,你保证从List foo3读取什么类型的对象:

    • 您可以读取Number,因为可以分配给foo3的任何列表都包含NumberNumber的子类。
    • 您无法读取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中是允许的。唯一的“保证”是你只能从中读取,你会得到一个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 IntegerList<? 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

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

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

  • 如果你需要一个List来使用T的值(你想将T写入列表),你需要用? super T声明它,例如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));}}

另见如何添加到列表<?扩展数字>数据结构?

使用延伸,您只能从集合中获取。您不能放入其中。此外,尽管超级允许获取和放置,但get期间的返回类型是?超级T

通用通配符针对两个主要需求:

从泛型集合中读取插入到泛型集合中有三种方法可以使用泛型通配符定义集合(变量)。它们是:

List<?>           listUknown = new ArrayList<A>();List<? extends A> listUknown = new ArrayList<A>();List<? super   A> listUknown = new ArrayList<A>();

List<?>表示输入未知类型的列表。这可能是List<A>List<B>List<String>等。

List<? extends A>表示作为class Asubclasses of A实例的对象列表(例如B和C)。List<? super A>表示列表键入A classsuperclass of A

阅读更多:http://tutorials.jenkov.com/java-generics/wildcards.html

想象一下这种等级制度

在此处输入图片描述

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 E42) new ArrayList<D1>(): can store    D1    E1 E23) new ArrayList<D2>(): can store       D2       E3 E44) new ArrayList<E1>(): can store          E15) new ArrayList<E2>(): can store             E26) new ArrayList<E3>(): can store                E37) 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 E42) new ArrayList<A2>(): can store    A2          B2       C1 C2    D1 D2 E1 E2 E3 E43) new ArrayList<A3>(): can store       A3          B3       C2 C3 D1 D2 E1 E2 E3 E44) new ArrayList<A4>(): can store          A4       B3 B4    C2 C3 D1 D2 E1 E2 E3 E45) new ArrayList<B2>(): can store                B2       C1 C2    D1 D2 E1 E2 E3 E46) new ArrayList<B3>(): can store                   B3       C2 C3 D1 D2 E1 E2 E3 E47) 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到一个列表中,该列表需要是X的列表或我的X可以在我写它们时向上转换到的事物的列表,即X的任何超类

List<? super   X>

如果我得到一个列表,我想从该列表中阅读一个X,那最好是一个X的列表,或者当我读出它们时可以向上转换为X的事物的列表,即延伸 X的任何东西

List<? extends X>

希望这有帮助。

基于Bert F的回答,我想解释一下我的理解。

假设我们有3个类

public class Fruit{}
public class Melon extends Fruit{}
public class WaterMelon extends Melon{}

这里我们有

List<? extends Fruit> fruitExtendedList = …
//Says that I can be a list of any object as long as this object extends Fruit.

好的,现在让我们尝试从水果ExtendedList中获取一些值

Fruit fruit = fruitExtendedList.get(position)
//This is valid as it can only return Fruit or its subclass.

再试一次

Melon melon = fruitExtendedList.get(position)
//This is not valid because fruitExtendedList can be a list of Fruit only, it may not be//list of Melon or WaterMelon and in java we cannot assign sub class object to//super class object reference without explicitly casting it.

情况也是如此

WaterMelon waterMelon = fruitExtendedList.get(position)

现在,让我们尝试设置一些对象在水果ExtendedList

添加水果对象

fruitExtendedList.add(new Fruit())
//This in not valid because as we know fruitExtendedList can be a list of any//object as long as this object extends Fruit. So what if it was the list of//WaterMelon or Melon you cannot add Fruit to the list of WaterMelon or Melon.

添加Melon对象

fruitExtendedList.add(new Melon())
//This would be valid if fruitExtendedList was the list of Fruit but it may//not be, as it can also be the list of WaterMelon object. So, we see an invalid//condition already.

最后让我们尝试添加WaterMelon对象

fruitExtendedList.add(new WaterMelon())
//Ok, we got it now we can finally write to fruitExtendedList as WaterMelon//can be added to the list of Fruit or Melon as any superclass reference can point//to its subclass object.

等等如果有人决定制作一种新型的柠檬,让我们说为了争论,SaltyLemon作为

public class SaltyLemon extends Lemon{}

现在水果扩展列表可以是水果,甜瓜,西瓜或盐柠檬列表。

那么我们的声明

fruitExtendedList.add(new WaterMelon())

也不是有效的。

基本上,我们可以说,我们不能写任何东西到一个水果ExtendedList。

总结List<? extends Fruit>

现在让我们看看

List<? super Melon> melonSuperList= …
//Says that I can be a list of anything as long as its object has super class of Melon.

现在让我们尝试从melonSuperList中获取一些价值

Fruit fruit = melonSuperList.get(position)
//This is not valid as melonSuperList can be a list of Object as in java all//the object extends from Object class. So, Object can be super class of Melon and//melonSuperList can be a list of Object type

同样,Melon、WaterMelon或任何其他对象都无法读取。

但请注意,我们可以读取Object类型的实例

Object myObject = melonSuperList.get(position)
//This is valid because Object cannot have any super class and above statement//can return only Fruit, Melon, WaterMelon or Object they all can be referenced by//Object type reference.

现在,让我们尝试从melonSuperList设置一些值。

添加对象类型对象

melonSuperList.add(new Object())
//This is not valid as melonSuperList can be a list of Fruit or Melon.//Note that Melon itself can be considered as super class of Melon.

添加水果类型对象

melonSuperList.add(new Fruit())
//This is also not valid as melonSuperList can be list of Melon

添加Melon类型对象

melonSuperList.add(new Melon())
//This is valid because melonSuperList can be list of Object, Fruit or Melon and in//this entire list we can add Melon type object.

添加WaterMelon类型对象

melonSuperList.add(new WaterMelon())
//This is also valid because of same reason as adding Melon

总而言之,我们可以在melonSuperList中添加Melon或其子类并只读Object类型对象。

这里最令人困惑的是,无论我们指定什么类型限制,赋值只以一种方式工作:

baseClassInstance = derivedClassInstance;

您可能会认为Integer extends NumberInteger会像<? extends Number>一样,但编译器会告诉您<? extends Number> cannot be converted to Integer(即,用人类的说法,任何扩展数字的东西都可以转换为整数是错误的):

class Holder<T> {T v;T get() { return v; }void set(T n) { v=n; }}class A {public static void main(String[]args) {Holder<? extends Number> he = new Holder();Holder<? super Number> hs = new Holder();
Integer i;Number n;Object o;
// Producer Super: always gives an error except//       when consumer expects just Objecti = hs.get(); // <? super Number> cannot be converted to Integern = hs.get(); // <? super Number> cannot be converted to Number// <? super Number> cannot be converted to ... (but//       there is no class between Number and Object)o = hs.get();
// Consumer Superhs.set(i);hs.set(n);hs.set(o); // Object cannot be converted to <? super Number>
// Producer Extendsi = he.get(); // <? extends Number> cannot be converted to Integern = he.get();o = he.get();
// Consumer Extends: always gives an errorhe.set(i); // Integer cannot be converted to <? extends Number>he.set(n); // Number cannot be converted to <? extends Number>he.set(o); // Object cannot be converted to <? extends Number>}}

hs.set(i);是可以的,因为#1可以转换为#2的任何超类(而不是因为IntegerNumber的超类,这不是真的)。

EDIT添加了一条关于消费者扩展和生产者超级的评论——它们没有意义,因为它们相应地指定了什么都没有Object。建议您记住PECS,因为CEPS从来没有用过。

何时使用扩展和超级

通配符在方法参数中最有用。它们允许方法接口必要的灵活性。

人们经常混淆何时使用扩展和何时使用超边界。经验法则是get-put原则。如果您从参数化容器中获取某些内容,请使用扩展。

int totalFuel(List<? extends Vehicle> list) {int total = 0;for(Vehicle v : list) {total += v.getFuel();}return total;}

总燃料方法从列表中获取车辆,询问它们有多少燃料,并计算总数。如果将对象放入参数化容器中,请使用超级。

int totalValue(Valuer<? super Vehicle> valuer) {int total = 0;for(Vehicle v : vehicles) {total += valuer.evaluate(v);}return total;}

极值方法将车辆放入Valuer中。知道扩展边界比超级边界更常见是很有用的。

您可以通过上面的所有答案来了解为什么.add()仅限于'<?>''<? extends>',部分限于'<? super>'

但是如果你想记住它,不想每次都去探索答案,这就是这一切的结论:

List<? extends A>表示这将接受A中的任何ListA的子类。但是你不能在这个列表中添加任何东西。甚至不是类型A的对象。

List<? super A>表示这将接受A的任何列表和A的超类。您可以添加类型A及其子类的对象。

将项目添加到列表中:

  • 列表<?扩展X>不允许添加任何内容,除了null到列表中。

  • List<?超X>允许添加任何X(X或其子类型)或null。

从列表中获取一个项目:

  • 当您从列表<?扩展X>获取项目时,您可以将其分配给X类型的变量或X的任何超类型,包括Object。
  • 当您从列表<?超级X>获取项目时,您只能将其分配给类型Object的变量。

一些例子:

    List<? extends Number> list1 = new ArrayList<Integer>();list1.add(null);  //OKNumber n = list1.get(0);  //OKSerializable s = list1.get(0);  //OKObject o = list1.get(0);  //OK
list1.add(2.3);  //ERRORlist1.add(5);  //ERRORlist1.add(new Object());  //ERRORInteger i = list1.get(0);  //ERROR

    List<? super Number> list2 = new ArrayList<Number>();list2.add(null);  //OKlist2.add(2.3);  //OKlist2.add(5);  //OKObject o = list2.get(0);  //OK
list2.add(new Object());  //ERRORNumber n = list2.get(0);  //ERRORSerializable s = list2.get(0);  //ERRORInteger i = list2.get(0);  //ERROR

投票通过的答案涵盖了许多方面的细节。然而,我会尝试以不同的方式回答这一点。

有两件事我们需要考虑,

1.对列表变量的赋值

List<? extends X> listvar;

这里,任何可以分配X的列表或X的子类列表到listvar。

List<?扩展编号>listvar;listvar=New ArrayList();//数组列表的个数listvar=new ArrayList();


List<? super X> listvar;

这里,任何可以分配X的列表或X的超类列表到listvar。

List<?超级号码>listvar;listvar=New ArrayList();//数组列表的个数listvar=new ArrayList();

2.对列表变量执行读取或写入操作

`List<? extends X> listvar;`

您可以使用此功能接受方法参数中的列表并对x型执行任何操作(注意:您可以从列表中的只读取X类型的对象)。

`List<? super Number> listvar;

您可以使用此功能接受方法参数中的列表并对对象类型执行任何操作,就像您可以从列表中的只读取Object类型的对象一样。但是,是的,这里的附加内容是,您可以将X类型的对象添加到列表中。

例如,继承顺序假设为O>S>T>U>V

使用扩展关键字,

正确:

List<? extends T> Object = new List<T>();List<? extends T> Object = new List<U>();List<? extends T> Object = new List<V>();

错误:

List<? extends T> Object = new List<S>();List<? extends T> Object = new List<O>();

超级关键词:

正确:

List<? super T> Object = new List<T>();List<? super T> Object = new List<S>();List<? super T> Object = new List<O>();

错误:

List<? super T> Object = new List<U>();List<? super T> Object = new List<V>();

添加对象:List Object=new List();

Object.add(new T()); //error

为什么会出错?让我们看看列表对象初始化的可能性

List<? extends T> Object = new List<T>();List<? extends T> Object = new List<U>();List<? extends T> Object = new List<V>();

如果我们使用Object.add(new T());那么只有当

List<? extends T> Object = new List<T>();

但是还有额外的两种可能

List Object=new List();List Object=new List();//列表对象如果我们尝试添加(new T())到上述两种可能性,它会给出错误,因为T是U和V的上级类。我们尝试添加一个T对象[是(new T())]到U和V类型的列表中。高级类对象(Base class)不能传递给低级对象(Sub class)。

由于额外的两种可能性,即使您使用了正确的所有权,Java也会给您错误,因为Java不知道您指的是什么对象。所以您不能将对象添加到List Object=new List();因为存在无效的可能性。

添加对象:List Object=new List();

Object.add(new T()); // compiles fine without errorObject.add(new U()); // compiles fine without errorObject.add(new V()); // compiles fine without error
Object.add(new S()); //  errorObject.add(new O()); //  error

但为什么上述两个错误?我们可以使用Object.add(new T());仅在以下情况下,

List<? super T> Object = new List<T>();List<? super T> Object = new List<S>();List<? super T> Object = new List<O>();

如果我们尝试使用Object.add(new T())List Object=new List();//列表对象和List Object=new List();//列表对象那就会出错这是因为我们不能将T对象[new T()]添加到List Object=new List();因为它是U类型的对象。我们不能将T对象[new T()]添加到U Object,因为T是基类而U是子类。我们不能将基类添加到子类,这就是发生错误的原因。这与另一种情况相同。

我想想象一下差异。假设我们有:

class A { }class B extends A { }class C extends B { }

List<? extends T>-读取和分配:

|-------------------------|-------------------|---------------------------------||         wildcard        |        get        |              assign             ||-------------------------|-------------------|---------------------------------||    List<? extends C>    |    A    B    C    |                       List<C>   ||-------------------------|-------------------|---------------------------------||    List<? extends B>    |    A    B         |             List<B>   List<C>   ||-------------------------|-------------------|---------------------------------||    List<? extends A>    |    A              |   List<A>   List<B>   List<C>   ||-------------------------|-------------------|---------------------------------|

List<? super T>-编写和分配:

|-------------------------|-------------------|-------------------------------------------||         wildcard        |        add        |                   assign                  ||-------------------------|-------------------|-------------------------------------------||     List<? super C>     |              C    |  List<Object>  List<A>  List<B>  List<C>  ||-------------------------|-------------------|-------------------------------------------||     List<? super B>     |         B    C    |  List<Object>  List<A>  List<B>           ||-------------------------|-------------------|-------------------------------------------||     List<? super A>     |    A    B    C    |  List<Object>  List<A>                    ||-------------------------|-------------------|-------------------------------------------|

在所有情况下:

  • 无论通配符如何,您都可以从列表中收到Object
  • 无论通配符如何,您总是可以添加null到可变列表。