Java 深入学习(30) —— 泛型的实现,擦除

1 什么是擦除


这里写图片描述

从以上的代码可以看出,List<String>List<Integer> 在运行时事实上是相同的类型,这两种形式都被擦除成它们原生的类型。

Java 泛型是使用擦除来实现的,当你在使用泛型的时候,任何具体的类型信息都会被擦除掉,你唯一知道的就是你在使用一个对象。


这里写图片描述

由以上的代码可知, Class.getTypeParameters() 将返回一个 TypeVariable 对象数组,表示有泛型声明所声明的类型参数。但是,返回的只是用作参数占位符的标识符,这并非有用的信息。

残酷的事实:在泛型代码的内部,无法获得任何有关泛型参数类型的信息。

你可以知道诸如:类型参数标识符、泛型类型边界这类信息,却无法知道用来创建某个特定实例的实际的类型参数。


2 类型擦除后保留的原始类型


原始类型(raw type)就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型。

无论何时定义一个泛型类型,相应的原始类型都会被自动地提供。类型变量被擦除(erasure),并使用其限定类型(无限定的变量用Object)替换。


无限定的类型参数

class Pair<T> {  
    private T value;  
    public T getValue() {  
        return value;  
    }  
    public void setValue(T  value) {  
        this.value = value;  
    }  
}  

Pair<T>的原始类型为:

class Pair {  
    private Object value;  
    public Object getValue() {  
        return value;  
    }  
    public void setValue(Object  value) {  
        this.value = value;  
    }  
}  

因为在Pair<T>中,T是一个无限定的类型变量,所以用Object替换,其结果就是一个普通的类。

在程序中可以包含不同类型的Pair,如Pair<String>Pair<Integer>,但是,擦除类型后它们就成为原始的Pair类型了,原始类型都是Object。


有限定(边界)的类型参数

如果类型变量有限定,那么原始类型就用第一个边界的类型变量来替换。

public class Pair<T extends Comparable& Serializable> {  }

原始类型为 Comparable。


public class Pair<T extends Serializable&Comparable > {  }

原始类型为 Serializable。

而编译器在必要的时要向Comparable插入强制类型转换。为了提高效率,应该将标签(tagging)接口(即没有方法的接口)放在边界限定列表的末尾。


泛型类型参数将擦除到它的第一个边界(它可能会有多个边界)。

类型参数的擦除,编译器实际会把类型参数替换为它的擦除。

像上面的例子中,T 擦除到了 Serializable。


3 原始类型与泛型变量的类型


在调用泛型方法的时候,可以指定泛型,也可以不指定泛型。

不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一个父类的最小级,直到Object

指定泛型的时候,该方法中的几种类型必须是该泛型实例类型或者其子类

public class Test2{  
    public static void main(String[] args) {  
        /**不指定泛型的时候*/  
        int i=Test2.add(1, 2); //这两个参数都是Integer,所以T为Integer类型  
        Number f=Test2.add(1, 1.2);//这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Number  
        Object o=Test2.add(1, "asd");//这两个参数一个是Integer,以风格是Float,所以取同一父类的最小级,为Object  

        /**指定泛型的时候*/  
        int a=Test2.<Integer>add(1, 2);//指定了Integer,所以只能为Integer类型或者其子类  
        int b=Test2.<Integer>add(1, 2.2);//编译错误,指定了Integer,不能为Float  
        Number c=Test2.<Number>add(1, 2.2); //指定为Number,所以可以为Integer和Float  
    }  

    //这是一个简单的泛型方法  
    public static <T> T add(T x,T y){  
        return y;  
    }  
}  

其实在泛型类中,不指定泛型的时候,也差不多,只不过这个时候的泛型类型为Object,就比如ArrayList中,如果不指定泛型,那么这个ArrayList中可以放任意类型的对象。

public static void main(String[] args) {  
        ArrayList arrayList=new ArrayList();  
        arrayList.add(1);  
        arrayList.add("121");  
        arrayList.add(new Date());  
    }  

4 类型擦除引起的问题及解决方法


4.1 先检查,在编译,以及检查编译的对象和引用传递的问题


public static  void main(String[] args) {  
        ArrayList<String> arrayList=new ArrayList<String>();  
        arrayList.add("123");  
        arrayList.add(123);//编译错误  
    }  

http://blog.csdn.net/lonelyroamer/article/details/7868820


抄不下去了…

编译,运行,检查傻傻的分不清楚。

等我学完了 JVM 再回来填坑

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值