java如何往List<? extends number>中加入元素?体会范型集合父子关系以及范型通配符的使用

以下来自一个stackoverflow的一个问答,写的很清楚。

基本上就是子类集合的引用付给父类引用,如果父类的引用变量声明的是<? extends Parent>, 则父类引用变量只能对集合进行读操作,读出来的变量是Parent类型,这是因为不确定该父类引用变量指向的是什么类型的集合,可以是Child1,也可以Child2,如果生命一个元素Parent p1,然后要加入集合,就会出错。

而如果父类变量声明的形式是<? super Child>,则通过该引用变量只能近些写操作,不能进行读操作。原理也是类似的。如果生命一个变量, Child c1, 将c1加入该集合,是没有问题的,因为集合的元素类型是Child的父类型。而如果读取,则读出来的元素就不能按照Child类型来接,所以读不可以。

在kotlin中,没有采用java这种wildcard形式来实现泛型的父子关系,而是使用了in, out的形式来表明泛型元素使用在类中的什么位置。

I have a List which is declared like this :

 List<? extends Number> foo3 = new ArrayList<Integer>();

I tried to add 3 to foo3. However I get an error message like this:

The method add(capture#1-of ? extends Number) in the type List<capture#1-of ?
extends Number> is not applicable for the arguments (ExtendsNumber)

----------------

Sorry, but you can't.

The wildcard declaration of List<? extends Number> foo3 means that the variable foo3 can hold any value from a family of types (rather than any value of a specific type). It means that any of these are legal assignments:

List<? extends Number> foo3 = new ArrayList<Number>();  // Number "extends" Number
List<? extends Number> foo3 = new ArrayList<Integer>(); // Integer extends Number
List<? extends Number> foo3 = new ArrayList<Double>();  // Double extends Number

So, given this, what type of object could you add to List foo3 that would be legal after any of the above possible ArrayList assignments:

  • You can't add an Integer because foo3 could be pointing at a List<Double>.
  • You can't add a Double because foo3 could be pointing at a List<Integer>.
  • You can't add a Number because foo3 could be pointing at a List<Integer>.

You can't add any object to List<? extends T> because you can't guarantee what kind of List it is really pointing to, so you can't guarantee that the object is allowed in that List. The only "guarantee" is that you can only read from it and you'll get a T or subclass of T.

The reverse logic applies to super, e.g. List<? super T>. These are legal:

List<? super Number> foo3 = new ArrayList<Number>(); // Number is a "super" of Number
List<? super Number> foo3 = new ArrayList<Object>(); // Object is a "super" of Number

You can't read the specific type T (e.g. Number) from List<? super T> because you can't guarantee what kind of List it is really pointing to. The only "guarantee" you have is you are able to add a value of type T (or any subclass of T) without violating the integrity of the list being pointed to.


The perfect example of this is the signature for Collections.copy():

public static <T> void copy(List<? super T> dest, List<? extends T> src)

Notice how the src list declaration uses extends to allow me to pass any List from a family of related List types and still guarantee it will produce values of type T or subclasses of T. But you cannot add to the src list.

The dest list declaration uses super to allow me to pass any List from a family of related List types and still guarantee I can write a value of a specific type T to that list. But it cannot be guaranteed to read the values of specific type T if I read from the list.

So now, thanks to generics wildcards, I can do any of these calls with that single method:

// copy(dest, src)
Collections.copy(new ArrayList<Number>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Number>(), new ArrayList<Integer>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Number>());
Collections.copy(new ArrayList<Object>(), new ArrayList<Double>());

Consider this confusing and very wide code to exercise your brain. The commented out lines are illegal and the reason why is stated to the extreme right of the line (need to scroll to see some of them):

  List<Number> listNumber_ListNumber  = new ArrayList<Number>();
//List<Number> listNumber_ListInteger = new ArrayList<Integer>();                    // error - can assign only exactly <Number>
//List<Number> listNumber_ListDouble  = new ArrayList<Double>();                     // error - can assign only exactly <Number>
  
  List<? extends Number> listExtendsNumber_ListNumber  = new ArrayList<Number>();
  List<? extends Number> listExtendsNumber_ListInteger = new ArrayList<Integer>();
  List<? extends Number> listExtendsNumber_ListDouble  = new ArrayList<Double>();
  
  List<? super Number> listSuperNumber_ListNumber  = new ArrayList<Number>();
//List<? super Number> listSuperNumber_ListInteger = new ArrayList<Integer>();      // error - Integer is not superclass of Number
//List<? super Number> listSuperNumber_ListDouble  = new ArrayList<Double>();       // error - Double is not superclass of Number
  

//List<Integer> listInteger_ListNumber  = new ArrayList<Number>();                  // error - can assign only exactly <Integer>
  List<Integer> listInteger_ListInteger = new ArrayList<Integer>();
//List<Integer> listInteger_ListDouble  = new ArrayList<Double>();                  // error - can assign only exactly <Integer>
  
//List<? extends Integer> listExtendsInteger_ListNumber  = new ArrayList<Number>(); // error - Number is not a subclass of Integer
  List<? extends Integer> listExtendsInteger_ListInteger = new ArrayList<Integer>();
//List<? extends Integer> listExtendsInteger_ListDouble  = new ArrayList<Double>(); // error - Double is not a subclass of Integer
  
  List<? super Integer> listSuperInteger_ListNumber  = new ArrayList<Number>();
  List<? super Integer> listSuperInteger_ListInteger = new ArrayList<Integer>();
//List<? super Integer> listSuperInteger_ListDouble  = new ArrayList<Double>();     // error - Double is not a superclass of Integer


  listNumber_ListNumber.add(3);             // ok - allowed to add Integer to exactly List<Number>
  
  // These next 3 are compile errors for the same reason:
  // You don't know what kind of List<T> is really
  // being referenced - it may not be able to hold an Integer.
  // You can't add anything (not Object, Number, Integer,
  // nor Double) to List<? extends Number>      
//listExtendsNumber_ListNumber.add(3);     // error - can't add Integer to *possible* List<Double>, even though it is really List<Number>
//listExtendsNumber_ListInteger.add(3);    // error - can't add Integer to *possible* List<Double>, even though it is really List<Integer>
//listExtendsNumber_ListDouble.add(3);     // error - can't add Integer to *possible* List<Double>, especially since it is really List<Double>

  listSuperNumber_ListNumber.add(3);       // ok - allowed to add Integer to List<Number> or List<Object>
  
  listInteger_ListInteger.add(3);          // ok - allowed to add Integer to exactly List<Integer> (duh)

  // This fails for same reason above - you can't
  // guarantee what kind of List the var is really
  // pointing to
//listExtendsInteger_ListInteger.add(3);   // error - can't add Integer to *possible* List<X> that is only allowed to hold X's
  
  listSuperInteger_ListNumber.add(3);      // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
  listSuperInteger_ListInteger.add(3);     // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: <? extends Father> 这种写法的意思是,List 可以存放 Father 类及其子类的对象,而 Father 类型的对象则不能存放进去。 这个写法可以让 List 变得更加灵活,可以存放任意 Father 类型及其子类的对象,比如 Father 的子类 Son、GrandSon 等等。 ### 回答2: 在Java泛型List<? extends Father>这样写的用途是为了使List能够接受Faher类及其子类的对象。?表示一个不确定的类型,而extends关键字限定了该类型必须是Father类或其子类。 使用List<? extends Father>的好处是能够提供更大的灵活性和可复用性。例如,假设有一个List<? extends Number>的列表,我们可以将其赋值为一个List<Integer>或List<Double>的对象,这样做可以使该列表接受更多不同类型的Number及其子类的对象。 另一个好处是能够实现协变。在Java,数组是不可协变的,即不能将Integer数组赋值给Number数组。但是,使用List<? extends Father>创建的列表可以实现协变,即可以将List<Father>赋值给List<? extends Father>。这样做的好处是可以在方法参数接受更多不同类型的Father及其子类的对象。 然而,使用List<? extends Father>也有一些限制。由于不确定具体的类型是Father的哪个子类,只能读取元素,而不能修改或添加元素。因为无法确定具体的类型是什么,所以在编译时期无法确定是否为合法的操作。这是为了保证类型安全和类型一致性。 综上所述,使用List<? extends Father>的好处在于提供了更大的灵活性和可复用性,实现了协变,同时也限制了对元素的修改和添加操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值