集合泛型定义问题怎么理解?

List list = new ArrayList<Integer>();//为什么能添加任何类型
List<String> list = new ArrayList<>();//只能添加String类型

阅读 1.9k
3 个回答
List list = new ArrayList<Integer>();//为什么能添加任何类型

泛型只是在编译期检查类型,在运行时泛型已经不存在了,这个写法是在实例化的时候声明了泛型,但是赋值给list的时候泛型丢弃了,因为list的声明类型是 List,List本身不带泛型相当于List<Object>或者 List<?> 所以可以添加任何类型

List<String> list = new ArrayList<>();//只能添加String类型

这个写法相当于List<String> list = new ArrayList<String>();,只是java8时加入的一个简化写法而已,效果是一样的

修正:是java7加入的语法糖,类型推断

要区分数组和泛型容器,那么就需要先理解以下三个概念:协变性(covariance)、逆变性(contravariance)和无关性(invariant)。

若类A是类B的子类,则记作A≦B。设有变换f(),则有以下定律:

  • 当A≦B时,有f(A)≦f(B),则称变换f()具有协变性。
  • 当A≦B时,有f(B)≦f(A),则称变换f()具有逆变性。
  • 如果以上两者皆不成立,那么称变换f()具有无关性。

在Java语言中,数组具有协变性,而泛型具有无关性,示例代码如下所示:

   Object[] array = new Integer[1];
   //这里ide会有红色下划线,表示编译错误。
   ArrayList<Object> list = new ArrayList<Integer>();

以上这两行代码,数组正常编译通过,而泛型抛出了编译期错误,应用之前提出的概念对代码进行分析,可知以下推论:

数组的变换可以表达为f(A)=A[],通过之前的示例

可以得出以下推论:

f(String) = String[] 以及 f(Object) = Object[]

通过代码验证,String[]≦Object[]是成立的,由此可见,数组具有协变性。ArrayList泛型的变换可以表达为f(A)=ArrayList

得出以下推论:

f(String) = ArrayList<String> 以及 f(Object) = ArrayList<Object>

最终得出结论,数组具备协变性,而泛型具备无关性。

所以,为了让泛型具备协变性和逆变性,Java引入了有界泛型的概念。

除了协变性的不同,数组还是具象化的,而泛型不是。

什么是具象化(也可以称之为具体化,物化)?在《Java语言规范》里,明确地规定了具象化类型的定义:

完全在运行时可用的类型被称为具象化类型(refiable type),会做这种区分是因为有些类型会在编译过程中被擦除,并不是所有的类型都在运行时可用。它包括:

  • 非泛型类声明,接口类型声明。
  • 所有泛型参数类型为无界通配符(仅用‘?’修饰)的泛型参数类。
  • 原始类型。
  • 基本数据类型。
  • 其元素类型为具象化类型的数组。
  • 嵌套类(内部类、匿名内部类等,例如java.util.HashMap.Entry),并且嵌套过程中的每一个类都是具象化的。

无论是在编译时还是运行时,数组都能确切地知道自己所属的类型。但是泛型在编译时会丢失部分类型信息,在运行时,它又会被当作Object处理。

Java的泛型最后都被当作上界  `(? extend Type )` 处理了。

换言之,数组必须清楚地知道自己内部元素的类型,并且会一直保存这个类型信息,在添加元素的时候,该信息会被用于做类型检查,而泛型的类型是不确定的。所以,在编译器层面就杜绝了这个问题的发生。这在《Java语言规范》里有明确地说明:

If the element type of an array were not reifiable,
the virtual machinecould not perform the store check described 
in the preceding paragraph.This is why 
creation of arrays of non-reifiable types is forbidden. 
Onemay declare variables of array types whose element 
type is not reifiable,but any attempt to assign them a value will 
give rise to an uncheckedwarning.

如果数组的元素类型不是具象化的,那么虚拟机将无法应用在前面章节里描述过的存储检
查。这就是为什么创建(实例化)非具象化的数组是不允许
的。你可以定义(声明)一个元素类型是非具象化的数组类
型,但任何试图给它分配一个值的操
作,都会产生一个unchecked warning。存储检查:
这里涉及Array的基本原理,可以自行参阅《Java语言规范》

泛型具有无关系。

因为 List 啥都能添加,List<String> 只能添加 String

推荐问题
宣传栏