java(程序员眼中的类):
import java.sql.Timestamp;
import java.util.Date;
public class TestGenerics <T extends Date> {
T t;
public T getT() {
return t;
}
public static void main(String[] args) {
}
public void test() {
final TestGenerics<Timestamp> dateSub = new TestGenerics<>();
final Timestamp t = dateSub.getT();
}
}
ByteCode(虚拟机眼中的类):
// class version 52.0 (52)
// access flags 0x21
// signature <T:Ljava/util/Date;>Ljava/lang/Object;
// declaration: TestGenerics<T extends java.util.Date>
public class TestGenerics {
// compiled from: TestGenerics.java
// access flags 0x0
// signature TT;
// declaration: t extends T
Ljava/util/Date; t
// access flags 0x1
public <init>()V
L0
LINENUMBER 4 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this LTestGenerics; L0 L1 0
// signature LTestGenerics<TT;>;
// declaration: this extends TestGenerics<T>
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x1
// signature ()TT;
// declaration: T getT()
public getT()Ljava/util/Date;
L0
LINENUMBER 8 L0
ALOAD 0
GETFIELD TestGenerics.t : Ljava/util/Date;
ARETURN
L1
LOCALVARIABLE this LTestGenerics; L0 L1 0
// signature LTestGenerics<TT;>;
// declaration: this extends TestGenerics<T>
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x9
public static main([Ljava/lang/String;)V
L0
LINENUMBER 11 L0
RETURN
L1
LOCALVARIABLE args [Ljava/lang/String; L0 L1 0
MAXSTACK = 0
MAXLOCALS = 1
// access flags 0x1
public test()V
L0
LINENUMBER 14 L0
NEW TestGenerics
DUP
INVOKESPECIAL TestGenerics.<init> ()V
ASTORE 1
L1
LINENUMBER 15 L1
ALOAD 1
INVOKEVIRTUAL TestGenerics.getT ()Ljava/util/Date;
CHECKCAST java/sql/Timestamp
ASTORE 2
L2
LINENUMBER 16 L2
RETURN
L3
LOCALVARIABLE this LTestGenerics; L0 L3 0
// signature LTestGenerics<TT;>;
// declaration: this extends TestGenerics<T>
LOCALVARIABLE dateSub LTestGenerics; L1 L3 1
// signature LTestGenerics<Ljava/sql/Timestamp;>;
// declaration: dateSub extends TestGenerics<java.sql.Timestamp>
LOCALVARIABLE t Ljava/sql/Timestamp; L2 L3 2
MAXSTACK = 2
MAXLOCALS = 3
}
1.虚拟机眼中没有泛型, 只有一个类TestGenerics (这就是兼容旧版本)
所以java的泛型是伪泛型, 是一个非运行期现象
2. 这里, 编译器把类中用到限定类型的地方做了向上转型, 这里是转为了Date
就是擦除了泛型, 把类结构变成非泛型类也就是虚拟机认识的样子
3. 再看CHECKCAST java/sql/Timestamp, 说明编译器根据传入的具体类型, 再使用到泛型的地方, 加了一条指令, 即检验并强制向左侧类型强转
4. 左侧类型如果乱写呢, 如何检查出来: signature/declaration是编译器记下的左右两侧的信息
// signature LTestGenerics<Ljava/sql/Timestamp;>;
// declaration: dateSub extends
补充
5. 擦除的时候, 不是说向上转型, 比如 T extends A & B …多个
那就擦成A. 此时泛型类定义的那些调用了T.方法的地方, 只要不是A的方法, 也会有强转,调用的是B方法就会强转成B再调用
6. 所以擦除后需要在泛型类中强转(擦除后类没那个方法), 也需要在调用的地方强转(因为方法返回值也被擦了). 前者意味着泛型类