2017-02-26 94 views
8

雖然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類型上調用該方法。

注意到:

  • 它肯定是被拋出異常我(消息和堆棧跟蹤)。
  • 如果我不拋棄我的話,不會拋出其他異常。
  • 我已經在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線55是本如果a聲明爲Comparable但是當它被聲明爲Enum不存在。第55行是JDK源中的Enum的聲明,第180行是明確拋出的ClassCastException

回答

13

您正在查看bridge method的影響!

TestInterface聲明的test方法擦除test(Object),但在Test宣佈test方法擦除test(Test)。對test(Object)方法的方法查找將找不到test(Test)方法,所以Java實際上將test(Object)test(Test)方法分開放在Test的字節碼中。

您的第一個試驗使用test(Test)方法,其行爲與您的預期相同。您的其他試驗使用test(Object)方法,該方法是一種只需調用test(Test)方法的合成橋接方法。這種橋接方法並沒有真正的行號,所以它顯示在堆棧軌跡中,相當任意的行數爲11.