javagenericserasure

Why can't overriding methods specify a type parameter if the overriden method doesn't?


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


Solution

  • 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.