Is this valid Java?
import java.util.Arrays;
import java.util.List;
class TestWillThatCompile {
public static String f(List<String> list) {
System.out.println("strings");
return null;
}
public static Integer f(List<Integer> list) {
System.out.println("numbers");
return null;
}
public static void main(String[] args) {
f(Arrays.asList("asdf"));
f(Arrays.asList(123));
}
}
- Eclipse 3.5 says yes
- Eclipse 3.6 says no
- Intellij 9 says yes
- Sun javac 1.6.0_20 says yes
- GCJ 4.4.3 says yes
- GWT compiler says yes
- Crowd at my previous Stackoverflow question says no
My java theory understanding says no!
It would be interesting to know what the JLS is saying about it.
It depends upon how you wish to call these methods. If you wish to call these methods from other Java source code, then it is considered invalid for reasons illustrated in Edwin’s answer. This is a limitation of the Java Language.
However, not all classes need to be generated from Java source code (consider all the languages that use the JVM as their runtime: JRuby, Jython, etc…). At the bytecode level, the JVM can disambiguate the two methods because the bytecode instructions specify the return type they are expecting. For example, here is a class written in Jasmin that can call either of these methods:
I compile it to a class file using the following command:
And call it using:
Behold, the output is:
Update
Simon posted an example program that calls these methods:
Here is the Java bytecode generated:
>javap -c RealyCompilesAndRunsFine Compiled from "RealyCompilesAndRunsFine.java" class RealyCompilesAndRunsFine extends java.lang.Object{ RealyCompilesAndRunsFine(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."":()V 4: return public static java.lang.String f(java.util.List); Code: 0: aload_0 1: iconst_0 2: invokeinterface #2, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 7: checkcast #3; //class java/lang/String 10: areturn public static java.lang.Integer f(java.util.List); Code: 0: aload_0 1: iconst_0 2: invokeinterface #2, 2; //InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 7: checkcast #4; //class java/lang/Integer 10: areturn public static void main(java.lang.String[]); Code: 0: iconst_1 1: anewarray #3; //class java/lang/String 4: dup 5: iconst_0 6: ldc #5; //String asdf 8: aastore 9: invokestatic #6; //Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List; 12: invokestatic #7; //Method f:(Ljava/util/List;)Ljava/lang/String; 15: astore_1 16: iconst_1 17: anewarray #4; //class java/lang/Integer 20: dup 21: iconst_0 22: bipush 123 24: invokestatic #8; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer; 27: aastore 28: invokestatic #6; //Method java/util/Arrays.asList:([Ljava/lang/Object;)Ljava/util/List; 31: invokestatic #9; //Method f:(Ljava/util/List;)Ljava/lang/Integer; 34: astore_2 35: getstatic #10; //Field java/lang/System.out:Ljava/io/PrintStream; 38: aload_1 39: invokevirtual #11; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 42: getstatic #10; //Field java/lang/System.out:Ljava/io/PrintStream; 45: aload_2 46: invokevirtual #12; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V 49: returnIt turns out the Sun compiler is generating the bytecode necessary to disambiguate the methods (see instructions 12 and 31 in the last method).
Update #2
The Java Language Specification suggests that this may, in fact, be valid Java source code. On page 449 (§15.12 Method Invocation Expressions) we see this:
Unless I am mistaken, this behavior should only apply to methods declared as abstract, though…
Update #3
Thanks to ILMTitan’s comment: