java的泛型擦除与泛型数组的可能性

何为泛型擦除

java中实现的泛型是伪泛型:即对于泛型graph<Integer>和graph<Double>,在编译后都会变成graph,在JVM看来没有区别。
Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会在编译器在编译的时候去掉。这个过程就称为泛型擦除。
究其原因,是因为泛型并不是java一开始就存在的特性,而是在1.5版本后附加的新特性,考虑到之前库与代码的兼容性,只能采取可擦除这一机制才能保证既实现了泛型的概念,又保证原先代码的可用性,是一种无奈之举。

数组与泛型容器

java中用来储存多个对象常用的手段便是数组和泛型容器,再考虑泛型擦除的影响之前,我们先来探讨一下数组与容器的不同:

数组——共变数组

为保证没有泛型之前的一些用数组解决的问题,需要数组能像其元素类型一样能发生协变,我们称之为数组的协变性:

  • 例如:Base是Sub的基类,那么Base[]便是Sub[]的基类。

由于这个特性,我们经常对数组进行转型操作,而正是这一转型操作,足以骗过编译器,造成一些不易察觉的错误,也使得java不得不放弃泛型数组。

泛型——不存在协变

  • 例如 Base是Sub的基类,但是List<Base>不是List<Sub>的基类。

这一不支持协变的特性使得在编译器检查阶段能确保形式的正确,在擦除后也能正常工作。

泛型数组 < T >

java并不是静止泛型数组的存在,而是禁止直接创建泛型数组实例的操作:

	List<Integer>[] myList = new List<Integer>[10];
	//error Cannot create a generic array of List<Integer>

试想,如果支持这种操作会有什么后果?
引用sun文档的例子

	List<String>[] lsa = new List<String>[10]; // Not really allowed.    
	Object o = lsa;    
	Object[] oa = (Object[]) o;    
	List<Integer> li = new ArrayList<Integer>();    
	li.add(new Integer(3));    
	oa[1] = li; // Unsound, but passes run time store check    
	String s = lsa[1].get(0); // Run-time error: ClassCastException.

假设第一行的泛型数组没有加以限制,通过向上转型后便可插入其他类型的泛型元素,在插入时因为泛型擦除不会发生错误,而取出时将导致运行出错,为了防止这种类型隐式转换造成的问题,java声明泛型数组必须显式的进行转换。

泛型数组 < ? >

java要求,声明泛型数组必须采用通配符:

List<?>[] lsa = new List<?>[10];                //1
List<Integer> li = new ArrayList<Integer>();
li.add(new Integer(3));
// Correct.
lsa[1] = li;
// Run time error, but cast is explicit.
Integer s = (Integer) lsa[1].get(0);              //2

这种做法需要我们清晰的清楚泛型数组中的元素类型,并加以显式的类型转换。

总结

综上,数组的类型不可以是类型变量,除非是采用通配符的方式。因为对于通配符的方式,最后取出数据是要做显式的类型转换的。而List<>等泛型实现的容器没有协变性,可以在编译检查解决大部分错误,是更加省心省力的选择。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值