Java泛型:类型擦除

Posted 流楚丶格念

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java泛型:类型擦除相关的知识,希望对你有一定的参考价值。

文章目录

问题导引

这个例子里,定义了两个List集合,不过一个是List泛型类型的,只能存储整数;一个 是List泛型类型的,

只能存储字符串。最后我们通过list1对象和list2对象的 getClass()方法获取他们的类的信息,结果发现结

果为true。说明泛型类型String和 Integer都被擦除掉了,只剩下原始类型。

例如如下代码:

package com.fanxing;

import java.util.ArrayList;
import java.util.List;

public class FanTest 
    public static void main(String[] args) 
        List<Integer> list1 = new ArrayList<Integer>();
        List<String> list2 = new ArrayList<String>();
        System.out.println(list1.getClass() == list2.getClass());
    

他的运行结果是true:

啊??????啊这??????这是啥?

别慌,这就是类型擦除,下面咱们来详细查看一下:

类型擦除概念

泛型是java1.5版本才引进的概念,在这之前没有泛型,但是,泛型代码能够很好的和之前版本的代码兼容,原因是泛型只存在代码编译阶段在进入JVM 之前,泛型相关的信息会被擦除掉,我们称之为类型擦除

类型擦除应用场景

需要值得注意的一点是:类型擦除后保留的原始类型

原始类型就是擦除去了泛型信息,最后在字节码中的类型变量的真正类型,无论何时定义一个泛型,相应的原始类型都会被自动提供,类型变量擦除,并使用其限定类型(无限定的变量用Object)替换。

看下面两个例子理解一下:

案例:原始类型被Object替换

代码如下(不能运行,就是看一下这个特性)

class Pair<T> 
    //原始类型就是Object
    private T value;

    public T getValue() 
        return value;
    

    public void setValue(T value) 
        this.value = value;
    

    public static void main(String[] args) 
        Pair pair = new Pair();
        pair.setValue();//调用setter方法能看到参数类型是 Object:setValue(Object) ctrl+鼠标放到setValue()中能看到setValue(Object)
    

因为在Pair中,T是一个无限定的类型变量,所以用Object代替,其结果就是一个普通的类,如同泛型加入Java之前就已经实现了。在程序中可以包含不同类型的Pair, 如Pair擦除类型后他们就成了原始的Pair 类型,原始类型都是Object。

案例:原始类型被限定类型替换

我们可以用extends设置上届

class Pair<T extends Comparator> 
    //原始类型就是Comparator
    private T value;

    public T getValue() 
        return value;
    

    public void setValue(T value) 
        this.value = value;
    

    public static void main(String[] args) 
        Pair pair = new Pair();
        pair.setValue();//调用setter方法能看到参数类型是 Comparator:setValue(Comparator)
    

这里相应的原始类型都会被自动提供了,原始类型就用第一个边界的类型替换,也就是Comparator

类型擦除的限制

无法利用同一泛型类的实例区分方法签名

例如下面代码,就会报错:

public class Erasure 
    //虽然泛型实例不同,但是由于类型擦除后是同一字节码(类型擦拭之后,就都是 List了),因此不能 用于区分方法签名
    public void test(List<String> list) 
        System.out.println("String");
    

    public void test(List<Integer> list) 
        System.out.println("Integer");
    

虽然泛型实例不同,但是由于类型擦除后是同一字节码(类型擦拭之后,就都是 List了),因此不能 用于区分方法签名 +

泛型类的静态变量是共享的

例如下面代码:

class StaticTest 
    public static void main(String[] args) 
        System.out.println(GT.var);
        GT<Integer> gti = new GT<>();
        gti.var = 1;
        System.out.println(GT.var);
        GT<String> gts = new GT<>();
        gts.var = 2;//泛型参数不同,但是共享类的静态成员
        System.out.println(GT.var);
    


class GT<T> 
    public static int var = 0;

    public void nothing(T x) 
    

结果如下所示,泛型参数不同,但是共享类的静态成员:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2YvBYisN-1659850950876)(…/AppData/Roaming/Typora/typora-user-images/image-20220807133710734.png)]

类型擦除的特征

  • 所有泛型类的类型参数在编译时都会被擦除,虚拟机运行时中没有泛型,只有普通类和普通方法,从这一点上来说,Java中的泛型从某种程度上是一种语法糖

  • Java泛型不支持基本类型 例如: short int double等

  • 在泛型代码内部,无法获得任何有关泛型参数类型的信息,如果传入的类型参数为T,那么在泛型代码内部你不知道T有什么方法,属性,关于T的一切信息都丢失了

  • 创建泛型对象时请指明类型,让编译器尽早的做参数检查

  • 不要忽略编译器的警告信息,那意味着潜在的ClassCastException等着你

  • Java的泛型类型不能用于new构建对象(也不能用于初始化数组).泛型不能用于显性地引用运行时类型的操作之中,例如转型,instanceof和new操作(包括new一个对象,new一个数组),因为所有关于参数的类型信息都在运行时丢失了,所以任何在运行时需要获取类型信息的操作都无法进行工作。

    例如下面测试,系统不知道T的信息,他根本不能初始化[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dbDMda04-1659850950877)(…/AppData/Roaming/Typora/typora-user-images/image-20220807134046789.png)]

以上是关于Java泛型:类型擦除的主要内容,如果未能解决你的问题,请参考以下文章

java泛型泛型的内部原理:类型擦除以及类型擦除带来的问题

java泛型 泛型的内部原理:类型擦除以及类型擦除带来的问题

java泛型类型擦除通配符

泛型的内部原理:类型擦除以及类型擦除带来的问题

关于Java泛型的类型擦除机制问题求教

27.Android架构-泛型擦除机制