`

Java泛型中的类型擦除

阅读更多
Java编译器在编译泛型代码的时候做了类型擦除。对于形参,类型擦除是将泛型转换为该类型的上限类型,如果没有上限,则转换为Object类型。如果有多个上限类型,则转为最左边的上限类型。对于实参,类型擦除是删除了泛型信息。例如:
class X<T extends Callable<Long> & Runnable> { 
  private T task1, task2; 
  ... 
  public void do() {  
    task1.run();  
    Long result = task2.call(); 
  } 
} 

类型擦除后代码为:
class X { 
  private Callable task1, task2; 
  ... 
  public void do() {  
    ( (Runnable) task1).run();  
    Long result = (Long) task2.call(); 
  } 
}


T task1 => Callable task1,形参的类型擦除,取了最左边类型,leftmost bound,泛型转换为上限类型。
class X<T extends Callable<Long> & Runnable> =>class X,实参的类型擦除,在创建X的实例的时候,是没有泛型信息的。

http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html#Topic3
How does the compiler translate Java generics?

By creating one unique byte code representation of each generic type (or method) and mapping all instantiations of the generic type (or method) to this unique representation.
The Java compiler is responsible for translating Java source code that contains definitions and usages of generic types and methods into Java byte code that the virtual machine can interpret. How does that translation work?
A compiler that must translate a generic type or method (in any language, not just Java) has in principle two choices:

Code specialization. The compiler generates a new representation for every instantiation of a generic type or method. For instance, the compiler would generate code for a list of integers and additional, different code for a list of strings, a list of dates, a list of buffers, and so on.

Code sharing. The compiler generates code for only one representation of a generic type or method and maps all the instantiations of the generic type or method to the unique representation, performing type checks and type conversions where needed.
Code specialization is the approach that C++ takes for its templates:
The C++ compiler generates executable code for every instantiation of a template. The downside of code specialization of generic types is its potential for code bloat.  A list of integers and a list of strings would be represented in the executable code as two different types. Note that code bloat is not inevitable in C++ and can generally be avoided by an experienced programmer.

Code specialization is particularly wasteful in cases where the elements in a collection are references (or pointers), because all references (or pointers) are of the same size and internally have the same representation. There is no need for generation of mostly identical code for a list of references to integers and a list of references to strings.  Both lists could internally be represented by a list of references to any type of object. The compiler just has to add a couple of casts whenever these references are passed in and out of the generic type or method. Since in Java most types are reference types, it deems natural that Java chooses code sharing as its technique for translation of generic types and methods.

The Java compiler applies the code sharing technique and creates one unique byte code representation of each generic type (or method).  The various instantiations of the generic type (or method) are mapped onto this unique representation by a technique that is called type erasure .

LINK TO THIS Technicalities.FAQ100
REFERENCES What is type erasure?

What is type erasure?

A process that maps a parameterized type (or method) to its unique byte code representation by eliding type parameters and arguments.
The compiler generates only one byte code representation of a generic type or method and maps all the instantiations of the generic type or method to the unique representation. This mapping is performed by type erasure.  The essence of type erasure is the removal of all information that is related to type parameters and type arguments. In addition, the compiler adds type checks and type conversions where needed and inserts synthetic bridge methods if necessary. It is important to understand type erasure because certain effects related to Java generics are difficult to understand without a proper understanding of the translation process.
The type erasure process can be imagined as a translation from generic Java source code back into regular Java code.  In reality the compiler is more efficient and translates directly to Java byte code.  But the byte code created is equivalent to the non-generic Java code you will be seeing in the subsequent examples.

The steps performed during type erasure include:

Eliding type parameters.
When the compiler finds the definition of a generic type or method, it removes all occurrences of the type parameters and replaces them by their leftmost bound, or type Object if no bound had been specified.

Eliding type arguments.
When the compiler finds a paramterized type, i.e. an instantiation of a generic type, then it removes the type arguments. For instance, the types List<String> , Set<Long> , and Map<String,?> are translated to List , Set and Map respectively.

Example (before type erasure):

interface Comparable <A> {
  public int compareTo( A that);
}
final class NumericValue implements Comparable <NumericValue> {
  priva te byte value; 
  public  NumericValue (byte value) { this.value = value; } 
  public  byte getValue() { return value; } 
  public  int compareTo( NumericValue t hat) { return this.value - that.value; }
}
class Collections { 
  public static <A extends Comparable<A>>A max(Collection <A> xs) {
    Iterator <A> xi = xs.iterator();
    A w = xi.next();
    while (xi.hasNext()) {
      A x = xi.next();
      if (w.compareTo(x) < 0) w = x;
    }
    return w;
  }
}
final class Test {
  public static void main (String[ ] args) {
    LinkedList <NumericValue> numberList = new LinkedList <NumericValue> ();
    numberList .add(new NumericValue((byte)0)); 
    numberList .add(new NumericValue((byte)1)); 
    NumericValue y = Collections.max( numberList ); 
  }
}
Type parameters are green and type arguments are blue .  During type erasure the type arguments are discarded and the type paramters are replaced by their leftmost bound.
Example (after type erasure):

interface Comparable {
  public int compareTo( Object that);
}
final class NumericValue implements Comparable {
  priva te byte value; 
  public  NumericValue (byte value) { this.value = value; } 
  public  byte getValue() { return value; } 
  public  int compareTo( NumericValue t hat)   { return this.value - that.value; }
  public  int compareTo(Object that) { return this.compareTo((NumericValue)that);  }
}
class Collections { 
  public static Comparable max(Collection xs) {
    Iterator xi = xs.iterator();
    Comparable w = (Comparable) xi.next();
    while (xi.hasNext()) {
      Comparable x = (Comparable) xi.next();
      if (w.compareTo(x) < 0) w = x;
    }
    return w;
  }
}
final class Test {
  public static void main (String[ ] args) {
    LinkedList numberList = new LinkedList();
    numberList .add(new NumericValue((byte)0)); 
    numberList .add(new NumericValue((byte)1)); 
    NumericValue y = (NumericValue) Collections.max( numberList ); 
  }
}
The generic Comparable interface is translated to a non-generic interface and the unbounded type parameter A is replaced by type Object .
The NumericValue class implements the non-generic Comparable interface after type erasure, and the compiler adds a so-called bridge method . The bridge method is needed so that class NumericValue remains a class that implements the Comparable interface after type erasure.

The generic method max is translated to a non-generic method and the bounded type parameter A is replaced by its leftmost bound, namely Comparable .  The parameterized interface Iterator<A> is translated to the raw type Iterator and the compiler adds a cast whenever an element is retrieved from the raw type Iterator .

The uses of the parameterized type LinkedList<NumericValue> and the generic max method in the main method are translated to uses of the non-generic type and method and, again, the compiler must add a cast. 
分享到:
评论

相关推荐

    解析Java泛型的类型擦除.pdf

    在 Java 语言中,泛型类型擦除的机制使得开发者难以理解和使用泛型,例如,在 Java 中,我们可以定义一个泛型类 `ArrayList&lt;T&gt;`,其中 `T` 是类型参数,但是,在编译后的字节码文件中,泛型类型信息已经被擦除,所有...

    Java泛型类型擦除后的补偿

    本文将深入探讨Java泛型类型擦除的概念,并介绍在类型擦除后,为了保持泛型的安全性和便利性,Java设计者所采取的一些补偿机制。 1. **类型擦除**: - 在编译期间,所有的泛型类型信息都会被替换为它们的实际类型...

    Java 泛型擦除后的三种补救方法

    然而,Java 的泛型在运行时是被擦除的,这意味着在运行时刻,所有的泛型类型信息都会丢失,无法直接用来创建对象或进行类型检查。这导致我们无法使用 `new T()` 或 `instanceof` 这样的操作。为了解决这个问题,我们...

    java 泛型类的类型识别示例

    综上所述,虽然Java泛型在编译后会进行类型擦除,但通过上述技巧,我们仍然能够在运行时获得关于泛型类实例化类型的一些信息。在实际开发中,这些方法可以帮助我们编写更加灵活和安全的代码。在示例文件`GenericRTTI...

    很好的Java泛型的总结

    Java泛型是Java语言中的一种机制,用于在编译期检查类型安全。Java泛型的出现解决了Java早期版本中类型安全检查的缺陷。Java泛型的好处是可以在编译期检查类型安全,避免了运行时的ClassCastException。 Java泛型的...

    Java 泛型总结(一):基本用法与类型擦除

    Java 中的泛型使用了类型擦除机制,所谓类型擦除是指编译器在编译泛型代码时,将泛型信息擦除,编译成普通的 Java 代码。这种机制使得泛型代码可以在 Java 1.4 及更低版本中运行。 例如,编译器将上面的 Holder 类...

    Java泛型与类型擦除

    这个过程由“类型擦除”实现。但是并非像许多开发者认为的那样,在 &lt;..&gt; 符号内的东西都被擦除了。看下面这段代码: public class ClassTest { public static void main(String[] args) throws Exception { ...

    Java泛型应用实例

    Java泛型是Java编程语言中的一个强大特性,它允许我们在定义类、接口和方法时指定类型参数,从而实现代码的重用和类型安全。在Java泛型应用实例中,我们可以看到泛型如何帮助我们提高代码的灵活性和效率,减少运行时...

    java泛型技术之发展

    - **类型擦除**:Java泛型采用了类型擦除的策略,这意味着在编译期间,泛型信息会被删除,只留下原始的无参数类型,这解决了与Java的向下兼容问题。 - **JDK 5.0引入**:Java 5.0正式引入泛型,使得在编译时期就能...

    关于C#、java泛型的看法

    另一方面,Java的泛型类型擦除带来了一定的灵活性,使得旧的无泛型代码能够与新的泛型代码兼容,这对于维护大型遗留项目尤其有利。然而,这也意味着Java开发者需要额外注意类型转换的安全性,避免出现...

    java泛型的内部原理及更深应用

    1. **类型擦除**:Java泛型的主要特点是类型擦除。这意味着在编译完成后,所有的泛型信息都会被擦除,替换为Object或者其他基础类型。因此,泛型在运行时并不存在,所有关于泛型的操作都在编译期间完成。 2. **边界...

    java 泛型接口示例

    Java泛型在编译后会进行类型擦除,也就是说,所有的泛型信息在运行时都会消失。因此,虽然在编译期间我们能获得类型检查的好处,但在运行时,泛型接口和类的行为与无参数类型版本基本相同。 5. **通配符** 在某些...

    java 泛型方法使用示例

    下面我们将深入探讨Java泛型方法的概念、语法以及使用示例。 **一、泛型方法概念** 泛型方法是一种具有类型参数的方法,这些类型参数可以在方法声明时指定,并在方法体内部使用。与类的泛型类似,它们提供了编译时...

    JVM如何理解Java泛型类.doc

    尽管如此,由于JVM本身的限制,所有的泛型信息在编译后都会被擦除,因此理解类型擦除的概念对于正确使用Java泛型至关重要。开发者应该注意在创建泛型对象时明确指定类型参数,并理解编译时与运行时的区别,以避免...

    关于java基础的泛型的练习

    通过以上知识点,我们可以看到Java泛型在编程中的重要性和灵活性。理解和掌握这些概念对于编写高效、安全的Java代码至关重要。在实际开发中,熟练运用泛型可以显著提高代码质量,减少潜在的错误,并提高代码的可维护...

    Java-Edge#Java-Interview-Tutorial#Java语法糖之泛型与类型擦除1

    - 泛型擦除前的例子把这段Java代码编译成Class文件,然后再用字节码反编译后,將会发现泛型都不见了,又变回了Java泛型出现之前的写法,泛型类型都变回了原

    SUN公司Java泛型编程文档

    4. **类型擦除**:Java泛型的一个重要特点是类型擦除。在编译后,所有的类型参数都会被替换为它们的边界或者Object,这意味着在运行时,泛型对象与非泛型对象并无实质区别,但编译期的类型检查仍然有效。 5. **创建...

Global site tag (gtag.js) - Google Analytics