При написании кода для другого ответа на этом сайте я обнаружил такую особенность:
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;
}
Во-первых, я совершенно не понимаю, почему sneakyThrow
компилятор работает с вызовом. Какой возможный тип он сделал, T
если нигде не упоминается непроверенный тип исключения?
Во-вторых, если согласиться с тем, что это работает, почему тогда компилятор жалуется на nonSneakyThrow
вызов? Они кажутся очень похожими.
источник
sneakyThrow
звонок. Особых правил выводаthrows T
форм не существует в спецификации Java 7.nonSneakyThrow
,T
должно бытьException
, а не «супертипом»Exception
, потому что это в точности тот тип аргумента, который был объявлен во время компиляции на сайте вызова.Exception
и верхняя границаThrowable
, поэтому наименьшая верхняя граница, которая является результирующим предполагаемым типом, равнаException
.Exception
фраза «или сам» может быть полезна для читателя, но в целом следует отметить, что в спецификации всегда используются термины «подтип» и «супертип» в смысле «включая себя»…Если вывод типа дает единственную верхнюю границу для переменной типа, обычно в качестве решения выбирается верхняя граница. Например, если
T<<Number
, решение естьT=Number
. ХотяInteger
иFloat
т. Д. Также могут удовлетворять ограничению, нет веских причин выбирать ихNumber
.Это было также в случае
throws T
в Java 5-7:T<<Throwable => T=Throwable
. (Все решения скрытого броска имеют явные<RuntimeException>
аргументы типа, иначе<Throwable>
предполагается.)В java8 с введением лямбда это становится проблематичным. Рассмотрим этот случай
interface Action<T extends Throwable> { void doIt() throws T; } <T extends Throwable> void invoke(Action<T> action) throws T { action.doIt(); // throws T }
Если мы вызываем с пустой лямбдой, что будет
T
выводиться как?Единственное ограничение
T
- это верхняя границаThrowable
. На более ранней стадии java8T=Throwable
предполагалось. См. Этот отчет, который я подал.Но довольно глупо делать вывод
Throwable
о проверенном исключении из пустого блока. Решение было предложено в отчете (который, по-видимому, принят 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)
т.е. если верхняя граница равна
Exception
илиThrowable
, выберитеRuntimeException
в качестве решения. В этом случае, это хороший повод , чтобы выбрать конкретный подтип верхней границы.источник
X:>RuntimeException
в вашем последнем фрагменте примера?При
sneakyThrow
типеT
является ограниченная переменная универсального типа без определенного типа (потому что нет места, откуда тип мог бы появиться).With
nonSneakyThrow
, типT
того же типа, что и аргумент, поэтому в вашем примереT
ofnonSneakyThrow(e);
isException
. ПосколькуtestSneaky()
не объявляет бросокException
, отображается ошибка.Обратите внимание, что это известное вмешательство Generics с отмеченными исключениями.
источник
sneakyThrow
самом деле он не относится к какому-то конкретному типу, а «приведение» относится к такому неопределенному типу? Интересно, что на самом деле с этим происходит.