泛型特性——擦除详解

泛型特性——在运行时,所有泛型类型被擦除,替换为它们的非泛型上界。List< T >替换为List,变量被替换为Object。

从外部看——擦除泛型参数
通过程序示例的两个测试可以看到,泛型参数在运行时不会有任何体现,LinkedList< String >()和LinkedList< Intege r>()的容器是原生容器,而泛型参数也是原生参数,在运行时泛型参数被擦除称为原生类型。

class EraseTest{
    public static void test1(){
        Class c1=new LinkedList<String>().getClass();
        Class c2=new LinkedList<Integer>().getClass();
        System.out.println(c1==c2);
    }

    public static void test2(){
        TypeVariable[] variables=new HashMap<String,Integer>().getClass().getTypeParameters();
        System.out.println(Arrays.toString(variables));

        variables=new TreeSet<String>().getClass().getTypeParameters();
        System.out.println(Arrays.toString(variables));
    }

    public static void main(String[] args) {
        test1();
        test2();
    }
}
//output:
//true
//[K, V]
//[E]

从内部看——在方法和类的内部擦除类型信息
下面两种法应该是通常想做的,但是在类和方法的内部并没有T的类型信息,任何在运行时需要知道的类型信息也都无法工作。也就是说在类的内部我们不能使用T。

class Erased<T> {
    T t=new T();  //Error
    
    public Erased(){
        t=new T();   //Error
        if(t instanceof T) {}  //Error
        if(t instanceof Object) {}  //OK
    }
}

由于擦除了类型信息,运行时无法探测T的真正类型,也就无法判断是否具有T是否具有f()方法,但是我发现可以调用Object方法,也就证明了在类和方法内部泛型变量被擦除到了Object(这个例子在C++可以,因为C++是纯正的泛型)。

class F{
    public void f(){ System.out.println("f()"); }
}
class Erased<T>{
    T t;
    public void Hasf(){
        t.f();   //Error
        t.hashCode();    //OK
    }

    public static void main(String[] args) {
        Erased<F> erased=new Erased<F>();
    }
}

迁移兼容性
由于初期的Java不支持泛型,但又不为了不重写类库,擦除是在泛型代码和非泛型代码之间的中和,擦除使得某个类库使用泛型的证据被擦除,使得泛型与非泛型实际是一样的,代码之间的交互也不需要探测对方是否使用了泛型,这样就实现了迁移兼容性。

再继续上面的例子,你会发现有泛型参数和无泛型参数的LinkedList也是一样的。

class EraseTest{
    public static void test1(){
        Class c1=new LinkedList<String>().getClass();
        Class c2=new LinkedList<Integer>().getClass();
        Class c3=new LinkedList().getClass();
        System.out.println(c1==c2);
        System.out.println(c1==c3);
        System.out.println(c2==c3);
    }
    public static void main(String[] args) {
        test1();
    }
}
//true
//true
//true

迁移兼容性使得泛型并不是强制的。

class GenericBase<T>{
    private T t;
    public T getT() { return t; }
    public void setT(T t) { this.t = t; }
}
class Derived extends GenericBase{ }

class Test{
    public static void main(String[] args) {
        Derived derived=new Derived();
        derived.setT(new String());
        Object obj=derived.getT();
    }
}

泛型作用
提供编译期间检查
s1与s2的区别是类型转换,但他们的汇编代码是一样的,也就是说泛型对传递出去的值自动转换。integer1在运行时异常,而integer2在编译期就无法通过,因为自动添加类型转换的缘故导致了类型不匹配。所以泛型可以是提供编译期间的检查。

class SimpleHolder{
    private Object obj;
    public Object getObj() { return obj; }
    public void setObj(Object obj) { this.obj = obj; }
}
class GenericHolder<T>{
    private T obj;
    public T getObj() { return obj; }
    public void setObj(T obj) { this.obj = obj; }
}
class ComparingTest{
    public static void main(String[] args) {
        SimpleHolder simpleHolder=new SimpleHolder();
        simpleHolder.setObj("Item");
        GenericHolder<String> genericHolder=new GenericHolder<String>();
        genericHolder.setObj("Item");
        
        
        String s1=(String)simpleHolder.getObj();    //类型转换
        String s2=genericHolder.getObj();          //无需类型装换

        Integer integer1=(Integer)simpleHolder.getObj(); //运行时异常
//      Integer integer2=(Integer)GenericHolder.getObj(); //编译期异常
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值