Eine besondere Funktion der Ausnahmetypinferenz in Java 8

Lesezeit: 5 Minuten

Benutzer-Avatar
Marco Topolnik

Beim Schreiben von Code für eine andere Antwort auf dieser Seite bin ich auf diese Besonderheit gestoßen:

static void testSneaky() {
  final Exception e = new Exception();
  sneakyThrow(e);    //no problems here
  nonSneakyThrow(e); //ERRROR: Unhandled exception: java.lang.Exception
}

@SuppressWarnings("unchecked")
static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
  throw (T) t;
}

static <T extends Throwable> void nonSneakyThrow(T t) throws T {
  throw t;
}

Erstens bin ich ziemlich verwirrt, warum die sneakyThrow Aufruf ist für den Compiler OK. Auf welchen möglichen Typ wurde daraus geschlossen T wenn nirgendwo ein ungeprüfter Ausnahmetyp erwähnt wird?

Zweitens, wenn man akzeptiert, dass dies funktioniert, warum beschwert sich dann der Compiler über die nonSneakyThrow Anruf? Sie scheinen sich sehr ähnlich zu sein.

Benutzer-Avatar
der Koop

Das T von sneakyThrow wird vermutet RuntimeException. Dies kann aus der Sprachspezifikation zur Typinferenz (http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html)

Zunächst gibt es einen Hinweis in Abschnitt 18.1.3:

Eine Grenze des Formulars throws α ist rein informativ: Es weist die Auflösung an, die Instanziierung von α zu optimieren, so dass es sich, wenn möglich, nicht um einen geprüften Ausnahmetyp handelt.

Dies hat keinerlei Auswirkungen, weist uns aber auf den Abschnitt Auflösung (18.4) hin, der weitere Informationen zu abgeleiteten Ausnahmetypen mit einem Sonderfall enthält:

… Andernfalls, wenn die gebundene Menge enthält throws αiund die eigentlichen Obergrenzen von αi sind höchstens Exception, Throwableund Objectdann Ti = RuntimeException.

Dieser Fall trifft zu sneakyThrow – die einzige Obergrenze ist ThrowableAlso T wird vermutet RuntimeException gemäß der Spezifikation, also kompiliert es. Der Hauptteil der Methode ist unerheblich – die ungeprüfte Umwandlung ist zur Laufzeit erfolgreich, weil sie nicht tatsächlich stattfindet, und hinterlässt eine Methode, die das zur Kompilierzeit geprüfte Ausnahmesystem umgehen kann.

nonSneakyThrow kompiliert nicht wie diese Methode T hat eine Untergrenze von Exception (dh T muss ein Supertyp von sein Exceptionoder Exception selbst), was eine geprüfte Ausnahme ist, aufgrund des Typs, mit dem es aufgerufen wird, also that T wird gefolgert als Exception.

  • @Maksym Du musst das meinen sneakyThrow Anruf. Die besonderen Vorschriften über den Rückschluss von throws T Formulare gab es in der Spezifikation von Java 7 nicht.

    – Marko Topolnik

    9. Juli 2015 um 12:09 Uhr

  • Kleine Spitzfindigkeit: In nonSneakyThrow, T muss sein Exceptionnicht “ein Supertyp von” Exceptionweil es genau der Typ des Arguments ist, das zur Kompilierzeit auf der Aufrufseite deklariert wurde.

    – llog

    9. Juli 2015 um 12:20 Uhr

  • @llogiq Wenn ich die Spezifikation richtig gelesen habe, hat sie eine Untergrenze von Exception und eine Obergrenze von Throwablealso ist die kleinste Obergrenze, die der resultierende abgeleitete Typ ist Exception.

    – der Koop

    9. Juli 2015 um 12:30 Uhr


  • @llogiq Beachten Sie, dass der Typ des Arguments nur eine untere Typgrenze festlegt, da jeder Supertyp des Arguments ebenfalls akzeptabel ist.

    – Marko Topolnik

    9. Juli 2015 um 12:55 Uhr

  • das „oder Exception sich selbst“ könnte für den Leser hilfreich sein, aber allgemein sollte beachtet werden, dass die Spezifikation die Begriffe „Subtyp“ und „Supertyp“ immer im Sinne von „sich selbst einschließend“ verwendet…

    – Holger

    9. Juli 2015 um 13:06 Uhr

Benutzer-Avatar
Zhong Yu

Wenn der Typrückschluss eine einzelne Obergrenze für eine Typvariable erzeugt, wird normalerweise die Obergrenze als Lösung gewählt. Zum Beispiel, wenn T<<NumberDie Lösung ist T=Number. Obwohl Integer, Float usw. könnten die Einschränkung ebenfalls erfüllen, es gibt keinen guten Grund, sie zu bevorzugen Number.

Das galt auch für throws T in Java 5-7: T<<Throwable => T=Throwable. (Sneaky Throw-Lösungen hatten alle explizite <RuntimeException> Geben Sie andernfalls Argumente ein <Throwable> wird gefolgert.)

In Java8 wird dies mit der Einführung von Lambda problematisch. Betrachten Sie diesen Fall

interface Action<T extends Throwable>
{
    void doIt() throws T;
}

<T extends Throwable> void invoke(Action<T> action) throws T
{
    action.doIt(); // throws T
}    

Wenn wir mit einem leeren Lambda aufrufen, was würde T abgeleitet werden als?

    invoke( ()->{} ); 

Die einzige Einschränkung auf T ist eine Obergrenze Throwable. In einer früheren Phase von Java8, T=Throwable würde gefolgert werden. Sieh dir das an Bericht Ich reichte ein.

Aber das ist ziemlich albern, darauf zu schließen Throwable, eine überprüfte Ausnahme, aus einem leeren Block. In dem Bericht wurde eine Lösung vorgeschlagen (die anscheinend von JLS übernommen wird) –

If E has not been inferred from previous steps, and E is in the throw clause, 
and E has an upper constraint E<<X,
    if X:>RuntimeException, infer E=RuntimeException
    otherwise, infer E=X. (X is an Error or a checked exception)

dh wenn die obere Grenze ist Exception oder Throwablewählen RuntimeException als Lösung. In diesem Fall dort ist ein guter Grund, einen bestimmten Untertyp der Obergrenze zu wählen.

  • Was ist die Bedeutung von X:>RuntimeException in Ihrem letzten Beispiel-Snippet?

    – marsouf

    15. Oktober 2018 um 16:32 Uhr

  • @marsouf – X hat eine untere Grenze von RuntimeException.

    – ZhongYu

    10. Oktober 2021 um 22:24 Uhr

Mit sneakyThrowder Typ T ist eine begrenzte generische Typvariable ohne einen bestimmten Typ (weil es keinen gibt, woher der Typ kommen könnte).

Mit nonSneakyThrowder Typ T ist vom gleichen Typ wie das Argument, also in Ihrem Beispiel die T von nonSneakyThrow(e); ist Exception. Wie testSneaky() erklärt keinen Wurf Exceptionwird ein Fehler angezeigt.

Beachten Sie, dass dies eine bekannte Interferenz von Generics mit geprüften Ausnahmen ist.

  • So für sneakyThrow es wird nicht wirklich auf einen bestimmten Typ gefolgert, und die “Umwandlung” bezieht sich auf einen solchen undefinierten Typ? Ich frage mich, was damit eigentlich passiert.

    – Marko Topolnik

    9. Juli 2015 um 12:00 Uhr

1247310cookie-checkEine besondere Funktion der Ausnahmetypinferenz in Java 8

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy