堆栈跟踪中的神秘行
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 行?三个测试用例的区别在于a
和b
的声明类型。
第 11 行(class 声明行)仅在以下条件下出现:
- 如果
Test
实现了一个接口,并且
- 如果接口方法抛出异常,并且
- 如果接口采用类型参数,并且
- 如果class声明的类型参数包含
extends Test<T>
(如果声明为class Test<T>
则不包含第11行),并且
- 如果在
TestInterface
类型而不是 Test
类型上调用该方法。
注意到:
- 这绝对是我抛出的异常(消息和堆栈跟踪)。
- 如果我不抛出我的,则不会抛出其他异常。
- 我已经用 Oracle JDK 1.7 和 1.8 在 Windows 上复制了这个,在 Ideone 上复制了 1.8。但是,1.7 在第 1 行而不是第 11 行包含堆栈跟踪元素(这很奇怪)。
这里发生了什么?该行如何在堆栈跟踪中结束,如果两个对象都声明为 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。
虽然
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 行?三个测试用例的区别在于a
和b
的声明类型。
第 11 行(class 声明行)仅在以下条件下出现:
- 如果
Test
实现了一个接口,并且 - 如果接口方法抛出异常,并且
- 如果接口采用类型参数,并且
- 如果class声明的类型参数包含
extends Test<T>
(如果声明为class Test<T>
则不包含第11行),并且 - 如果在
TestInterface
类型而不是Test
类型上调用该方法。
注意到:
- 这绝对是我抛出的异常(消息和堆栈跟踪)。
- 如果我不抛出我的,则不会抛出其他异常。
- 我已经用 Oracle JDK 1.7 和 1.8 在 Windows 上复制了这个,在 Ideone 上复制了 1.8。但是,1.7 在第 1 行而不是第 11 行包含堆栈跟踪元素(这很奇怪)。
这里发生了什么?该行如何在堆栈跟踪中结束,如果两个对象都声明为 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。