堆栈跟踪中的神秘线

时间:2017-02-26 03:41:09

标签: java generics exception language-lawyer stack-trace

investigating a stack trace discrepancy撰写另一个答案时,我遇到了一个我不理解的行为。考虑以下测试程序(这是我可以缩小它的范围):

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行(类声明行)仅在以下条件下出现:

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

    • 绝对是我抛出的异常(消息和堆栈跟踪)。
    • 如果我不扔我的话,不会抛出其他例外。
    • 我在Windows上使用Oracle JDK 1.7和1.8以及Ideone上的1.8重现了这一点。但是,1.7在第1行包含堆栈跟踪元素而不是11(这是双重奇怪的)。

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

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

1 个答案:

答案 0 :(得分:13)

您正在查看bridge method的效果!

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

您的第一次试用使用test(Test)方法,该方法的行为符合您的预期。您的其他试验使用test(Object)方法,这是一种只调用test(Test)方法的合成桥接方法。这种桥接方法实际上没有行号,因此它在堆栈跟踪中显示相当任意的行号11。