Java Insanity: Two methods with the same super.method()

Overview

Recently I wrote an article on how you could write multiple methods with the same name and parameters by using generics. I wondered if you can have multiple methods with the same super.method()

Generics and signatures

The way the Java compiler works, the generic is effectively part of the signature. However, due to type erasure, the JVM doesn't care so how can this work? The answer is that for the JVM the return type is part of the signature. This means if you have two method with different generic signatures and return types they can have the same name and parameter types.

For the purposes of determining a parent super.method(), they share the same method. So if you have two methods overriding a method, which one is actually called. The answer is neither, polymorphism breaks and the parent still gets called.
static class A {
    public Number method() {
        System.out.println("Inside: Number A.method()");
        return 0;
    }
}

static class B extends A {
    public <T extends Integer> T method() {
        System.out.println("Inside: Integer B.method()");
        super.method();
        return (T) (Integer) 0;
    }
    public <T extends Long> T method() {
        System.out.println("Inside: Long B.method()");
        super.method();
        return (T) (Long) 0L;
    }

}
public static void main(String... args) {
    B b = new B();
    b.<Integer>method();
    System.out.println("=============");
    b.<Long>method();
    System.out.println("=============");
    A a = b;
    a.method();
}

prints
Inside: Integer B.method()
Inside: Number A.method()
=============
Inside: Long B.method()
Inside: Number A.method()
=============
Inside: Number A.method()

The byte code

For anyone who doubts this even compiles here is the byte code for your interest.

Main byte code

$ javap -c -classpath . 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:   new     #2; //class Main$B
   3:   dup
   4:   invokespecial   #3; //Method Main$B."":()V
   7:   astore_1
   8:   aload_1
   9:   invokevirtual   #4; //Method Main$B.method:()Ljava/lang/Integer;
   12:  pop
   13:  getstatic       #5; //Field java/lang/System.out:Ljava/io/PrintStream;
   16:  ldc     #6; //String =============
   18:  invokevirtual   #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   21:  aload_1
   22:  invokevirtual   #8; //Method Main$B.method:()Ljava/lang/Long;
   25:  pop
   26:  getstatic       #5; //Field java/lang/System.out:Ljava/io/PrintStream;
   29:  ldc     #6; //String =============
   31:  invokevirtual   #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   34:  aload_1
   35:  astore_2
   36:  aload_2
   37:  invokevirtual   #9; //Method Main$A.method:()Ljava/lang/Number;
   40:  pop
   41:  return

}

Main.A byte code

$ javap -c -classpath . Main\$A
Compiled from "Main.java"
class Main$A extends java.lang.Object{
Main$A();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   return

public java.lang.Number method();
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #3; //String Inside: Number A.method()
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   iconst_0
   9:   invokestatic    #5; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   12:  areturn

}

Main.B byte code

You can see on line 9 of each method that they call the same parent class.
$ javap -c -classpath . Main\$B
Compiled from "Main.java"
class Main$B extends Main$A{
Main$B();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method Main$A."":()V
   4:   return

public java.lang.Integer method();
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #3; //String Inside: Integer B.method()
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   aload_0
   9:   invokespecial   #5; //Method Main$A.method:()Ljava/lang/Number;
   12:  pop
   13:  iconst_0
   14:  invokestatic    #6; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   17:  areturn

public java.lang.Long method();
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #7; //String Inside: Long B.method()
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   aload_0
   9:   invokespecial   #5; //Method Main$A.method:()Ljava/lang/Number;
   12:  pop
   13:  lconst_0
   14:  invokestatic    #8; //Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
   17:  areturn

}

Comments

  1. Interesting... But if I use Eclipse for compilation I get error 'Duplicate method method() in type test.B'. If i use javac for compilation it is good!

    ReplyDelete
  2. @mmashnitsky, IntelliJ complains as well, but compiles it fine (with the Sun/Oracle javac)

    ReplyDelete
  3. Thanks for interesting articles!

    ReplyDelete
  4. This is nice post. It is really beneficial for me. Here I have got another link on super method in java, this link is also beneficial... that link is..
    http://mindstick.com/Blog/252/Super%20method%20in%20java

    Thanks

    ReplyDelete
  5. @Memtech, When posting code, you should try to ensure it compiles. Posting pseudo code isn't as useful. ;)

    ReplyDelete

Post a Comment

Popular posts from this blog

Low Latency Microservices, A Retrospective

Unusual Java: StackTrace Extends Throwable

System wide unique nanosecond timestamps