I have this code:
class A {
public void func(String str, List<String> lst) {}
}
class B extends A {
public void func(String str, List<Integer> lst) {} // Does not compile
}
The line with the comment does not compile with the following explanation: "both methods have same erasure yet neither overrides the other". On the one hand, I understand that it is not overriding, since the generic type in the second argument is different. On the other hand, after erasure both functions have essentially the same signature, so it is not overloading either. But I am struggling to understand why it doesn't compile. Any help would be appreciated.
ADDED: Even if I change the signature to
public Object func(String str, List<Integer> lst) {return null;}
It still does not compile, with the same reason. Now these are different signatures altogether, so it makes me even more confused.
CodePudding user response:
From JLS 8.4.2:
Two methods or constructors, M and N, have the same signature if they have the same name, the same type parameters (if any) (§8.4.4), and, after adapting the formal parameter types of N to the type parameters of M, the same formal parameter types.
How is that relevant to your code?
The first method signature you posted is:
public void func(String str, List<String> lst)
This signature exists like this only at compile-time because it uses generics. Once compiled, the runtime signature will be like below (
List<String>
becomesList
) due to type erasure:public void func(String str, List lst)
The second method signature in your example is:
public void func(String str, List<Integer> lst)
While the starting point is different – a List of Integer instead of a List of String – the runtime signature for this sees a similar change, replacing
List<Integer>
withList
:public void func(String str, List lst)
Even though your code specifies differences in the argument list (List<String>
vs List<Integer>
) which leads you to believe there are two different method signature, the two signatures are effectively the same:
- they are both named "func", and
- they have the same argument list: String, List
In Erasure of Generic Types, they show an example similar to what you're seeing where a type of Node<T>
becomes Node
. That is happening with your examples, too, where List<String>
becomes List
.
During the type erasure process, the Java compiler erases all type parameters and replaces each with its first bound if the type parameter is bounded, or Object if the type parameter is unbounded.
Note that the return type is not part of a method signature uniqueness, which is why changing one to return Object
didn't make a difference.
Here's a bit more info from the Java Tutorial section on Type Erasure, with a little emphasis added in a few spots:
Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:
- Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods.
- Insert type casts if necessary to preserve type safety.
- Generate bridge methods to preserve polymorphism in extended generic types. Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.