《Java 核心技术 卷一》 泛型类型章节中“仍会导致一个类型错误” 是什么类型错误 ?

这个 “仍会导致一个类型错误” 是什么类型错误 ?


回答:

private static class Pair<T> {

public T t ;

public Pair(T t) {

this.t = t;

}

}

public static void main(String[] args) {

Pair<String> pairs[]=new Pair[10];

Object objPairs[]=pairs;

//这里报错是因为声明类型虽然为Object,但是实际的数组类型已经确定了为Pair,所以在赋值时就会报错了

//java.lang.ArrayStoreException: java.lang.String

// objPairs[0]="123";

//这里并不会报错,因为能通过类型检查: 泛型因为被擦除,最后检查的都是Pair<Object>

objPairs[0]=new Pair<Integer>(1);

//但是如果我在通过pairs数组去拿就会出错了

//java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

Pair<String> pair = pairs[0];

//8.6.3的小节标题就是结论: 不能创建参数化类型的数组,因为泛型擦除了,我个人理解是创建了也没啥用,在编译器检测不出来错误。

}


回答:

要区分数组和泛型容器,那么就需要先理解以下三个概念:协变性(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语言规范》

泛型具有无关系。

.......

以上是 《Java 核心技术 卷一》 泛型类型章节中“仍会导致一个类型错误” 是什么类型错误 ? 的全部内容, 来源链接: utcz.com/p/944968.html

回到顶部