0
点赞
收藏
分享

微信扫一扫

Java泛型擦除与兼容

落拓尘嚣 2022-04-27 阅读 80
java

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. 所以擦除后需要在泛型类中强转(擦除后类没那个方法), 也需要在调用的地方强转(因为方法返回值也被擦了). 前者意味着泛型类

举报

相关推荐

0 条评论