深入探索Java泛型的本质原创
1年前
327445
为什么泛型擦除后仍可以获取类型信息,如何获取泛型类型,Java泛型与C++、Python中的有何区别,本文将为您揭开泛型的内幕。读完该篇文章,您可以了解到1.为什么需要泛型
2.Java代码在编译后是如何保存泛型信息的
3.Java泛型与C++、Python中的有何区别
4.如何动态获取泛型类型
1、Java为什么需要泛型?
2、泛型
-
泛型类:参数类型用于类中;
-
泛型接口:参数类型用于接口中;
-
泛型方法:参数类型用于方法中。
-
Java笔记 – 泛型 泛型方法 泛型接口 擦除 边界 通配符(1)
https://www.itzhai.com/java-bi-ji-fan-xing-fan-xing-fang-fa-fan-xing-jie-kou-ca-chu-bian-jie-tong-pei-fu.html -
Java笔记 – 泛型 泛型方法 泛型接口 擦除 边界 通配符(2)
https://www.itzhai.com/generic-method-interface-border-wildcard-2.html -
Java基础笔记 - Java中的泛型使用详细介绍
https://www.itzhai.com/java-based-notebook-java-generics-use-the-detailed.html
3、探索Java泛型的本质
Signature
、 LocalVariableTypeTable
新属性。3.1、LocalVariableTypeTable
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
private GT1 param1; public GT1 getParam1() { return param1; } public void setParam1(GT1 param1) { this.param1 = param1; } } class GenericClassTest extends GenericClass<Double> { @Override public Double getParam1() { System.out.println(super.getParam1()); return super.getParam1(); } } |
javap -v
命令查看 GenericClass<GT1>
的反汇编信息,我们在实例构造器中的code中,发现有LocalVariableTypeTable:LocalVariableTypeTable
的介绍: 4.7.14. The LocalVariableTypeTable
Attributehttps://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.14
LocalVariableTypeTable
。3.2、Signature
LocalVariableTypeTable
里面携带了一个具有类型的Signature签名。对比可以发现,LocalVariableTable中也有Signature,不过LocalVariableTable中并不携带泛型信息。
这样,即使在编译后,泛型的类型信息被擦除了,也能通过这个Signature获取到泛型的签名信息。
-
具有通用或者具有参数化类型的超类或者超接口的类;
-
方法中的通用或者参数化类型的返回值或者入参,以及方法的throw子句中的类型变量;
-
任何类型、类型变量、或者参数化类型的字段、形式参数或者局部变量;
Lcom/itzhai/jvm/executeengine/编译期优化/泛型/GenericClass<TGT1;>;
TGT1
3.2.1、Signature是怎么存储的
class
file attributes (by location)表中,我们可以知道,Signature属性在ClassFile,field_info,method_info都可以存在,用于代表不同主体的类型签名信息。我们上面看到的是在method_info中的Signature。我们在上图标注一下,哪些地方会存储这个Signature:3.3、擦除了泛型之后
GenericClass<GT1>
类的 getParam1()
和 setParam1()
方法编译完成后,得到的反汇编代码如下:
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
descriptor: ()Ljava/lang/Object; flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: getfield #2 // Field param1:Ljava/lang/Object; 4: areturn LineNumberTable: line 15: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/itzhai/jvm/executeengine/编译期优化/泛型/GenericClass; LocalVariableTypeTable: Start Length Slot Name Signature 0 5 0 this Lcom/itzhai/jvm/executeengine/编译期优化/泛型/GenericClass<TGT1;>; Signature: #20 // ()TGT1; public void setParam1(GT1); descriptor: (Ljava/lang/Object;)V flags: ACC_PUBLIC Code: stack=2, locals=2, args_size=2 0: aload_0 1: aload_1 2: putfield #2 // Field param1:Ljava/lang/Object; 5: return LineNumberTable: line 19: 0 line 20: 5 LocalVariableTable: Start Length Slot Name Signature 0 6 0 this Lcom/itzhai/jvm/executeengine/编译期优化/泛型/GenericClass; 0 6 1 param1 Ljava/lang/Object; LocalVariableTypeTable: Start Length Slot Name Signature 0 6 0 this Lcom/itzhai/jvm/executeengine/编译期优化/泛型/GenericClass<TGT1;>; 0 6 1 param1 TGT1; Signature: #23 // (TGT1;)V |
3.4、通过反射从Signature中获取泛型信息
3.4.1、获取泛型信息例子
2 |
System.out.println(Arrays.toString(temp.getClass().getTypeParameters())); |
|
|
GenericClass
Class文件里面只有
|
|
2 3 4 5 6 7 8 9 |
Class clazz = genericClass.getClass(); // getGenericSuperclass()获得带有泛型的父类 // Type是Java中所有类型的公共高级接口,包括原始类型、参数化类型、数组类型、类型变量和基本类型。 Type type = clazz.getGenericSuperclass(); ParameterizedType p = (ParameterizedType)type; // getActualTypeArguments获取参数化类型的数组,泛型可能有多个 Class c = (Class) p.getActualTypeArguments()[0]; System.out.println(c); |
|
|
GenericClassTest
的ClassFile的attributes中包含了泛型的签名信息:
2 |
SourceFile: "GenericClass.java" |
GenericClassTest
类生成的。4、Java泛型与C++泛型有什么区别?
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
using namespace std; template<class T> class Manipulator { T obj; public: Manipulator(T x) { obj = x; } void manipulate() { obj.f(); } }; class HasF { public: void f() { cout << "HasF::f()" << endl; } }; int main() { HasF hf; Manipulator<HasF> manipulator(hf); manipulator.manipulate(); } /* Output: HasF::f() |
2 3 4 5 6 7 8 9 10 |
private T obj; public Manipulator(T x){ obj = x; } public void doSomething(){ obj.f(); // 编译错误 } } |
2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
private T obj; public Manipulator(T x){ obj = x; } public void doSomething(){ obj.f(); // 编译错误 } } class HasF{ public void f(){ System.out.println("HasF.f();"); } } |
5、Java泛型的弊端与改进思路
潜在潜在机制
或 结构化类型机制
(鸭子类型机制:如果它走起来像鸭子,并且叫起来也像鸭子,那么你就可以将它当做鸭子对待。)潜在类型机制
使得你可以横跨类继承结构,调用不属于某个公共接口的方法。因此,实际上一段代码可以声明:“我不关心你是什么类型,只要你可以speak()和sit()即可。”由于不要求具体类型,因此代码就可以更加泛化了。两种支持潜在类型机制的语言:Python和C++。
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
def speak(self): print "Arf!" def sit(self): print "Sitting" def repoduce(self) pass class Robot: def speak(self): print "Click!" def sit(self): print "Clank!" def repoduce(self) pass def perform(anything): anything.spead() anything.sit() |
6、Java中对缺乏类型机制的补偿
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
public void walkAgainstTheWind() {} public void sit() { System.out.println("Pretending to sit"); } public void pushInvisibleWalls() {} public String toString() { return "Mime"; } } class SmartDog { public void speak() { System.out.println("Woof!"); } public void sit() { System.out.println("Sitting"); } public void reproduce() {} } class CommunicateReflectively { public static void perform(Object speaker) { Class<?> spkr = speaker.getClass(); try { try { Method speak = spkr.getMethod("speak"); speak.invoke(speaker); } catch(NoSuchMethodException e) { System.out.println(speaker + " cannot speak"); } try { Method sit = spkr.getMethod("sit"); sit.invoke(speaker); } catch(NoSuchMethodException e) { System.out.println(speaker + " cannot sit"); } } catch(Exception e) { throw new RuntimeException(speaker.toString(), e); } } } public class Chapter15_17_1 { public static void main(String[] args) { CommunicateReflectively.perform(new SmartDog()); CommunicateReflectively.perform(new Robot()); CommunicateReflectively.perform(new Mime()); } } /* Output: Woof! Sitting Click! Clank! Mime cannot speak Pretending to sit *///:~ |
References
https://www.itzhai.com/java-bi-ji-fan-xing-fan-xing-fang-fa-fan-xing-jie-kou-ca-chu-bian-jie-tong-pei-fu.html
https://www.itzhai.com/generic-method-interface-border-wildcard-2.html?sdf
https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
欢迎关注微信公众号《Java架构杂谈》。
点赞收藏
分类: