Mysterious line in stack trace

喜夏-厌秋 提交于 2020-01-12 14:31:11

问题


While investigating a stack trace discrepancy when composing another answer, I came across a behavior I do not understand. Consider the following test program (this is as far down as I could narrow it):

interface TestInterface <U> {
    void test (U u);
}

static class Test <T extends Test<T>> implements TestInterface<T> { // line 11
    @Override public void test (T t) {
        throw new RuntimeException("My exception"); // line 13
    }
}

static class TestA extends Test<TestA> { }
static class TestB extends Test<TestB> { }

public static void main (String[] args) throws Exception {

    try {
        Test a = new TestA();
        Test b = new TestB();
        a.test(b);        
    } catch (Exception x) {
        x.printStackTrace(System.out);
    }

    try {
        TestInterface a = new TestA();
        Test b = new TestB();
        a.test(b);        
    } catch (Exception x) {
        x.printStackTrace(System.out);
    }

    try {
        TestInterface a = new TestA();
        TestInterface b = new TestB();
        a.test(b);        
    } catch (Exception x) {
        x.printStackTrace(System.out);
    }

}

Lines 11 and 13 are labelled in the above snippet and it can be run on ideone. The output of that program is:

java.lang.RuntimeException: My exception
    at Ideone$Test.test(Main.java:13)
    at Ideone.main(Main.java:25)
java.lang.RuntimeException: My exception
    at Ideone$Test.test(Main.java:13)
    at Ideone$Test.test(Main.java:11)
    at Ideone.main(Main.java:33)
java.lang.RuntimeException: My exception
    at Ideone$Test.test(Main.java:13)
    at Ideone$Test.test(Main.java:11)
    at Ideone.main(Main.java:41)

My question is: Why is line 11 in the stack trace for the second and third test cases? The difference between the three test cases there are the declared types of a and b.

Line 11 (the class declaration line) is only present under the following conditions:

  1. If Test implements an interface, and
  2. If the exception is thrown from the interface method, and
  3. If the interface takes a type parameter, and
  4. If the class declaration's type parameter contains extends Test<T> (line 11 is not included if it is declared as class Test<T>), and
  5. If the method is called on the TestInterface type rather than the Test type.

Noting that:

  • It is definitely my exception being thrown (message and stack trace).
  • No other exceptions are thrown if I do not throw mine.
  • I have reproduced this with the Oracle JDK 1.7, and 1.8 on Windows, and 1.8 on Ideone. However, 1.7 includes a stack trace element on line 1 instead of 11 (which is doubly weird).

What is happening here? How is that line ending up in the stack trace and why does it not appear if both objects are declared as Test?

Here is the original program that prompted this, where line 55 of java.lang.Enum is present if a is declared as Comparable but not present when it is declared as Enum. Line 55 is the declaration of Enum in the JDK source, line 180 is an explicitly thrown ClassCastException.


回答1:


You're looking at the effects of a bridge method!

The test method declared in TestInterface has erasure test(Object), but the test method declared in Test has erasure test(Test). A method lookup for the test(Object) method won't find the test(Test) method, so Java actually puts separate test(Object) and test(Test) methods in Test's bytecode.

Your first trial uses the test(Test) method, which behaves as you expected. Your other trials use the test(Object) method, which is a synthetic bridge method that just calls the test(Test) method. This bridge method doesn't really have a line number, so it shows up in the stack trace with the fairly arbitrary line number of 11.



来源:https://stackoverflow.com/questions/42464475/mysterious-line-in-stack-trace

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!