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

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

时间:2023-06-13 14:33:23浏览次数:39  
标签:范型 java ArrayList List Number add Integer new

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

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

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

 


 

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):

 1   List<Number> listNumber_ListNumber  = new ArrayList<Number>();
 2 //List<Number> listNumber_ListInteger = new ArrayList<Integer>();                    // error - can assign only exactly <Number>
 3 //List<Number> listNumber_ListDouble  = new ArrayList<Double>();                     // error - can assign only exactly <Number>
 4   
 5   List<? extends Number> listExtendsNumber_ListNumber  = new ArrayList<Number>();
 6   List<? extends Number> listExtendsNumber_ListInteger = new ArrayList<Integer>();
 7   List<? extends Number> listExtendsNumber_ListDouble  = new ArrayList<Double>();
 8   
 9   List<? super Number> listSuperNumber_ListNumber  = new ArrayList<Number>();
10 //List<? super Number> listSuperNumber_ListInteger = new ArrayList<Integer>();      // error - Integer is not superclass of Number
11 //List<? super Number> listSuperNumber_ListDouble  = new ArrayList<Double>();       // error - Double is not superclass of Number
12   
13  
14 //List<Integer> listInteger_ListNumber  = new ArrayList<Number>();                  // error - can assign only exactly <Integer>
15   List<Integer> listInteger_ListInteger = new ArrayList<Integer>();
16 //List<Integer> listInteger_ListDouble  = new ArrayList<Double>();                  // error - can assign only exactly <Integer>
17   
18 //List<? extends Integer> listExtendsInteger_ListNumber  = new ArrayList<Number>(); // error - Number is not a subclass of Integer
19   List<? extends Integer> listExtendsInteger_ListInteger = new ArrayList<Integer>();
20 //List<? extends Integer> listExtendsInteger_ListDouble  = new ArrayList<Double>(); // error - Double is not a subclass of Integer
21   
22   List<? super Integer> listSuperInteger_ListNumber  = new ArrayList<Number>();
23   List<? super Integer> listSuperInteger_ListInteger = new ArrayList<Integer>();
24 //List<? super Integer> listSuperInteger_ListDouble  = new ArrayList<Double>();     // error - Double is not a superclass of Integer
25  
26  
27   listNumber_ListNumber.add(3);             // ok - allowed to add Integer to exactly List<Number>
28   
29   // These next 3 are compile errors for the same reason:
30   // You don't know what kind of List<T> is really
31   // being referenced - it may not be able to hold an Integer.
32   // You can't add anything (not Object, Number, Integer,
33   // nor Double) to List<? extends Number>      
34 //listExtendsNumber_ListNumber.add(3);     // error - can't add Integer to *possible* List<Double>, even though it is really List<Number>
35 //listExtendsNumber_ListInteger.add(3);    // error - can't add Integer to *possible* List<Double>, even though it is really List<Integer>
36 //listExtendsNumber_ListDouble.add(3);     // error - can't add Integer to *possible* List<Double>, especially since it is really List<Double>
37   listSuperNumber_ListNumber.add(3);       // ok - allowed to add Integer to List<Number> or List<Object>
38   
39   listInteger_ListInteger.add(3);          // ok - allowed to add Integer to exactly List<Integer> (duh)
40   // This fails for same reason above - you can't
41   // guarantee what kind of List the var is really
42   // pointing to
43 //listExtendsInteger_ListInteger.add(3);   // error - can't add Integer to *possible* List<X> that is only allowed to hold X's
44   
45   listSuperInteger_ListNumber.add(3);      // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>
46   listSuperInteger_ListInteger.add(3);     // ok - allowed to add Integer to List<Integer>, List<Number>, or List<Object>

 



问题一:

List<? extends Number>  list = new ArrayList<>();
list .add(100);  //这样的代码会报错的

 

为啥这样的List就不能调用add函数呢?    

