Java 泛型的类型擦除

Java的泛型是伪泛型,Java在编译期间,所有的泛型信息都会被擦掉。正确理解泛型概念的首要前提是理解类型擦除。Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的,使用泛型的时候加上类型参数,在编译器编译的时候会去掉,这个过程成为类型擦除

 

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

  • 在不指定泛型的情况下,泛型变量的类型为该方法中的几种类型的同一父类的最小级,直到Object
  • 在指定泛型的情况下,该方法的几种类型必须是该泛型的实例的类型或者其子类

 

先检查类型,然后在进行类型擦除,再编译,以及编译的对象和引用传递问题

 

涉及类型检查的是它的引用。

ArrayList<String> list1 = new ArrayList();
list1.add("1"); //编译通过
list1.add(1); //编译错误  

ArrayList list2 = new ArrayList<String>();
list2.add("1"); //编译通过  
list2.add(1); //编译通过  

自动类型转换

因为类型擦除的问题,所以所有的泛型类型变量最后都会被替换为原始类型。

既然都被替换为原始类型,那么为什么我们在获取的时候,不需要进行强制类型转换呢?

public E get(int index) {  

    RangeCheck(index);  

    return (E) elementData[index];  //(E)虽然泛型信息会被擦除掉,但是会将(E) //elementData[index],编译为(Date)elementData[index]

}

当存取一个泛型域时也会自动插入强制类型转换。自动地在字节码中插入强制类型转换。

 

运行时类型查询

在编译前的类型检查时报错误。

        ArrayList<String> arrayList = new ArrayList<String>();
        if(arrayList instanceof ArrayList<String>){

        }

因为类型擦除之后,ArrayList<String>只剩下原始类型ArrayList,泛型信息String不存在了。

不会报错

        if(arrayList instanceof ArrayList){

        }

 

泛型在静态方法和静态类中的问题

泛型类中的静态方法和静态变量不可以使用泛型类所声明的泛型类型参数

举例说明:

public class Test2<T> {    
    public static T one;   //编译错误    
    public static  T show(T one){ //编译错误    
        return null;    
    }    
}

因为泛型类中的泛型参数的实例化是在定义对象的时候指定的,而静态变量和静态方法不需要使用对象来调用。对象都没有创建,如何确定这个泛型参数是何种类型,所以当然是错误的。static方法要使用泛型能力,就必须使其成为泛型方法。

但是要注意区分下面的一种情况:

public class Test2<T> {    

    public static <T >T show(T one){ //这是正确的。 在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。static方法要使用泛型能力,就必须使其成为泛型方法。
        return null;    
    }    
}

类型擦除与多态的冲突和解决方法 --(桥方法)

public class Node<T> {
 
    public T data;
 
    public Node(T data) { this.data = data; }
 
    public void setData(T data) {
        System.out.println("Node.setData");
        this.data = data;
    }
}
 
public class MyNode extends Node<Integer> {
    public MyNode(Integer data) { super(data); }
 
    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
}

 

//编译器生成的类。

class MyNode extends Node {
 
    // 编译器生成的桥方法
    //
    public void setData(Object data) {
        setData((Integer) data);
    }
 
    public void setData(Integer data) {
        System.out.println("MyNode.setData");
        super.setData(data);
    }
 
    // ...
}

泛型方法与可变参数

再看一个泛型方法和可变参数的例子:

public <T> void printMsg( T... args){
    for(T t : args){
        Log.d("泛型测试","t is " + t);
    }
}
printMsg("111",222,"aaaa","2323.4",55.55);

java 和C++泛型的区别

在C++中,模板本质上就是一套宏指令集,只是换了个名头,编译器会针对每种类型创建一份模板代码的副本

有个证据可以证明这一点:MyClass<Foo>不会与MyClass<Bar>共享静态变量。然而,两个MyClass<Foo>实例则会共享静态变量

由于架构设计上的差异,Java泛型和C++模板还有如下很多不同点;

1. C++模板可以使用int等基本数据类型。Java则不行,必须转而使用Integer

2. Java中,可以将模板的类型参数限定为某种特定类型。例如,你可能会使用泛型实现CardDeck,并规定参数必须扩展自CardGame。

3. C++中,类型参数可以实例化,Java不可以实例化

4. Java中,类型参数(即MyClass<Foo>中的Foo)不能用于静态方法和变量,因为他们会被MyClass<Foo>和MyClass<Bar>共享。但在C++中,这些类是不同的,类型参数可以用于静态方法和静态变量。

5. 在Java中,不管类型参数是什么,MyClass的所有实例都是同一类型。类型参数会在运行时被抹去。而C++中,参数类型不同,实例类型也不同

 

参考:

https://www.cnblogs.com/wuqinglong/p/9456193.html

https://www.cnblogs.com/linghu-java/p/10031239.html

https://blog.csdn.net/coding_is_fun/article/details/81564512

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值