Beim Schreiben von Code für eine andere Antwort auf dieser Site 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 der sneakyThrow
Aufruf des Compilers in Ordnung ist. Auf welchen möglichen Typ wurde geschlossen, T
wenn kein ungeprüfter Ausnahmetyp erwähnt wird?
Zweitens, wenn man akzeptiert, dass dies funktioniert, warum beschwert sich der Compiler dann über den nonSneakyThrow
Aufruf? Sie scheinen sich sehr ähnlich zu sein.
quelle
sneakyThrow
Anruf meinen . Die besonderen Bestimmungen über den Rückschluss vonthrows T
Formularen gab es in der Spezifikation von Java 7 nicht.nonSneakyThrow
,T
mussException
nicht "ein Supertyp von" seinException
, da es genau der Typ des Arguments ist, das zur Kompilierungszeit am Aufrufstandort deklariert wurde.Exception
und eine Obergrenze vonThrowable
, so dass die kleinste Obergrenze, die der resultierende abgeleitete Typ ist, istException
.Exception
Ausdruck „oder sich selbst“ mag für den Leser hilfreich sein, aber im Allgemeinen sollte beachtet werden, dass in der Spezifikation immer die Begriffe „Subtyp“ und „Supertyp“ im Sinne von „sich selbst einschließen“ verwendet werden…Wenn die Typinferenz eine einzelne Obergrenze für eine Typvariable erzeugt, wird normalerweise die Obergrenze als Lösung ausgewählt. Zum Beispiel, wenn
T<<Number
die Lösung istT=Number
. ObwohlInteger
,Float
usw. auch die Bedingung erfüllen konnte, gibt es keinen Grund , sie zu wählen , überNumber
.Dies war auch
throws T
in Java 5-7 der Fall :T<<Throwable => T=Throwable
. (Sneaky-Throw-Lösungen hatten alle explizite<RuntimeException>
Typargumente, andernfalls<Throwable>
wird darauf geschlossen.)In Java8 wird dies mit der Einführung von Lambda problematisch. Betrachten Sie diesen Fall
Wenn wir mit einem leeren Lambda anrufen, was würde
T
daraus abgeleitet werden?Die einzige Einschränkung
T
ist eine ObergrenzeThrowable
. In einem früheren Stadium von Java8T=Throwable
würde abgeleitet werden. Siehe diesen Bericht, den ich eingereicht habe.Aber das ist ziemlich albern, um
Throwable
eine überprüfte Ausnahme aus einem leeren Block abzuleiten. In dem Bericht (der offenbar von JLS angenommen wird) wurde eine Lösung vorgeschlagen -dh wenn die Obergrenze
Exception
oder istThrowable
, wählen SieRuntimeException
als Lösung. In diesem Fall gibt es einen guten Grund, einen bestimmten Subtyp der Obergrenze zu wählen.quelle
X:>RuntimeException
in Ihrem letzten Beispiel-Snippet?Mit
sneakyThrow
ist der TypT
eine begrenzte generische Typvariable ohne einen bestimmten Typ (da es keinen Ort gibt, von dem der Typ stammen könnte).Mit
nonSneakyThrow
dem TypT
ist vom gleichen Typ wie das Argument, also in Ihrem Beispiel, dieT
vonnonSneakyThrow(e);
istException
. Da eintestSneaky()
Wurf nicht deklariert wirdException
, wird ein Fehler angezeigt.Beachten Sie, dass dies eine bekannte Störung von Generics mit aktivierten Ausnahmen ist.
quelle
sneakyThrow
es nicht wirklich auf einen bestimmten Typ geschlossen ist und die "Besetzung" auf einen so undefinierten Typ ist? Ich frage mich, was damit eigentlich passiert.