2017-02-26 2 views
8

Während investigating a stack trace discrepancy beim Verfassen einer anderen Antwort, stieß ich auf ein Verhalten, das ich nicht verstehe. Betrachten Sie das folgende Testprogramm (das ist so weit unten wie ich es verengen könnte):Mysteriöse Linie im Stapel verfolgen

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

} 

Linien 11 und 13 sind in dem obigen Schnipsel markiert und es kann run on ideone sein. Die Ausgabe dieses Programms ist:

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) 

Meine Frage ist: Warum ist Linie 11 in der Stack-Trace für den zweiten und dritten Testfälle? Der Unterschied zwischen den drei Testfällen sind die deklarierten Typen a und b.

Zeile 11 (die Klassendeklaration Linie) ist nur vorhanden, unter den folgenden Bedingungen:

  1. Wenn Test eine Schnittstelle implementiert, und
  2. Wenn die Ausnahme von der Schnittstellenmethode geworfen wird, und
  3. Wenn die Schnittstelle einen Typparameter verwendet und
  4. Wenn der Typparameter der Klassendeklaration extends Test<T> enthält (Zeile 11 ist nicht enthalten, wenn sie als class Test<T> deklariert ist) und
  5. Wenn die Methode über den Typ TestInterface statt über den Typ Test aufgerufen wird.

dass in Anbetracht:

  • Es ist auf jeden Fall meine Ausnahme (Meldung und Stack-Trace) geworfen.
  • Keine anderen Ausnahmen werden geworfen, wenn ich meine nicht werfe.
  • Ich habe dies mit dem Oracle JDK 1.7 und 1.8 unter Windows und 1.8 auf Ideone reproduziert. 1.7 enthält jedoch ein Stack-Trace-Element in Zeile 1 statt in 11 (was doppelt seltsam ist).

Was passiert hier? Wie kommt es, dass diese Zeile im Stack-Trace endet und warum erscheint sie nicht, wenn beide Objekte als Test deklariert sind?

Here is the original program that prompted this, wo die Leitung 55 von java.lang.Enum vorhanden ist, wenn a als Comparable deklariert, aber nicht vorhanden, wenn es als Enum deklariert wird. Zeile 55 ist die Deklaration von Enum in der JDK-Quelle, Zeile 180 ist eine explizit geworfene ClassCastException.

Antwort

13

Sie betrachten die Auswirkungen einer bridge method!

Die test Methode in TestInterface erklärt hat Löschung test(Object), aber die test Methode in Test hat test(Test) Löschung erklärt. Ein Methoden-Lookup für die test(Object) Methode wird die test(Test) Methode nicht finden, so Java setzt separate test(Object) und test(Test) Methoden in Test 's Bytecode.

Ihre erste Testversion verwendet die test(Test)-Methode, die sich wie erwartet verhält. Ihre anderen Versuche verwenden die Methode test(Object), bei der es sich um eine synthetische Brückenmethode handelt, die nur die Methode test(Test) aufruft. Diese Bridge-Methode hat nicht wirklich eine Zeilennummer, daher erscheint sie in der Stack-Trace mit der ziemlich willkürlichen Zeilennummer von 11.