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 list1 = new ArrayList();
        List list2 = new ArrayList();
        System.out.println(list1.getClass() == list2.getClass());
    }
}

他的运行结果是true:

Java中泛型——类型擦除_第1张图片

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

类型擦除概念

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

类型擦除应用场景

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

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

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

案例:原始类型被Object替换

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

class  Pair  { 
    //原始类型就是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  { 
    //原始类型就是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

类型擦除的限制

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

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

Java中泛型——类型擦除_第2张图片

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

    public  void  test(List list)  { 
        System.out.println("Integer");
    }
}

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

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

例如下面代码:

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

class GT  { 
    public static int var = 0;

    public void nothing(T x) { 
    }
}

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

Java中泛型——类型擦除_第3张图片

类型擦除的特征

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

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

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

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

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

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

你可能感兴趣的:(jvm,java,开发语言)