首页 文章

java泛型类型擦除

提问于
浏览
3

声明类型参数为 <T extends A & B & C> 的类时,类型擦除过程将使用类型 A 替换 T . 但是如果 T 类型的变量调用在接口 BC 中声明的方法,Java会用它做什么?

当我们创建一个泛型类型的实例,如 ArrayList<String> 时,类型参数 String 也将被删除,但调用 get 方法将返回类型 String ,这些信息来自哪里,因为它已被删除?

我知道使用java反射,但我需要一些具体的解释 .

2 回答

  • 6

    不使用反射 - 铸造是 . 例如,请使用以下代码:

    interface A {
        void a();
    }
    
    interface B {
        void b();
    }
    
    interface C {
        void c();
    }
    
    class Generic<T extends A & B & C> {
        T t;
    
        Generic(T t) {
            this.t = t;
        }
    
        void callMethods() {
            t.a();
            t.b();
            t.c();
        }
    }
    

    现在看一下 Generic 的字节码(删除了构造函数):

    class Generic extends java.lang.Object{
    A t;
    
    void callMethods();
      Code:
       0:   aload_0
       1:   getfield        #2; //Field t:LA;
       4:   invokeinterface #3,  1; //InterfaceMethod A.a:()V
       9:   aload_0
       10:  getfield        #2; //Field t:LA;
       13:  checkcast       #4; //class B
       16:  invokeinterface #5,  1; //InterfaceMethod B.b:()V
       21:  aload_0
       22:  getfield        #2; //Field t:LA;
       25:  checkcast       #6; //class C
       28:  invokeinterface #7,  1; //InterfaceMethod C.c:()V
       33:  return    
    }
    

    在每个invokeinterface调用 b()c() 之前,请注意checkcast指令 .

    结果就像 Generic 实际上是这样编写的:

    class Generic<T extends A> {
        T t;
    
        Generic(T t) {
            this.t = t;
        }
    
        void callMethods() {
            t.a();
            ((B) t).b();
            ((C) t).c();
        }
    }
    

    至于你关于 ArrayList 的问题 - 有关作为列表元素类型的 get() 的返回类型的信息仍然存储为 ArrayList 类的一部分 . 编译器将再次在调用代码中插入强制转换,因此:

    ArrayList<String> strings = new ArrayList<String>();
    strings.add("foo");
    String x = strings.get(0);
    

    在执行时相当于:

    ArrayList strings = new ArrayList();
    strings.add("foo");
    String x = (String) strings.get(0);
    

    这样做的一个重要方面是你不能在执行时询问 ArrayList 对象 T 是什么 - 该信息已被删除 .

  • 2

    在编译器完成类型检查后,类型擦除有效地用 Object 替换所有泛型类型参数 . 在您的 <T extends A & B & C> 示例中, A 就像其他所有内容一样被删除 .

    当您在 ArrayList<String> 上调用 get 时,编译器会生成字节码,将返回的对象自动转换为字符串 . 列表本身并不是一个字符串列表(由于类型擦除),但是调用 get 的代码知道它希望从列表中获取的东西是一个字符串 .

相关问题