奇怪,明明100是Integer对象类型,然后Integer是Number的子类,符合<? extends Number>上线限制的原则啊,,,为啥就报错了呢?

这样的问题,网上很多回答的,但总是说类型不安全,导致不让add。。。。说的一塌糊涂,,,让我想了好久,最后我根据我的理解,整理出来,方便大家理解,有哪里说的不对的,欢迎指正。

 

答:1 . List<? extends Number> list 就是对加入的元素进行上限限制,,,只要是Number或者Number的子类都事可以的,,,但具体是哪一个? 大哥, 这谁知道啊,,,当然编译器也是不知道的,。。。。那编译器都不知道是什么类型,你就直接add一个Integer类型的,,,要是人家后面确定是Float类型的,那不就是搞错了吗? 这样的原因,导致java编译器直接就不让add了,从而避免了该问题的发生。

2. 还有种解释是,,,现在JAVA的强类型检测,当编辑器遇到<? extends Number>时候,他只知道有东西要加入了,至于具体是什么类型,就不得而知了,所以在声明该变量的时候,只是存了一个变量加入类型的标记符,也就是占位符,具体是什么类型,还不能确定。。。当list调用add函数加入的时候, 会进行类型判断的,这个时候,因为上面保存的只是占位符,却没有真正的标记变量的类型。。。导致没法判断,就这样JAVAl数据类型的强类型问题问题,就会导致报错和各种问题,所以JAVA就直接不让add了,从而避免了该问题的发生。

 

问题二:

List<? super Number> listb = new ArrayList<>();
Integer a = 10;
listb.add(a);

 

listb.add((Object)a)    //这个的add会报错的

为啥这里的listb.add(a)不会报错,而listb.add(Object)对象会报错呢?

 

答:先说明下这里色List<? super Number> list,,,这个就是泛型的下线限制符。。。里面可以存储的对象是Number类以及Number类的父类。。。至于具体是哪个类型,就不得而知了。这就更奇怪了,为啥Integer类存储成功了,而Object类存储报错了。。。这是为啥呢?

首先先说,为啥add Integer对象成功了。。。其实是这样的,因为JAVA的继承原理,对应的属性图都是从上到下的方式,Integer对象肯定是Number对象。。。而List<? super Number> list可以存储的就是Number类或者Number类的父类,但具体是哪个类型,就不得而知了。。。当然编译器也是不知道的。。。但就是因为JAVA的继承原理,让每一个Number子类都同时是Number类,,,同时也是所以Number父类的子类。。。这不就是完全符合了List<? super Number>list 可以存储的要求了吗? 因为Number子类,全部可以看成是Number类,然后Number类都是可以符合该List要求的。。。。那这样明显就可以存储成功了啊。。。

然后在说add Object失败的问题,很明显,在申明一次,List<? super Number> list存储的只能是Number类或者Number类的父类,但具体是哪个类??? 谁知道啊,,,有谁知道吗??? 鬼都不知道,编辑器当然也是不知道的

。。。这个时候,你add object对象,编辑器都不知道里面存储的数据类型,因为JAVA的强类型和Java类型检测机制,,,这里就会报错啦。。。。

问题三:

基于上面的考虑,List<? extends Number>list 都不能add,,,那有这个东西有什么用呢?   这个自己体会吧,,,简单说明一下,抽象类和接口都不能new对象,怎么就那么大作用呢? 道理是一样的,,,比如如下:

List<? super Number>  list = new Arrary<>();
List<Integer>  listb  = new Arrary<>();
list  = listb;

这就是用到的地方,对类型进行限制。。。其他的自己体会下吧,,,

    

总结:

当考虑到要add时候,对于安全性的考虑,应该使用泛型的<? super XXX>模式,当考虑到要对list进行get类型显示的时候,就要考虑用到<? extends XXX>,这样的泛型才能起到,类型安全,并且简洁,程序员开发直观的效果!!!!!!





