2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > 【Java 泛型】使用上下边界通配符解决泛型擦除问题

【Java 泛型】使用上下边界通配符解决泛型擦除问题

时间:2021-04-07 02:34:48

相关推荐

【Java 泛型】使用上下边界通配符解决泛型擦除问题

文章目录

前言一、使用上边界通配符示例二、分析字节码的附加信息

前言

上一篇博客 【Java 泛型】泛型用法 ( 泛型编译期擦除 | 上界通配符 <? extends T> | 下界通配符 <? super T> ) 一、泛型擦除 章节中 , 讲到了泛型擦除问题 , 泛型只保留到了编译阶段 , 运行时就没有泛型的限制了 ;

本篇博客中介绍一种方法 , 使用上下边界通配符解决泛型擦除问题 ;

一、使用上边界通配符示例

接口类 :

public interface Data <T>{void set(T t);T get();}

实现类 :

public class DataImpl<T extends Person> implements Data<T>{private T t;@Overridepublic void set(T t) {}@Overridepublic T get() {return null;}}

反编译查看 实现类的 字节码的信息 :发现分别有 222 个 get 和 set 方法 ;

使用

javap -p DataImpl.class

命令 , 反编译 DataImpl.class 字节码文件 , 查看类中的主要方法 ;

D:\002_Project\004_Java_Learn\Main\out\production\Main>javap -p DataImpl.classCompiled from "DataImpl.java"public class DataImpl<T extends Person> implements Data<T> {private T t;public DataImpl();public void set(T);public T get();public java.lang.Object get();public void set(java.lang.Object);}

下面的 222 个方法 , 明显不符合 Java 语法规范 , 方法名和参数一样 ;

public T get();public java.lang.Object get();

二、分析字节码的附加信息

下面分析字节码详细信息 ;

使用

javap -v DataImpl.class

命令 , 查看详细的字节码附加信息 ;

D:\002_Project\004_Java_Learn\Main\out\production\Main>javap -v DataImpl.classClassfile /D:/002_Project/004_Java_Learn/Main/out/production/Main/DataImpl.classLast modified -9-7; size 907 bytesMD5 checksum 90421d2a83f40d38de81c4c7f3cf341bCompiled from "DataImpl.java"public class DataImpl<T extends Person> extends java.lang.Object implements Data<T>minor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPERConstant pool:#1 = Methodref#6.#32 // java/lang/Object."<init>":()V#2 = Methodref#5.#33 // DataImpl.get:()LPerson;#3 = Class #34 // Person#4 = Methodref#5.#35 // DataImpl.set:(LPerson;)V#5 = Class #36 // DataImpl#6 = Class #37 // java/lang/Object#7 = Class #38 // Data#8 = Utf8t#9 = Utf8LPerson;#10 = Utf8Signature#11 = Utf8TT;#12 = Utf8<init>#13 = Utf8()V#14 = Utf8Code#15 = Utf8LineNumberTable#16 = Utf8LocalVariableTable#17 = Utf8this#18 = Utf8LDataImpl;#19 = Utf8LocalVariableTypeTable#20 = Utf8LDataImpl<TT;>;#21 = Utf8set#22 = Utf8(LPerson;)V#23 = Utf8(TT;)V#24 = Utf8get#25 = Utf8()LPerson;#26 = Utf8()TT;#27 = Utf8()Ljava/lang/Object;#28 = Utf8(Ljava/lang/Object;)V#29 = Utf8<T:LPerson;>Ljava/lang/Object;LData<TT;>;#30 = Utf8SourceFile#31 = Utf8DataImpl.java#32 = NameAndType #12:#13 // "<init>":()V#33 = NameAndType #24:#25 // get:()LPerson;#34 = Utf8Person#35 = NameAndType #21:#22 // set:(LPerson;)V#36 = Utf8DataImpl#37 = Utf8java/lang/Object#38 = Utf8Data{public DataImpl();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 1: 0LocalVariableTable:Start Length Slot Name Signature0 50 this LDataImpl;LocalVariableTypeTable:Start Length Slot Name Signature0 50 this LDataImpl<TT;>;public void set(T);descriptor: (LPerson;)Vflags: ACC_PUBLICCode:stack=0, locals=2, args_size=20: returnLineNumberTable:line 7: 0LocalVariableTable:Start Length Slot Name Signature0 10 this LDataImpl;0 11t LPerson;LocalVariableTypeTable:Start Length Slot Name Signature0 10 this LDataImpl<TT;>;0 11t TT;Signature: #23// (TT;)Vpublic T get();descriptor: ()LPerson;flags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aconst_null1: areturnLineNumberTable:line 11: 0LocalVariableTable:Start Length Slot Name Signature0 20 this LDataImpl;LocalVariableTypeTable:Start Length Slot Name Signature0 20 this LDataImpl<TT;>;Signature: #26// ()TT;public java.lang.Object get();descriptor: ()Ljava/lang/Object;flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETICCode:stack=1, locals=1, args_size=10: aload_01: invokevirtual #2 // Method get:()LPerson;4: areturnLineNumberTable:line 1: 0LocalVariableTable:Start Length Slot Name Signature0 50 this LDataImpl;LocalVariableTypeTable:Start Length Slot Name Signature0 50 this LDataImpl<TT;>;public void set(java.lang.Object);descriptor: (Ljava/lang/Object;)Vflags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETICCode:stack=2, locals=2, args_size=20: aload_01: aload_12: checkcast#3 // class Person5: invokevirtual #4 // Method set:(LPerson;)V8: returnLineNumberTable:line 1: 0LocalVariableTable:Start Length Slot Name Signature0 90 this LDataImpl;LocalVariableTypeTable:Start Length Slot Name Signature0 90 this LDataImpl<TT;>;}Signature: #29// <T:LPerson;>Ljava/lang/Object;LData<TT;>;SourceFile: "DataImpl.java"

