确认泛型擦除方法

确认泛型擦除方法

简单来说,泛型擦除就是编译器在编译的时候,把泛型代码转换为普通的Java字节码,即字节码中没有泛型。

如何证明编译器进行了泛型擦除呢?

  1. 既然是进行了擦除,那么泛型的类型应该是Object或者是泛型类型的上界。
  2. 直接查看字节码文件

准备泛型类

准备一个泛型类,有两个私有变量,并通过构造函数初始化。

class GenericErasureClass<T, K extends String> {
    private T t;
    private K k;

    public GenericErasureClass(T t, K k){
        this.t = t;
        this.k = k;
    }
}

方法一,在运行时获取泛型的类型

在客户端代码中创建泛型类的实例对象,然后通过反射的方式查看对象的私有变量的类型。

public class GenericErasureExample {
    public static void main(String[] args) {
        GenericErasureClass<String, String> genericErasureClass = new GenericErasureClass<>("t", "k");
        Field[] declaredFields = genericErasureClass.getClass().getDeclaredFields();
        for (Field field : declaredFields){
            System.out.println(field.getGenericType() + ":" + field.getName() + ":" +field.getType());
        }
    }
}

输出结果如下:

T:t:class java.lang.Object
K:k:class java.lang.String

从输出结果看泛型类型T,被擦除了,变成了Object类型;泛型类型K,由于设置泛型上界类型String,所以也是被擦除了。

方法二,通过字节码文件查看

以下使用javap命令查看字节码

javap -c -p -s GenericErasureClass.class
Compiled from "GenericErasureExample.java"
class com.foo.GenericErasureClass<T, K extends java.lang.String> {
  private T t;
    descriptor: Ljava/lang/Object;

  private K k;
    descriptor: Ljava/lang/String;

  public com.foo.GenericErasureClass(T, K);
    descriptor: (Ljava/lang/Object;Ljava/lang/String;)V
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: aload_0
       5: aload_1
       6: putfield      #2                  // Field t:Ljava/lang/Object;
       9: aload_0
      10: aload_2
      11: putfield      #3                  // Field k:Ljava/lang/String;
      14: return
}

泛型参数被擦除,字段t 的描述符是 Ljava/lang/Object,表示在字节码层面,它的类型被看作是 Object,这是泛型T 的默认擦除类型。字段k 的描述符是 Ljava/lang/String,它的类型被明确为 String,这与泛型类型定义 K extends java.lang.String 的上界是一致的。

公共构造函数的描述符也是同理,Ljava/lang/Object;Ljava/lang/String;

从以上的分析中,可以明显的看到Java泛型的擦除效果。

但是,私有变量t、k的类型还有泛型参数T、K是怎么回事呢?请参考:泛型擦除后仍然保留的泛型信息

总结

尽管在源代码级别我们使用了泛型,但在编译后的字节码中,这些泛型信息已经被擦除,并被替换为它们的界限或默认类型。

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注