I'm trying to solve a problem and I've come up with this solution (simplified):
package help;
public class Problem {
private static class A<T> {
public void foo(T t) {}
}
private static class B<T> {}
private static class C<T> extends A<B<T>> {
public void foo(T t) {}
}
}
It wont compile since "foo(T) in help.Problem.C clashes with foo(T) in help.Problem.A; both methods have same erasure, yet neither overrides the other".
I'm not just trying to solve the problem, i also would like to understand what is going on. I noticed that if the B class is omitted, the error is gone.
Also: could you provide an example of a piece of code such, that the compiler wouldn't be able to bind a variable to one of those two methods?
- Erasure is a type of alteration in document. It can be classified as chemical erasure and physical erasure.
Type erasure is a process in which compiler replaces a generic parameter with actual class or bridge method. In type erasure, compiler ensures that no extra classes are created and there is no runtime overhead.
Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.
At runtime, the parameter types are replaced by Object . So saveAll(Collection<?>) and saveAll(Collection<MyClass>) are transformed to saveAll(Collection) . This is a name clash.
The class definition of A
means that the compiler "expects" something like this if you want to override foo
:
private static class C<T> extends A<B<T>> {
public void foo(B<T> t) {}
}
However, since you're just providing T
as a type to that method rather than B<T>
, it's not a valid override of foo
in class A
- the parameter types must match. This wouldn't cause an error with non-generic types - you've just got an overloaded method (a method that has the same name, but differs in the type / number of its parameters) instead of an overriden one.
However, since the generic types disappear on compilation (through type erasure), this means you can't have two methods differentiated only by an generic type, as their definitions in the bytecode would be identical.
To see why they erase to the same type, just remove all <>
and the stuff inside them, then replace all the generic parameter types with Object
:
class Problem {
private static class A {
public void foo(Object t) {}
}
private static class B {}
private static class C extends A {
public void foo(Object t) {}
}
}
Now you see why.
Not being able to overload a method like this is one of the limitations of generics. Read more here.
The root of the problem is really that in C
, T
means something different than it does in A
. Let's suppose you remove the declaration of foo
in C
and you have an instance of C<T>
. You want to call foo
using this instance. What parameter do you need to pass? A B<T>
, right? If foo
is declared to accept a T
in C
, that wouldn't override A.foo
because A.foo
need to accept a B<T>
, not just T
.
To actually override foo
, change the parameter to be of type B<T>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With