堆栈跟踪中的神秘行

Mysterious line in stack trace

虽然 在撰写另一个答案时,我遇到了一个我不明白的行为。考虑以下测试程序(这是我所能缩小的范围):

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);
    }

}

第 11 行和第 13 行在上面的代码片段中被标记,可以是 run on ideone。该程序的输出是:

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)

我的问题是:为什么第二个和第三个测试用例的堆栈跟踪中的第 11 行?三个测试用例的区别在于ab的声明类型。

第 11 行(class 声明行)仅在以下条件下出现:

  1. 如果Test实现了一个接口,并且
  2. 如果接口方法抛出异常,并且
  3. 如果接口采用类型参数,并且
  4. 如果class声明的类型参数包含extends Test<T>(如果声明为class Test<T>则不包含第11行),并且
  5. 如果在 TestInterface 类型而不是 Test 类型上调用该方法。

注意到:

这里发生了什么?该行如何在堆栈跟踪中结束,如果两个对象都声明为 Test,为什么它不出现?

Here is the original program that prompted this,其中 java.lang.Enum 的第 55 行在 a 声明为 Comparable 时存在,但在声明为 Enum 时不存在。第 55 行是 JDK 源中 Enum 的声明,第 180 行是显式抛出的 ClassCastException.

您正在查看 bridge method!

的效果

TestInterface中声明的test方法有擦除test(Object),但Test中声明的test方法有擦除test(Test)test(Object) 方法的方法查找不会找到 test(Test) 方法,因此 Java 实际上在 Test 中放置了单独的 test(Object)test(Test) 方法的字节码。

您的第一个试用版使用了 test(Test) 方法,它的行为符合您的预期。您的其他试验使用 test(Object) 方法,这是一种仅调用 test(Test) 方法的合成桥接方法。这个桥接方法并没有真正的行号,所以它在堆栈跟踪中显示了相当任意的行号 11。