Java泛型:类型擦除
类型擦除
代码片段一
Class c1 = new ArrayList<Integer>().getClass();
Class c2 = new ArrayList<String>().getClass();
System.out.println(c1 == c2); /* Output
true
*/
显然在平时使用中,ArrayList<Integer>()和new ArrayList<String>()是完全不同的类型,但是在这里,程序却的的确确会输出true。
这就是Java泛型的类型擦除造成的,因为不管是ArrayList<Integer>()还是new ArrayList<String>(),都在编译器被编译器擦除成了ArrayList。那编译器为什么要做这件事?原因也和大多数的Java让人不爽的点一样——兼容性。由于泛型并不是从Java诞生就存在的一个特性,而是等到SE5才被加入的,所以为了兼容之前并未使用泛型的类库和代码,不得不让编译器擦除掉代码中有关于泛型类型信息的部分,这样最后生成出来的代码其实是『泛型无关』的,我们使用别人的代码或者类库时也就不需要关心对方代码是否已经『泛化』,反之亦然。
在编译器层面做的这件事(擦除具体的类型信息),使得Java的泛型先天都存在一个让人非常难受的缺点:
在泛型代码内部,无法获得任何有关泛型参数类型的信息。
代码片段二
List<Integer> list = new ArrayList<Integer>();
Map<Integer, String> map = new HashMap<Integer, String>();
System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
System.out.println(Arrays.toString(map.getClass().getTypeParameters())); /* Output
[E]
[K, V]
*/
关于getTypeParameters()的解释:
Returns an array of TypeVariable objects that represent the type variables declared by the generic declaration represented by this GenericDeclaration object, in declaration order. Returns an array of length 0 if the underlying generic declaration declares no type variables.
我们期待的是得到泛型参数的类型,但是实际上我们只得到了一堆占位符。
代码片段三
public class Main<T> {
    public T[] makeArray() {
        // error: Type parameter 'T' cannot be instantiated directly
        return new T[5];
    }
}
我们无法在泛型内部创建一个T类型的数组,原因也和之前一样,T仅仅是个占位符,并没有真实的类型信息,实际上,除了new表达式之外,instanceof操作和转型(会收到警告)在泛型内部都是无法使用的,而造成这个的原因就是之前讲过的编译器对类型信息进行了擦除。
同时,面对泛型内部形如T var;的代码时,记得多念几遍:它只是个Object,它只是个Object……
代码片段四
public class Main<T> {
    private T t;
    public void set(T t) {
        this.t = t;
    }
    public T get() {
        return t;
    }
    public static void main(String[] args) {
        Main<String> m = new Main<String>();
        m.set("findingsea");
        String s = m.get();
        System.out.println(s);
    }
}
/* Output
findingsea
*/
虽然有类型擦除的存在,使得编译器在泛型内部其实完全无法知道有关T的任何信息,但是编译器可以保证重要的一点:内部一致性,也是我们放进去的是什么类型的对象,取出来还是相同类型的对象,这一点让Java的泛型起码还是有用武之地的。
代码片段四展现就是编译器确保了我们放在t上的类型的确是T(即便它并不知道有关T的任何类型信息)。这种确保其实做了两步工作:
set()处的类型检验get()处的类型转换
这两步工作也成为边界动作。
代码片段五
public class Main<T> {
    public List<T> fillList(T t, int size) {
        List<T> list = new ArrayList<T>();
        for (int i = 0; i < size; i++) {
            list.add(t);
        }
        return list;
    }
    public static void main(String[] args) {
        Main<String> m = new Main<String>();
        List<String> list = m.fillList("findingsea", 5);
        System.out.println(list.toString());
    }
}
/* Output
[findingsea, findingsea, findingsea, findingsea, findingsea]
*/
代码片段五同样展示的是泛型的内部一致性。
擦除的补偿
如上看到的,但凡是涉及到确切类型信息的操作,在泛型内部都是无法共工作的。那是否有办法绕过这个问题来编程,答案就是显示地传递类型标签。
代码片段六
public class Main<T> {
    public T create(Class<T> type) {
        try {
            return type.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public static void main(String[] args) {
        Main<String> m = new Main<String>();
        String s = m.create(String.class);
    }
}
代码片段六展示了一种用类型标签生成新对象的方法,但是这个办法很脆弱,因为这种办法要求对应的类型必须有默认构造函数,遇到Integer类型的时候就失败了,而且这个错误还不能在编译器捕获。
进阶的方法可以用限制类型的显示工厂和模板方法设计模式来改进这个问题,具体可以参见《Java编程思想 (第4版)》P382。
代码片段七
public class Main<T> {
    public T[] create(Class<T> type) {
        return (T[]) Array.newInstance(type, 10);
    }
    public static void main(String[] args) {
        Main<String> m = new Main<String>();
        String[] strings = m.create(String.class);
    }
}
代码片段七展示了对泛型数组的擦除补偿,本质方法还是通过显示地传递类型标签,通过Array.newInstance(type, size)来生成数组,同时也是最为推荐的在泛型内部生成数组的方法。
以上,泛型的第二部分的结束。
Java泛型:类型擦除的更多相关文章
- java泛型-类型擦除
		
详细内容:参考java编程思想P373,p650. Java 泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,但有一点需要注意:Java 的泛型在编译器有效,在运行期被删除,也就 ...
 - 聊一聊Java泛型的擦除
		
最近看了<thinking in java>的第十五章泛型,感觉有些东西需要记录下来. 泛型是Java SE5才被引入的概念,现在我的工作中泛型主要使用在集合,这样可以知道set()和ge ...
 - Java中泛型 类型擦除
		
转自:Java中泛型是类型擦除的 Java 泛型(Generic)的引入加强了参数类型的安全性,减少了类型的转换,但有一点需要注意:Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类 ...
 - 【进阶之路】Java的类型擦除式泛型
		
Java选择的泛型类型叫做类型擦除式泛型.什么是类型擦除式泛型呢?就是Java语言中的泛型只存在于程序源码之中,在编译后的字节码文件里,则全部泛型都会被替换为原来的原始类型(Raw Type),并且会 ...
 - 神奇的互换身体术--java的类型擦除
		
故事背景 <互换身体>是由环球影业发行的喜剧电影,于2011年8月5日在美国上映.该片由大卫·道金执导,瑞安·雷诺兹.杰森·贝特曼.奥利维亚·王尔德等主演.该片讲述了一位居家好男人和一位蜂 ...
 - java 泛型 类型作为参量 Class<T> transform
		
Class<T> transform T:作为类型,用于定义变量: transform:作为具体类的类:用于创建实例. 类型信息是脱敏的具体类: 可以使用class的具体功能: 不能使用具 ...
 - 转:有关Java泛型的类型擦除(type erasing)
		
转载自:拈花微笑 自从Java 5引入泛型之后,Java与C++对于泛型不同的实现的优劣便一直是饭后的谈资.在我之前的很多training中,当讲到Java泛型时总是会和C++的实现比较,一般得出的结 ...
 - 关于Java泛型"擦除"的一点思考
		
头次写博客,想说的东西不难,关于泛型的疑问,是前一阵在学习jackson中遇到的. 下面就把我所想到的.遇到的,分享出来. 泛型是JDK1.5后的一个特性,是一个参数类型的应用,可以将这个参数声明在类 ...
 - 初探Java类型擦除
		
本篇博客主要介绍了Java类型擦除的定义,详细的介绍了类型擦除在Java中所出现的场景. 1. 什么是类型擦除 为了让你们快速的对类型擦除有一个印象,首先举一个很简单也很经典的例子. // 指定泛型为 ...
 - 【Java心得总结三】Java泛型上——初识泛型
		
一.函数参数与泛型比较 泛型(generics),从字面的意思理解就是泛化的类型,即参数化类型.泛型的作用是什么,这里与函数参数做一个比较: 无参数的函数: public int[] newIntAr ...
 
随机推荐
- (转)使用Amoeba 实现MySQL DB 读写分离
			
Amoeba(变形虫)项目是一个开源框架,于2008年开始发布一款 Amoeba for Mysql软件: 这个软件致力于MySQL的分布式数据库前端代理层,它主要在应用层访问MySQL的时候充当SQ ...
 - 3月31日学习笔记(CSS基础)
			
背景属性 文本属性 direction 属性影响块级元素中文本的书写方向.表中列布局的方向.内容水平填充其元素框的方向.以及两端对齐元素中最后一行的位置. 注释:对于行内元素,只有当 unicode- ...
 - spring aop配置及用例说明(1)
			
欢迎转载交流,博客地址http://www.cnblogs.com/shizhongtao/p/3469776.html 首先,什么是aop,其实通俗一点讲就是,再方法执行时候我们加入其它业务逻辑.比 ...
 - 一个封装好的C++比特数组BitArray,可以对位进行直接操作
			
本来仅仅只是用来做哈夫曼实验时的辅助,后来一想干脆封装好,省得以后又要用到比特位的操作. 基本用法示例: BitArray bits; bits[] = ; bits[] = ; cout<& ...
 - 关于fputs和fgets的几个细节
			
C语言中两个标准IO fputs和fgets都是针对行来进行数据的读取的!这里关于这两个IO函数我有几个小细节想在这里和大家分享一下,希望能够对大家产生帮助! 首先贴上这两个函数的函数声明,下面以这两 ...
 - excel 导入功能
			
一:示例代码 //InputStream fis = new FileInputStream(tomcaturl+this.awardTask.getFileRoute());//可以通过上述方式获得 ...
 - java在线截图---通过指定的URL对网站截图
			
如何以java实现网页截图技术 http://wenku.baidu.com/view/a7a8b6d076eeaeaad1f3305d.html http://blog.csdn.net/cping ...
 - TreeView递归取值
			
string jingyuan = ""; string jinghui = ""; private void DiGui(TreeNode tn) { if ...
 - GridView - javascript 触发后台 OnSelectedIndexChanged
			
1.ASPX <asp:GridView ID="gdvDealers" runat="server" AutoGenerateColumns=" ...
 - ACE_linux:TCP通信
			
1.涉及类 ACE_INET_Addr//ACE网络地址ACE_SOCK_Acceptor//ACE网络服务器ACE_SOCK_Connector//ACE网络客户端ACE_SOCK_Stream// ...