Bootstrap

java-泛型程序设计(三)

java学习之路记录
前两篇博客已经介绍了java泛型程序设计的基础,跟类型擦除问题。这篇介绍一下最后一个问题。因为类型擦除带来的一系列问题。在这些问题中,本人认为最重要的就是java不支持创建泛型数组(泛型数组这个叫法,不能的书有不同的解释,这个稍后讨论)


类型擦除后带来的约束跟局限性

1 不能用基本类型做类型参数

例如没有Pair<double> 只有Pair<Double>

原因很简单,因为类型擦除后,Pair类中只含有Object类型的域,而Object不能存储double值。


2 运行时类型检查只使用于原始类型
泛型不能用于显式的引用进行时类型的操作之中,这些操作例如转型,instanceof ,new 表达式。因为这些操作都得知道参数的真实类型。但是实际上这些参数的类型已经被擦除了。
例如,下面测试a是否是任意类型的一个Pair.

if (a instanceof Pair<String>) //编译不通过。因为参数类型String 在运行会被擦掉。

3 不能创建参数化类型的数组
首先说明一下,我看了多篇博客,这个说法也叫 “不能创建泛型数组“,但是在java核心卷一第十版这本书中,“不能创建泛型数组“是另外一个意思。具体是什么意思,见后文说明。现在暂且叫做不能创建泛型数组。
为什么不能创建泛型数组呢?
我参考了这篇博客:
http://www.blogjava.net/sean/archive/2005/08/09/9630.html
http://blog.csdn.net/aabbwoshishei/article/details/50163261
下面说下我的理解:
Java的范型停留在编译这一层,到了运行时,这些范型的信息其实是被抹掉的.
我们现在假设java允许定义泛型数组

 Pair<String>[] p=new Pair<String>[10];//实际这句是不能通过编译的,eclipse会提示错误  

以上代码定义了一个泛型数组。然后我们把p可以转换成Object[].因为 Object[]是所有数组的父类,根据java的向上转换性质,是可以这样做的。
接着,问题来了。

 Object[0] = new Pair<Double>()//Object[0]存入的是Double类型
 Object[1] = new Pair<String>();//Object[1]存入的是String类型

这样的存入在编译阶段是不会报错的。因为它们的父类都是object类型。如果允许定义泛型数组,那么在运行阶段,参数类型会被擦掉,虚拟机只知道存入的是 Pair类就可以了,这样虚拟机在运行阶段也不会报错。
想想看,我们本来定义的是装Pair<String>的数组,结果我们却可以往里面放任何Pair,接下来如果有代码试图按原有的定义去取值,后果是什么不言自明。


补充说明
需要说明的是,java只是不需要创建泛型数组,但是声明类型Pair<String>[] 的变量还是合法的,不过不能用 new Pair<String>[10] 初始化这个变量p。
如果想要创建泛型数组怎么办?
如果需要收集参数化类型对象(收集多个泛型类型对象),只有一种安全有效的方法:使用ArrayList:ArrayList<Pair<String>>


4 不能实例化类型变量
意思就是说 不能使用像 new T(..), new T[..] ,T.class
这样的表达式中的类型变量。举个例子:

//Pair<T>这样的构造器是非法的。
public Pair(){
    first = new T ();
    second =  new T();
}

//不能实例化泛型数组
T[] mm = new T[2];

因为类型 T 会被擦除,那么就变成了 new Object(); 反正类似 new T()肯定不正确。
至于怎么去构造一个泛型对象。传统的方法是通过反射调用Class.newInstance方法来构造。


5 不能抛出也不能捕获泛型类的实例
具体的不想写了。。。。


以上就是运行时候擦除泛型带来的几个主要问题。接下来泛型还有个知识点就是 通配符类型。

;