主要分析 下面 222 个方法的详细字节码数据 ;

public void set(T);public void set(java.lang.Object);

public void set(T)方法的字节码详细数据如下 :

public void set(T);descriptor: (LPerson;)Vflags: ACC_PUBLICCode:stack=0, locals=2, args_size=20: returnLineNumberTable:line 7: 0LocalVariableTable:Start Length Slot Name Signature0 10 this LDataImpl;0 11t LPerson;LocalVariableTypeTable:Start Length Slot Name Signature0 10 this LDataImpl<TT;>;0 11t TT;Signature: #23// (TT;)V

public void set(java.lang.Object)的字节码详细数据如下 :该方法是桥接方法 ;

public void set(java.lang.Object);descriptor: (Ljava/lang/Object;)Vflags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETICCode:stack=2, locals=2, args_size=20: aload_01: aload_12: checkcast#3 // class Person5: invokevirtual #4 // Method set:(LPerson;)V8: returnLineNumberTable:line 1: 0LocalVariableTable:Start Length Slot Name Signature0 90 this LDataImpl;LocalVariableTypeTable:Start Length Slot Name Signature0 90 this LDataImpl<TT;>;

分析public void set(java.lang.Object)方法 :

该方法传入 Object 类型 , 所有的类都是 Object 子类 ;

descriptor: (Ljava/lang/Object;)V说明该方法的参数是Ljava/lang/Object;类型 , 返回值是void类型 ;

ACC_BRIDGE标识 标明 该该方法是一个桥接方法 ;

0: aload_0从局部变量 0 装载引用类型值到操作数栈 ;

1: aload_1从局部变量 1 装载引用类型值到操作数栈 ;

2: checkcast #3检查该值是否是常量值#3的引用 , 也就是检查参数中传入的 Object 参数是否是 Person 类型 ;

Constant pool:#3 = Class #34 // Person

5: invokevirtual #4如果上一步检查 , 传入的参数是 Person 类型 , 就调用常量池中的#4常量对应的方法 , 也就是实际的public void set(T)方法 ;

Constant pool:#4 = Methodref#5.#35 // DataImpl.set:(LPerson;)V

通过 上下边界 通配符 解决 泛型擦除问题 ;

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。