堆栈跟踪中的神秘线路 [英] Mysterious line in stack trace
问题描述
虽然在撰写另一个答案时调查堆栈跟踪差异,我遇到了一个我不明白的行为。考虑下面的测试程序(这可以尽量缩小它):
interface TestInterface< U> {
void test(U u);
}
static class Test< T extends Test< T>>实现TestInterface< T> {//第11行
@Override public void test(T t){
throw new RuntimeException(My exception); //第13行
}
}
静态类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);
}
尝试{
TestInterface a = new TestA();
Test b = new TestB();
a.test(b);
} catch(Exception x){
x.printStackTrace(System.out);
}
尝试{
TestInterface a = new TestA();
TestInterface b = new TestB();
a.test(b);
} catch(Exception x){
x.printStackTrace(System.out);
}
}
第11行和第13行标记为上面的代码片段,它可以在ideone上运行。该程序的输出是:
java.lang.RuntimeException:我的异常
在Ideone $ Test.test( Main.java:13)
在Ideone.main(Main.java:25)
java.lang.RuntimeException:我的异常
在Ideone $ Test.test(Main.java:13)
在Ideone $ Test.test(Main.java:11)
在Ideone.main(Main.java:33)
java.lang.RuntimeException:我的异常
在Ideone $ Test.test(Main.java:13)
在Ideone $ Test.test(Main.java:11)
在Ideone.main(Main.java:41)
我的问题是:为什么第二个和第三个测试用例的第11行是堆栈跟踪?这三个测试用例之间的区别在于 b
的声明类型。
第11行(类声明行)仅在以下情况下存在:
- 如果
Test
实现一个接口,并且 - 如果从接口方法抛出异常,并且
- If如果类声明的类型参数包含
extends Test< T>
(不包含第11行)如果它被声明为class Test< T>
),并且 - 如果在
TestInterface
类型而不是Test
类型。
注意到:
- 这肯定是我抛出的异常(消息和堆栈跟踪)。
- 如果我不抛出其他异常,则抛出它。
- 我已经使用Oracle JDK 1.7重现了这一点,在Windows上是1.8,在Ideone上是1.8。但是,1.7在第1行中包含堆栈跟踪元素,而不是11(这很奇怪)。这条线如何在堆栈跟踪中结束,以及为什么当两个对象都声明为
Test
?
这是提示的原始程序,其中
java.lang的第55行如果
存在作为a
被声明为Comparable
,但在声明时不存在,则EnumEnum
。第55行是JDK源代码中Enum
的声明,第180行是一个明确抛出的ClassCastException
。解决方案您正在查看 bridge method !
test
在TestInterface
中声明的方法已经擦除test(Object)
,但是test $ c在
Test
中声明的$ c>方法已经擦除test(Test)
。方法查找test(Object)
方法不会找到test(Test)
方法,所以Java实际上在Test $ c>中放入单独的
test(Object)
和test(Test)
你的第一个试验使用
test(Test)
方法,它的行为和你一样预期。您的其他试验使用test(Object)
方法,该方法是一种仅调用test(Test)
的合成桥接方法>方法。这个桥接方法并没有真正的行号,所以它在堆栈跟踪中显示出相当任意的行数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
andb
.Line 11 (the class declaration line) is only present under the following conditions:
- If
Test
implements an interface, and - If the exception is thrown from the interface method, and
- If the interface takes a type parameter, and
- If the class declaration's type parameter contains
extends Test<T>
(line 11 is not included if it is declared asclass Test<T>
), and - If the method is called on the
TestInterface
type rather than theTest
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 ifa
is declared asComparable
but not present when it is declared asEnum
. Line 55 is the declaration ofEnum
in the JDK source, line 180 is an explicitly thrownClassCastException
.解决方案You're looking at the effects of a bridge method!
The
test
method declared inTestInterface
has erasuretest(Object)
, but thetest
method declared inTest
has erasuretest(Test)
. A method lookup for thetest(Object)
method won't find thetest(Test)
method, so Java actually puts separatetest(Object)
andtest(Test)
methods inTest
's bytecode.Your first trial uses the
test(Test)
method, which behaves as you expected. Your other trials use thetest(Object)
method, which is a synthetic bridge method that just calls thetest(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.这篇关于堆栈跟踪中的神秘线路的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- If