Section 4.4. How Erasure Works


4.4. How Erasure Works

The erasure of a type is defined as follows: drop all type parameters from parameterized types, and replace any type variable with the erasure of its bound, or with Object if it has no bound, or with the erasure of the leftmost bound if it has multiple bounds. Here are some examples:

  • The erasure of List<Integer>, List<String>, and List<List<String>> is List.

  • The erasure of List<Integer>[] is List[].

  • The erasure of List is itself, similarly for any raw type (see Section 5.3 for an explanation of raw types).

  • The erasure of int is itself, similarly for any primitive type.

  • The erasure of Integer is itself, similarly for any type without type parameters.

  • The erasure of T in the definition of asList(see Section 1.4) is Object, because T has no bound.

  • The erasure of T in the definition of max (see Section 3.2) is Comparable, because T has bound Comparable<? super T>.

  • The erasure of T in the final definition of max (see Section 3.6) is Object, because T has bound Object & Comparable<T> and we take the erasure of the leftmost bound.

  • The erasures of S and T in the definition of copy (see Section 3.6) are Appendable and Readable, because S has bound Appendable & Closeable and T has bound Readable & Closeable.

  • The erasure of LinkedCollection<E>.Node or LinkedCollection.Node<E> (see Section 4.3) is LinkedCollection.Node.

In Java, two distinct methods cannot have the same signature. Since generics are implemented by erasure, it also follows that two distinct methods cannot have signatures with the same erasure. A class cannot overload two methods whose signatures have the same erasure, and a class cannot implement two interfaces that have the same erasure.

For example, here is a class with two convenience methods. One adds together every integer in a list of integers, and the other concatenates together every string in a list of strings:

 class Overloaded {   public static int sum(List<Integer> ints) {     int sum = 0;     for (int i : ints) sum += i;     return sum;   }   public static String sum(List<String> strings) {     StringBuffer sum = new StringBuffer();     for (String s : strings) sum.append(s);     return sum.toString();   } } 

This works as intended:

 assert sum(Arrays.asList(1,2,3)) == 6; assert sum(Arrays.asList("a","b")).equals("ab"); 

Here are the erasures of the signatures of the two methods:

 int sum(List) String sum(List) 

The two methods have different return types, which is sufficient for Java to distinguish them.

However, say we change the methods so that each appends its result to the end of the argument list rather than returning a value:

 class Overloaded2 {   // compile-time error, cannot overload two methods with same erasure      public static boolean allZero(List<Integer> ints) {     for (int i : ints) if (i != 0) return false;     return true;   }   public static boolean allZero(List<String> strings) {     for (String s : strings) if (s.length() != 0) return false;     return true;   } } 

We intend this code to work as follows:

 assert allZero(Arrays.asList(0,0,0)); assert allZero(Arrays.asList("","","")); 

However, in this case the erasures of the signatures of both methods are identical:

 boolean allZero(List) 

Therefore, a name clash is reported at compile time. It is not possible to give both methods the same name and try to distinguish between them by overloading, because after erasure it is impossible to distinguish one method call from the other.

For another example, here is a bad version of the integer class, that tries to make it possible to compare an integer with either an integer or a long:

 class Integer implements Comparable<Integer>, Comparable<Long> {     // compile-time error, cannot implement two interfaces with same erasure     private final int value;     public Integer(int value) { this.value = value; }     public int compareTo(Integer i) {         return (value < i.value) ? -1 : (value == i.value) ? 0 : 1;     }     public int compareTo(Long l) {         return (value < l.value) ? -1 : (value == l.value) ? 0 : 1;     } } 

If this were supported, it would, in general, require a complex and confusing definition of bridge methods (see Section 3.7). By far, the simplest and most understandable option is to ban this case.




Java Generics and Collections
Java Generics and Collections
ISBN: 0596527756
EAN: 2147483647
Year: 2006
Pages: 136

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net