The following code does not compile, however, if I change f(object) to f(String) or f(Integer) it compiles. I've read the other posts about the subject, but I still don't get it, how come the compiler doesn't know which method to use (in case of a new instance A a = new B();)
public class SampleTester {
public static class A {
public void f(int x) { System.out.print("1"); }
public void f(Object x) { System.out.print("2"); }
}
public static class B extends A {
public <T> void f(T x) { System.out.print("3"); } //compiler error
}
public static void main(String[] args) {
A a = new A();
B b= new B();
a.f(3);
a.f("foo");
b.f(3);
b.f("foo");
}
}
If I change T x
to Object t
it still doesn't compile, so what's the difference? and besides, why it doesn't just override the function from A? (both has the same signature after type erasure
The class B
extends A
and thus inherits all the methods that are present in A
. This means that B
will have 3 methods :
public void f(int x)
public void f(Object x)
public <T> void f(T x)
The problem is that f(T x)
will go through type erasure during compilation and T
will be replaced with Object
, thus resulting in a duplicate method since you already have a method that takes an Object
argument.
To fix this, either remove the method that takes an Object
argument or provide an upper bound for T
. (example T extends Number
so that T
is replaced with Number
during type-erasure)
To address your comment (which I have now added to your question) :
public <T> void f(Object x)
doesn't compile because it is not a valid override for public void f(Object x)
from A
.
The method public <T> void f(Object x)
belongs to the subclass and can be called using a superlcass reference as a.<Integer>f(null);
or a.<String>f(null);
. Since overriden methods are resolved at runtime BUT generics go through type erasure at compile time itself, there is no way for the compiler to know whether to substitute T
with Integer
or String
at compile time.
It is still legal to reverse the methods such that you have the method public <T> void f(Object x)
in A
and the method public void f(Object x)
in B
since the compiler has all the information necessary to decide what <T>
should be replaced with.