Java中的Erasure类型是矛盾的:编译后的类文件如何确保类型检查?

时间:2013-07-10 19:01:01

标签: java generics

在类型擦除过程中,Java编译器会擦除所有类型参数,如果type参数是有界的,则将每个参数替换为第一个绑定,如果type参数是无界的,则替换为Object。

但是当我们使用编译类引用方法时,编译器会确保在编译时进行类型检查。

有关。例如如果我在A类上使用泛型编译它然后通过类B引用它,在编译期间它将确保类型检查。

如果java在编译时擦除了类型,那么编译后的类文件如何确保类型检查?

2 个答案:

答案 0 :(得分:12)

除了擦除的上限之外,在运行时没有类型检查。 Java Generics完全是关于编译器检查。

另一方面,如果类型参数信息从字节码中消失,那么您的问题可能只是编译器如何进行检查。答案就是它并没有作为一个整体从类文件中消失:它作为元数据附加,可供编译器(以及Reflection API)使用,但与执行代码无关。

答案 1 :(得分:2)

我不同意在运行时没有检查 - 但所有类型检查都是在编译时完成的。

查看生成的实际字节码总是很有趣。我们来看看这段代码

import java.util.*;

class Types{

  public static void main(String [] args){

    List<String> list = new ArrayList<String>();
    list.add("hello");
    System.out.println(list.get(0));

  }

}

编译它,然后使用javap -c对其进行反汇编,我们得到

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/util/ArrayList
       3: dup           
       4: invokespecial #3                  // Method java/util/ArrayList."<init>":()V
       7: astore_1      
       8: aload_1       
       9: ldc           #4                  // String hello
      11: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
      16: pop
      17: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      20: aload_1       
      21: iconst_0      
      22: invokeinterface #7,  2            // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
      27: checkcast     #8                  // class java/lang/String
      30: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      33: return        
}

我们注意到两件事

  • 正如所料,列表是对象列表
  • 在第27行,我们调用checkcast以确保从列表中检索的对象实际上是String。这是编译器插入的运行时检查。在Generics之前手动引入的东西。