Question

I came across an example that suggests that erasure is done differently on the method signature and method, but I don't know why/how. The JLS §8.4.8.3 states:

It is a compile-time error if a type declaration T has a member method m1 and there exists a method m2 declared in T or a supertype of T such that all of the following conditions hold:

  1. m1 and m2 have the same name.
  2. m2 is accessible from T.
  3. The signature of m1 is not a subsignature (§8.4.2) of the signature of m2.
  4. The signature of m1 or some method m1 overrides (directly or indirectly) has the same erasure as the signature of m2 or some method m2 overrides (directly or indirectly).

The compile-time error example given:

class C<T> {
    T id (T x) {...}
}
class D extends C<String> {
    Object id(Object x) {...}
}

The explanation:

This is illegal since D.id(Object) is a member of D, C.id(String) is declared in a supertype of D, and:

  • The two methods have the same name, id
  • C.id(String) is accessible to D
  • The signature of D.id(Object) is not a subsignature of that of C.id(String)
  • The two methods have the same erasure

The first two points are obvious, but I don't understand the last two points of the explanation. How can the two methods have the same erasure if the third point holds? From the third point, it seems the erasure of signature is done using the parameterized class C<String>'s method (i.e. id(String) instead of id(T)). If that is the case, then the two methods should have different erasures, but the example suggests that method erasure is done on the non-parameterized class. So, how is erasure actually applied on the method signature and method?

Was it helpful?

Solution

Erasure means that all occurences of generic type T (String in case of C) are replaced by Object (+ required type casts). As type informations are lost this way, there is a conflict in the example - the JVM would not be able to decide which method to call.

edit(this is wrong): afaik : A subsignature would be a method that accepts a compatible type (e.g. a super type of String) and/or returns a covariant type.

I tried it out and it is confusing, but came to this explanation: The compiler does not erase, but replace generic signatures. Therefore, when D extends C<String> the signature of the overriden C<String>.id becomes: String id(String x). Clearly D's method id has not the same signature and also not the same erasure (because String is not a generic type). Therefore the signature of D.id is not a subsignature of C. (That matches rule 3)

On the other hand, the erasure of C<T>.id(T x) is Object id(Object x) and identical to the erasure of D.id(Object x). (That matches rule 4)

Following that, it would be valid to override id if one could keep the signatures and erasures aligned. And apparently that is possible (although not really useful):

class Foo<T> {
   public void set(final T thing) {}
}

class Bar<T> extends Foo<T> {
   @Override
   public void set(final Object thing) {}
}

This compiles in eclipse without warning.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top