标签:范型,java,ArrayList,List,Number,add,Integer,new
From: https://www.cnblogs.com/r1-12king/p/17477389.html

相关文章

  • Java:如何写好代码,少点bug
    前言工作差不多两年了。2021-04-14实习入职,至今2023-04-07,和大佬相比我这还是属于初级程序员,技术不强。平时写代码没啥技术含量,但真的挺多同事连基本的CRUD都写不好,不管是代码规范还是安全性问题都有很大的纰漏,所以觉得自己最大的骄傲就是代码规范,bug少。同时希望刚工作不久的......
  • java 泛型 深入
    评:泛型的好处:(casting)的绝对无误。/*******不使用泛型类型*******/Listlist1=newArrayList();list1.add(8080);//编译器不检查值String......
  • 【技术积累】JavaSciprt中的函数【一】
    什么是函数?如何声明函数?JavaScript中的函数是一段可重复使用的代码块,它可以接受输入并返回输出。在JavaScript中,函数是一种特殊的对象,因此可以将其存储在变量中,将其作为参数传递给其他函数,并从其他函数中返回。在JavaScript中,声明函数有两种方式:函数声明和函数表达式。1.函数......
  • Java8 Stream List Map:Stream 流对象汇总 求和 某个属性 BigDecimal MDouble
    测试实体(数字对象使用MDouble):importcom.mchweb.common.lang.MDouble;importlombok.*;@Getter@Setter@Builder(toBuilder=true)@NoArgsConstructor@AllArgsConstructorpublicclassUser{privateMDoublemoney;}importcom.mchweb.common.lang.MDouble;imp......
  • 你真的读懂了Java源码?Collections源码初探
    最近重温Java知识,遇到不懂的问题搜索互联网/博客很难直接找到答案,还好如今有了chatGPT,比大多数CV复读机/纯文档翻译的内容更有用。很多文章总结冠以“深入理解xxx”,“万字长文详解xxx”的文章,也不过是演示一遍调用代码,让你知道了怎么用,在什么情况下用。但至于为什么这么用,以及Java......
  • java WebUploader 分片上传
    ​ 对于大文件的处理,无论是用户端还是服务端,如果一次性进行读取发送、接收都是不可取,很容易导致内存问题。所以对于大文件上传,采用切块分段上传,从上传的效率来看,利用多线程并发上传能够达到最大效率。 本文是基于springboot+vue实现的文件上传,本文主要介绍服务端实现文件......
  • C# 将 List<dynamic> 转换为 List<string>
    vardlist=newList<dynamic>(){"Guangzhou","Zhuhai","Shenzhen"};提取集合中的所有字符串,忽略所有其他类型,可以使用://Solution1:Includeonlystrings,nonullvalues,noexceptionsthrownvarstrings=dlist.OfType<stri......
  • Java 利用POI对象 SXSSFWorkbook 导出Excel
    最开始调用的方法是(标记的地方): workbook=newHSSFWorkbook();和workbook=newXSSFWorkbook();这两个方法就是导出Excel的最关键的方法,接下来我来说说这两个方法作用:1.HSSFWorkbook:是操作Excel2003以前(包括2003)的版本,扩展名是.xls;2.XSSFWorkbook:是操作Excel2007的版本......
  • Java判断一个List中是否有重复元素
    1.将List转为Set,通过2个集合的size大小是否相等来判断有无重复元素publicstaticvoidmain(String[]args){ListstringList=newArrayList<>(Arrays.asList("a","a","b","c"));SetstringSet=newHashSet<>(stringList);......
  • Javascript中的内存(垃圾)回收机制
    JavaScript具有自动垃圾回收机制。垃圾收集器会按照固定的时间间隔周期性的执行常见的垃圾回收方式:标记清除、引用计数方式。一、标记清除方法1、工作原理:【标记“离开环境”的就回收内存】当变量进入环境时,将这个变量标记为“进入环境”。当变量离开环境时,则将其标记为“......