Why calling a generic overridden function produces 2 stack frames in Java

Viewed 48

I've noticed that sometimes the call of my overridden function produces two stack frames instead of one. And after some experiments, I noticed that it is somehow connected with generic return type.

There is an example:

public class MyTest {
    interface A<T> {
        T a();
    }

    static class AA implements A<String> {
        @Override
        public String a() {
            throw new RuntimeException("fail");
        }
    }

    static void myFun(A<String> a) {
        a.a();
    }

    public static void main(String[] args) {
        myFun(new AA());
    }
}

And running these test prints:

java.lang.RuntimeException: fail
    at MyTest$AA.a(MyTest.java:9)
    at MyTest$AA.a(MyTest.java:6)
    at MyTest.myFun(MyTest.java:14)
    at MyTest.main(MyTest.java:18)
...

And if I replace generic return type with the exact return type, e.g.

public class MyTest {
    interface A<T> {
        String a();
    }

    static class AA implements A<String> {
        @Override
        public String a() {
            throw new RuntimeException("fail");
        }
    }

    static void myFun(A<String> a) {
        a.a();
    }

    public static void main(String[] args) {
        myFun(new AA());
    }
}

The resulting exception will look like this:

java.lang.RuntimeException: fail
    at MyTest$AA.a(MyTest.java:9)
    at MyTest.myFun(MyTest.java:14)
    at MyTest.main(MyTest.java:18)

What does produce this additional stackframe at MyTest$AA.a(MyTest.java:6) (line 6 is static class AA implements A<String> {)? Thanks!

EDT: Also, adding generic parameter produces this behavior. E.g.

public class MyTest {
    interface A<T> {
        String a(T t);
    }

    static class AA implements A<String> {
        @Override
        public String a(String s) {
            throw new RuntimeException(s);
        }
    }

    static void myFun(A<String> a) {
        a.a("fail");
    }

    public static void main(String[] args) {
        myFun(new AA());
    }
}

Produces:

java.lang.RuntimeException: fail
    at MyTest$AA.a(MyTest.java:9)
    at MyTest$AA.a(MyTest.java:6)
    at MyTest.myFun(MyTest.java:14)
    at MyTest.main(MyTest.java:18)

Tests are run on Java 11.

EDT 2: remove test library

1 Answers

Before J2SE 5.0, Java did not have covariant return types. The JVM bytecode rules are that it overrides on exact parameter match including the return type. So the compiler inserts a synthetic bridge method that you are seeing.

Perhaps surprisingly, the JLS doesn't talk much about bridge methods. It is in the notes on the section Create Frame, Synchronize, Transfer Control in the Expressions chapter.

Related