Osobliwa cecha wnioskowania typu wyjątku w Javie 8
Podczas pisania kodu do innej odpowiedzi na tej stronie natknąłem się na tę osobliwość:
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;
}
Po pierwsze, jestem dość zdezorientowany, dlaczego wywołanie sneakyThrow
jest OK dla kompilatora. Jaki typ można wywnioskować dla T
, gdy nigdzie nie ma wzmianki o niezaznaczonym typie wyjątku?
Po Drugie, przyjmując, że to działa, dlaczego kompilator skarży się na wywołanie nonSneakyThrow
? Wydają się bardzo podobne.
3 answers
T z sneakyThrow
przyjmuje się jako RuntimeException
. To może być wykonane z langauge spec na typ wnioskowania ( http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html )
Po pierwsze, jest notatka w sekcji 18.1.3:
Wiązanie formularza
throws α
ma charakter czysto informacyjny: kieruje rozdzielczość w celu optymalizacji instancji α tak, aby, jeśli to możliwe, nie był to sprawdzony typ wyjątku.
To nie ma wpływu na nic, ale wskazuje nam na w sekcji rozdzielczość (18.4), która zawiera więcej informacji na temat wnioskowanych typów wyjątków ze szczególnym przypadkiem:
... W przeciwnym razie, jeśli zbiór bound zawiera
throws αi
, a właściwe górne granice ai są co najwyżej,Exception
,Throwable
, iObject
, Następnie Ti =RuntimeException
.
Ten przypadek dotyczy sneakyThrow
- jedyną górną granicą jest Throwable
, więc T
wnioskuje się jako RuntimeException
zgodnie ze specyfikacją, więc kompiluje. Ciało metody jest niematerialne - niezaangażowany odlew udaje się w czasie wykonywania ponieważ tak się nie dzieje, pozostawiając metodę, która może pokonać system wyjątków sprawdzonych w czasie kompilacji.
nonSneakyThrow
nie kompiluje się, ponieważ T
ma dolną granicę Exception
(tzn. T
musi być supertyp Exception
, lub sama Exception
), co jest wyjątkiem sprawdzonym, ze względu na typ, z którym jest wywoływana, tak że T
jest wnioskowana jako Exception
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-09 12:32:18
Jeśli wnioskowanie typu tworzy pojedynczą górną granicę dla zmiennej typu, zazwyczaj górna granica jest wybierana jako rozwiązanie. Na przykład, jeśli T<<Number
, rozwiązaniem jest T=Number
. Chociaż Integer
, Float
itd. może również zaspokoić ograniczenia, nie ma dobrego powodu, aby wybrać je zamiast Number
.
Tak było również w przypadku throws T
w Javie 5-7: T<<Throwable => T=Throwable
. (Sneaky throw solutions all had explicit <RuntimeException>
type arguments, otherwise <Throwable>
is conferred.)
W java8, z wprowadzeniem lambda, to staje się problematyczne. Rozważmy ten przypadek
interface Action<T extends Throwable>
{
void doIt() throws T;
}
<T extends Throwable> void invoke(Action<T> action) throws T
{
action.doIt(); // throws T
}
Jeśli wywołamy pustą lambdę, to co T
można wywnioskować jako?
invoke( ()->{} );
Jedynym ograniczeniem na T
jest górna granica Throwable
. We wcześniejszym etapie java8, T=Throwable
można by wywnioskować. Zobacz ten raport , który złożyłem.
Ale to jest dość głupie, wywnioskować Throwable
, sprawdzony wyjątek, z pustego bloku. W sprawozdaniu zaproponowano rozwiązanie (które najwyraźniej zostało przyjęte przez JLS) -
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)
Tj. Jeśli górna granica wynosi Exception
lub Throwable
, wybierz RuntimeException
jako rozwiązanie. W tym przypadku istnieje jest dobry powód, aby wybrać konkretny Podtyp górnej granicy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-10 01:48:50
Z sneakyThrow
, typ T
jest ograniczoną zmienną typu ogólnego bez określonego typu(ponieważ nie ma skąd Typ może pochodzić).
Z nonSneakyThrow
, typ T
jest tym samym typem co argument, więc w twoim przykładzie T
z nonSneakyThrow(e);
jest Exception
. Ponieważ testSneaky()
nie deklaruje rzuconego Exception
, wyświetlany jest błąd.
Zauważ, że jest to znana interferencja leków generycznych z zaznaczonymi wyjątkami.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-09 11:54:34