With generics, the return type is part of the method signature.
I recently came across this odd behaviour in Java. The JVM has always treated the return type as part of the signature but before Generics Java did not. Here is an example of where it does.
This was tested using Oracle JDK 6 update 23, OpenJDK 6 update 20, and IBM's JDK R9 2.4. Apparently Java 5.0 update 22 doesn't not compile this code, but given Java 5.0 is EOL, I would have hoped Java 6 would have the correct behaviour.
Note: all these print() methods have the same method name and parameters after erasure, but importantly they have different return types and the JVM can distiguish them based on that. If you make the return types the same, the program will not compile.
I was sceptical that this would compile at first and wanted to look at the compiled byte code.
This was tested using Oracle JDK 6 update 23, OpenJDK 6 update 20, and IBM's JDK R9 2.4. Apparently Java 5.0 update 22 doesn't not compile this code, but given Java 5.0 is EOL, I would have hoped Java 6 would have the correct behaviour.
public class Main { public static void main(String... args) { Main.<Integer>print(); Main.<Short>print(); Main.<Byte>print(); Main.<Void>print(); } public static <T extends Integer> int print() { System.out.println("here - Integer"); return 0; } public static <T extends Short> short print() { System.out.println("here - Short"); return 0; } public static <T extends Byte> byte print() { System.out.println("here - Byte"); return 0; } public static <T extends Void> void print() { System.out.println("here - Void"); } }Prints
here - Integer here - Short here - Byte here - Void
Note: all these print() methods have the same method name and parameters after erasure, but importantly they have different return types and the JVM can distiguish them based on that. If you make the return types the same, the program will not compile.
I was sceptical that this would compile at first and wanted to look at the compiled byte code.
$ javap -classpath . -c Main Compiled from "Main.java" public class Main extends java.lang.Object{ public Main(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."":()V 4: return public static void main(java.lang.String[]); Code: 0: invokestatic #2; //Method print:()I 3: pop 4: invokestatic #3; //Method print:()S 7: pop 8: invokestatic #4; //Method print:()B 11: pop 12: invokestatic #5; //Method print:()V 15: return public static int print(); Code: 0: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #7; //String here - Integer 5: invokevirtual #8; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: iconst_0 9: ireturn public static short print(); Code: 0: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #9; //String here - Short 5: invokevirtual #8; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: iconst_0 9: ireturn public static byte print(); Code: 0: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #10; //String here - Byte 5: invokevirtual #8; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: iconst_0 9: ireturn public static void print(); Code: 0: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #11; //String here - Void 5: invokevirtual #8; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return }
Update
This doesn't compile with Oracle Java 7. You get an errorError:Error:line (20)error: name clash:print() and print() have the same erasure where T#1,T#2 are type-variables: T#1 extends Short declared in method print() T#2 extends Integer declared in method print()
Sorry man, but javac cannot distiguish between methods that only differ on the return type. Your examples won´t compile.
ReplyDelete@Kapser, I suggest you try as it does compile and run, thats where I got the output from. ;)
ReplyDeleteSeriously? you have one buggy compiler than.
ReplyDeleteFor reference http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.2
I have tried the latest version of OpenJDK for ubuntu "1.6.0_20", Oracle's 1.6.0_23 and IBM's JRE 1.6.0 IBM J9 2.4 and they all compile the same.
ReplyDeleteThank you for the link BTW, It doesn't mention anything about generics making a difference but it does. Take the generics off and it won't compile.
ReplyDeletedoes not compile.
ReplyDelete@developer, clearly it does, how else could I produce the byte code shown.
ReplyDeleteYes, it is compiling and running from command prompt. But Eclipse is giving errors.
ReplyDelete@Reddy, IntelliJ gives an error as well, but still compiles and runs it.
ReplyDeleteIt compiles because is a Java 1.6 bug. On Java 1.7 it should NOT compile.
ReplyDelete@cristian, That was my first impression as well, however JLS 8.4.2 isn't clear on this as it talks about types which now includes generics. It doesn't talk about type erasure.
ReplyDelete@cristian, it doesn't compile with Java 7 even with -source 1.6 -target 1.6 but does compile and run on Java 6. ;)
ReplyDeleteIt is considered as a bug in Java compiler and fixed in Java7. Use Java6 to try the example
ReplyDelete