java 泛型-擦除erasure

    类型擦除(type erasure)。 Java中的泛型基本上都是在编译器这个层次来实现的。在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉。这个过程就称为类型擦除。如在代码中定义的List<Object>和List<String>等类型,在编译之后都会变成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法避免在运行时刻出现类型转换异常的情况。类型擦除也是Java的泛型实现方式与C++模板机制实现方式之间的重要区别。

    很多泛型的奇怪特性都与这个类型擦除的存在有关,包括:
    泛型类并没有自己独有的Class类对象。比如并不存在List<String>.class或是List<Integer>.class,而只有List.class。
    静态变量是被泛型类的所有实例所共享的。对于声明为MyClass<T>的类,访问其中的静态变量的方法仍然是 MyClass.myStaticVar。不管是通过new MyClass<String>还是new MyClass<Integer>创建的对象,都是共享一个静态变量。

当泛型遇见重载:


public class GenericTypes { 
  public static void method(List<String> list) { 
  System.out.println("invoke method(List<String> list)"); 
 } 
  public static void method(List<Integer> list) { 
  System.out.println("invoke method(List<Integer> list)"); 
 } 
}

    这段代码是不能被编译的,是因为参数List<Integer>和List<String>编译之后都被擦除了,变成了一样的原生类型List<E>,擦除动作导致这两个方法的特征签名变得一模一样。初步看来,无法重载的原因已经找到了,但是真的就是如此吗?只能说,泛型擦除成相同的原生类型只是无法重载的其中一部分原因。

public class GenericTypes { 
  public static String method(List<String> list) { 
  System.out.println("invoke method(List<String> list)"); 
  return ""; 
} 
  public static int method(List<Integer> list) { 
  System.out.println("invoke method(List<Integer> list)"); 
  return 1; 
} 
 public static void main(String[] args) { 
  method(new ArrayList<String>()); 
  method(new ArrayList<Integer>()); 
} 
}
    两个method方法添加了不同的返回值,由于这两个返回值的加入,方法重载居然成功了,即这段代码可以被编译和执行了。这是我们对Java语言中返回值不参与重载选择的基本认知的挑战吗? 

    重载当然不是根据返回值来确定的,之所以这次能编译和执行成功,是因为两个mehtod()方法加入了不同的返回值后才能共存在一个Class文件之中。Class文件方法表(method_info)的数据结构时曾经提到过,方法重载要求方法具备不同的特征签名,返回值并不包含在方法的特征签名之中,所以返回值不参与重载选择,但是在Class文件格式之中,只要描述符不是完全一致的两个方法就可以共存。也就是说两个方法如果有相同的名称和特征签名,但返回值不同,那它们也是可以合法地共存于一个Class文件中的。



你可能感兴趣的:(java 泛型-擦除erasure)