Before the introduction of Generics in Java 5 it’s commonly known that in a class you can’t have two methods with the same signature that differ by return type alone. More formally, the Java Language Specification §8.4.2 says that “it is a compile time error to declare two methods with override-equivalent signatures in a class”. Take for example the following code which does exactly this and unsurprisingly doesn’t compile:
1 // Generates error "myMethod(java.util.List) is already defined 2 // in DoesNotCompile when compiled with Sun JDK version 1.5.0_13. 3 abstract class DoesNotCompile { 4 abstract String myMethod(List listOfStrings); 5 6 abstract Integer myMethod(List listOfIntegers); 7 }
Code Listing 1
It might be surprising though that a class with the same methods changed slightly to use Generics compiles without errors:
1 abstract class Compiles { 2 abstract String myMethod(List<String> listOfStrings); 3 4 abstract Integer myMethod(List<Integer> listOfIntegers); 5 }
Code Listing 2
Without knowing better, one could dismiss the two method signatures above as different; after all, one uses a type parameter of java.lang.String
and the other java.lang.Integer
. But Generics works by type erasure, where specific type information like List<String>
is erased by the compiler to become just List
. Type erasure of the parameter types of the two methods in Listing 2 results in the same type —java.util.List
— for both methods.
So the two methods in Compiles
really do differ solely by return type yet aren’t override-equivalent. Why this is so isn’t clear. JLS §8.4.2 and an evaluation in a Sun bug report explain that it’s because the method erasures aren’t the same, but unfortunately there aren’t any details given about how a method erasure is computed exactly. Whatever the algorithm is, we now know empirically that the method return type matters.