8

I have a simple program like this:

package test;

public class TestGenericsInheritance {

    public static void main(String[] args) {}

    public static abstract class A<Q>{

        public void foo2(Q obj){}
        public abstract <T> void foo(T obj);
    }

    public static class C extends A<Object>{

        @Override
        public <T> void foo(T obj) {}
    }

    public static class B extends A{

        @Override
        public <T> void foo(T obj) {}
    }
}

As you can see It does absolutely nothing. Compilation of this program files on java 1.6 and 1.7 with following error:

/D:/Projects/.../test/TestGenericsInheritance.java:[24,19] test.TestGenericsInheritance.B is not abstract and do es not override abstract method foo(java.lang.Object) in test.TestGenericsInheritance.A /D:/Projects/.../test/TestGenericsInheritance.java:[27,25] name clash: foo(T) in test.TestGenericsInheritance .B and foo(T) in test.TestGenericsInheritance.A have the same erasure, yet neither overrides the other /D:/Projects/.../test/TestGenericsInheritance.java:[26,9] method does not override or implement a method from a supertype

Classes C and B are semantically identical, however class B doesn't recognize method foo as implementation of A#foo. To make this code compliant I have to implement method A#foo in class B with following signature:

public void foo(Object obj) 

My question is, why my program doesn't compile? Generic types Q and T are completely independent, so why I may implement generic function A#foo only when I explicitly specify generic type Q for the inherited class A?

Thanks

4castle
  • 32,613
  • 11
  • 69
  • 106
MAREK
  • 173
  • 1
  • 1
  • 7
  • I'm able to repro'. What's more is that having the non-generic method and generic method at the same time causes an error for having the same type erasure. – 4castle Jul 01 '16 at 13:47
  • Eclipse suggests adding: ```@Override public void foo(Object obj) {}``` to ```B``` which does actually make it compile. (1.8) – Jorn Vernee Jul 01 '16 at 14:08
  • @4castle your edit has made the error messages confusing as they mention a source file name which you have removed. – Klitos Kyriacou Jul 01 '16 at 14:39
  • @KlitosKyriacou It wasn't me that removed it. It was the editor before. I'll rollback. – 4castle Jul 01 '16 at 14:39
  • @4castle apologies for blaming you, it was indeed the person before you. This is an interesting issue and as the answer below shows, the moral is that you should try to avoid using raw types whenever possible. – Klitos Kyriacou Jul 01 '16 at 14:43

1 Answers1

6

B extends the raw type A. Because you use a raw type, all generic information is erased, not only the type variable you failed to specify (see Section 4.6 of the Java Language Specification). This means that A has a method void foo(Object obj), whereas A<SomeType> has a method <T> void foo(T obj).

If you override a method, it must have the same signature (optionally after taking the type erasure of the overridden method) -- interestingly, the overriding method may have a different, more specific, return type. The signatures of your two methods are different (you would need type erasure in the overriding method), and therefore your example doesn't compile.

The reason type erasure was implemented as it is, is backwards compatibility. The idea was that new code would only use generics, and that only old code would use raw types. So, the goal was not to make raw types the most convenient (because new code shouldn't use them anyway), but to make raw types the most compatible.

Consider for example the class ArrayList, which was introduced in Java 2 (well before generics). It had a method public Object[] toArray(Object[] a). In Java 5 generics were introduced, and the signature of this method could be changed to public <T> T[] toArray(T[] a) (where T is not the element type) without difficulty: because of the way type erasure is implemented, for old code which used (or subclassed) ArrayList rather than ArrayList<SomeType> the method signature stayed the same.

Hoopje
  • 12,677
  • 8
  • 34
  • 50
  • I still don't get it. Where is "all generic information is erased" appear in the spec? – 4castle Jul 01 '16 at 14:20
  • 1
    It says: "Type erasure also maps the signature (§8.4.2) of a constructor or method to a signature that has no parameterized types or type variables." No mention of any additional conditions there. – Hoopje Jul 01 '16 at 14:27
  • @Hoopje Thank you for your explanation. I understand what is the reason behind my issue. However I see no point in erasing all generic types in such case. I understand that type parameter should be erased. But why erase method parameter if it is independent from type parameter? Could you provide any case in which such erasure is necessary? – MAREK Jul 01 '16 at 14:58
  • @MAREK I updated the answer to answer also that question. – Hoopje Jul 01 '16 at 19:40