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.

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 error
    Error: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()

Comments

  1. Sorry man, but javac cannot distiguish between methods that only differ on the return type. Your examples won´t compile.

    ReplyDelete
  2. @Kapser, I suggest you try as it does compile and run, thats where I got the output from. ;)

    ReplyDelete
  3. Seriously? you have one buggy compiler than.
    For reference http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.4.2

    ReplyDelete
  4. 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.

    ReplyDelete
  5. Thank 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.

    ReplyDelete
  6. @developer, clearly it does, how else could I produce the byte code shown.

    ReplyDelete
  7. Yes, it is compiling and running from command prompt. But Eclipse is giving errors.

    ReplyDelete
  8. @Reddy, IntelliJ gives an error as well, but still compiles and runs it.

    ReplyDelete
  9. It compiles because is a Java 1.6 bug. On Java 1.7 it should NOT compile.

    ReplyDelete
  10. @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
  11. @cristian, it doesn't compile with Java 7 even with -source 1.6 -target 1.6 but does compile and run on Java 6. ;)

    ReplyDelete
  12. It is considered as a bug in Java compiler and fixed in Java7. Use Java6 to try the example

    ReplyDelete

Post a Comment

Popular posts from this blog

Low Latency Microservices, A Retrospective

Unusual Java: StackTrace Extends Throwable

System wide unique nanosecond timestamps