Java 泛型擦除的理解及如何获取泛型的实际类型

泛型

泛型是JDK1.5之后的一项新增特性, 它的本质是参数化类型(Parametersized Type)的应用,
即所操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法的创建中,分别为泛型类、泛型接口和泛型方法。
Java中的泛型只在程序源代码中存在, 在编译后的字节码文件中就已经替换为原来的原生类型(Raw Type), 并在相应的地方插入强制类型装换代码。
对于运行期的Java语言来说,ArrayList<int> 与 ArrayList<String> 就是同一个类,泛型技术实际上就是Java语言的一颗语法糖,Java语言中的泛型实现方法称为类型擦除,基于这种方法的实现的泛型称为伪泛型。

java 字节码层面

源代码

public class MyListTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("test");
        String list1 = list.get(0);
        System.out.println(list1);
        System.out.println(list);
    }
}

编译命令

javac -encoding UTF-8 MyListTest.java

编译为class文件

public class MyListTest {
    public MyListTest() {
    }

    public static void main(String[] var0) {
        ArrayList var1 = new ArrayList();
        var1.add("test");
        String var2 = (String)var1.get(0); // 这里插入强制类型转换
        System.out.println(var2);
        System.out.println(var1);
    }
}

javap查看字节码

javap -c MyListTest
警告: 二进制文件MyListTest包含com.james.example.MyListTest
Compiled from "MyListTest.java"
public class com.james.example.MyListTest {
  public com.james.example.MyListTest();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup
       4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #4                  // String test
      11: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      16: pop
      17: aload_1
      18: iconst_0
      19: invokeinterface #6,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;  这里获取的是Object类型
      24: checkcast     #7                  // class java/lang/String 类型转换指令
      27: astore_2
      28: getstatic     #8                  // Field java/lang/System.out:Ljava/io/PrintStream;
      31: aload_2
      32: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      35: getstatic     #8                  /

Java中获取泛型的类型信息

Java泛型的引入,各种场景(虚拟机解析, 反射等)下的方法调用都可能对原有的基础产生影响和新需求, 如在泛型类中如何获取传入的参数化类型等。基于此,JCP组织对虚拟机规范做出了相应的修改,引入了诸如Signature, LocalVariableType Table 等新属性解决伴随泛型而来的参数化类型的识别问题。
Signature 是其中最重要的一项属性,作用是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型不是原生类型,而是包括了参数化类型的信息。

<>: 念做typeof
List<E>: E称为类型参数变量
List<Integer>: Integer称为实际类型参数
List<Integer>: 整个List<Integer>称为参数化类型(对应着java.lang.reflect.ParameterizedType接口)

实例化子类中获取泛型

定义一个MyType

public class MyType<T> {
    public Class<T> getTClass() {
        return (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }
}

测试

        MyType<String> myType = new MyType<String>(){}; //注意这里是实例化的
        // 在类的外部获取
        Type actualTypeArguments = ((ParameterizedType) myType.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
        Type rawType = ((ParameterizedType) myType.getClass().getGenericSuperclass()).getRawType();
        System.out.println(actualTypeArguments);
        System.out.println(rawType);
        System.out.println(myType.getTClass());

测试结果

class java.lang.String
class com.james.example.type.MyType
class java.lang.String

未实例化子类无法获取泛型

测试

MyType<String> myType = new MyType<>(); //注意这里是未实例化的
Type actualTypeArguments = ((ParameterizedType) myType.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
Type rawType = ((ParameterizedType) myType.getClass().getGenericSuperclass()).getRawType();
System.out.println(actualTypeArguments);
System.out.println(rawType);
System.out.println(myType.getTClass());

结果

Exception in thread "main" java.lang.ClassCastException: class java.lang.Class cannot be cast to class java.lang.reflect.ParameterizedType (java.lang.Class and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')
    at com.james.example.type.MyListTest.main(MyListTest.java:23)

List测试

ArrayList<String> list3 = new ArrayList<String>(){}; 
System.out.println(((ParameterizedType)list3.getClass().getGenericSuperclass()).getRawType());
System.out.println(((ParameterizedType)list3.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
        
ArrayList<String> list6 = new ArrayList<>();// 这里new ArrayList<String>() 结果也是同new ArrayList<>()
System.out.println(((ParameterizedType)list6.getClass().getGenericSuperclass()).getRawType());
System.out.println(((ParameterizedType)list6.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
class java.util.ArrayList
class java.lang.String
class java.util.AbstractList
E

总结

getGenericInterfaces()
getGenericSuperclass()
import java.lang.reflect.ParameterizedType; getActualTypeArguments()
import java.lang.reflect.Type;

Signature属性的出现,Java泛型擦除法所谓的擦除,只是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们能通过反射手段获取参数化类型的根本依据。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容