字节码分析-泛型
源码
package java.bytecode;
/**
* 泛型示例 - 演示泛型类、泛型方法
*/
public class GenericExample<T extends Number> {
private T genericField;
public GenericExample(T value) {
this.genericField = value;
}
// 泛型方法
public <E> E genericMethod(E param) {
return param;
}
// 泛型方法 - 多个类型参数
public <K, V> void genericMethod2(K key, V value) {
System.out.println("Key: " + key + ", Value: " + value);
}
public T getGenericField() {
return genericField;
}
public void setGenericField(T genericField) {
this.genericField = genericField;
}
}
结构化反解析
Classfile /D:/zcp/github/yggdrasil/java/bytecode/GenericExample.class
Last modified 2025-12-3; size 1271 bytes
MD5 checksum 491fa07e2f426ab948e734dbb313c1c8
Compiled from "GenericExample.java"
public class java.bytecode.GenericExample<T extends java.lang.Number> extends java.lang.Object
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
...
{
private T genericField;
descriptor: Ljava/lang/Number;
flags: ACC_PRIVATE
Signature: #44 // TT;
public java.bytecode.GenericExample(T);
descriptor: (Ljava/lang/Number;)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: putfield #7 // Field genericField:Ljava/lang/Number;
9: return
Signature: #48 // (TT;)V
public <E extends java.lang.Object> E genericMethod(E);
descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
flags: ACC_PUBLIC
Code:
stack=1, locals=2, args_size=2
0: aload_1
1: areturn
Signature: #51 // <E:Ljava/lang/Object;>(TE;)TE;
public <K extends java.lang.Object, V extends java.lang.Object> void genericMethod2(K, V);
descriptor: (Ljava/lang/Object;Ljava/lang/Object;)V
flags: ACC_PUBLIC
Code:
...
Signature: #54 // <K:Ljava/lang/Object;V:Ljava/lang/Object;>(TK;TV;)V
public T getGenericField();
descriptor: ()Ljava/lang/Number;
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #7 // Field genericField:Ljava/lang/Number;
4: areturn
Signature: #57 // ()TT;
public void setGenericField(T);
descriptor: (Ljava/lang/Number;)V
flags: ACC_PUBLIC
Code:
...
Signature: #48 // (TT;)V
}
Signature: #59 // <T:Ljava/lang/Number;>Ljava/lang/Object;
SourceFile: "GenericExample.java"
详细说明
Signature属性
泛型信息存储在Signature属性中,而不是方法描述符中:
- 类签名:
<T:Ljava/lang/Number;>Ljava/lang/Object; - 字段签名:
TT;(表示类型参数T) - 方法签名:
<E:Ljava/lang/Object;>(TE;)TE;(泛型方法)
类型擦除
泛型在运行时被擦除,只保留原始类型(Raw Type):
T extends Number→NumberE→ObjectK, V→Object
方法描述符
方法描述符使用擦除后的类型:
public GenericExample(T);
descriptor: (Ljava/lang/Number;)V // T被擦除为Number
public <E> E genericMethod(E);
descriptor: (Ljava/lang/Object;)Ljava/lang/Object; // E被擦除为Object
类型检查
泛型的类型检查在编译时完成,运行时通过类型转换保证类型安全:
14: checkcast #18 // class java/lang/Integer
桥接方法
编译器可能生成桥接方法以保持类型安全,但在本例中未生成。
泛型的作用
- 编译时类型检查:确保类型安全
- 代码复用:避免类型转换
- 运行时擦除:保持向后兼容性
注意事项
- 泛型信息在运行时不可用(通过反射可以获取Signature属性)
- 类型擦除可能导致一些限制(如不能创建泛型数组)
- 需要使用通配符和边界来增强类型系统的表达能力