1

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

CKing
  • 14,153
  • 4
  • 39
  • 77
DsCpp
  • 1,856
  • 11
  • 25
  • I am not sure why this question was downvoted. It seems to be a pretty good question IMO. – CKing Feb 12 '17 at 15:37
  • While I didn't downvote, an obvious question would be "So what's the error message?" *Any* question saying that something doesn't compile (or compiles, but throws an exception) should include the error details. – Jon Skeet Feb 12 '17 at 16:28

1 Answers1

3

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.

CKing
  • 14,153
  • 4
  • 39
  • 77
  • But if I change T x to Object t it compiles, 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 – DsCpp Feb 12 '17 at 14:35
  • It doesn't compile here. Unless you also remove from the method signature. In that case, you simply override the base method. – JB Nizet Feb 12 '17 at 14:37
  • @DsCpp My previous explanation was incomplete as it assumed some deeper understanding at your level. Please see the edit for a more clear explanation. – CKing Feb 12 '17 at 15:36
  • See the JLS http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.2 and http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.8.1 – Lew Bloch Feb 12 '17 at 18:26