Особенность вывода типов исключений в Java 8

При написании кода для другого ответа на этом сайте я столкнулся с этой особенностью:

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 вызов? Они кажутся очень похожими.

3 ответа

Решение

Т из sneakyThrow предполагается, что RuntimeException, Это можно сделать из спецификации языка на вывод типа ( http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html)

Во-первых, в разделе 18.1.3 есть примечание:

Граница формы throws α носит чисто информационный характер: он направляет разрешение для оптимизации создания экземпляра α, так что, если возможно, это не проверенный тип исключения.

Это ни на что не влияет, но указывает на раздел "Разрешение" (18.4), в котором содержится дополнительная информация о предполагаемых типах исключений в особом случае:

... В противном случае, если связанный набор содержит throws αiи правильные верхние границы αi не более Exception, Throwable, а также Objectтогда Ti = RuntimeException,

Это дело относится к sneakyThrow - единственная верхняя граница Throwable, так T предполагается, что RuntimeException согласно спецификации, поэтому он компилируется. Тело метода нематериально - непроверенное приведение завершается успешно во время выполнения, потому что на самом деле этого не происходит, оставляя метод, который может победить проверенную во время компиляции систему исключений.

nonSneakyThrow не компилируется как этот метод T имеет нижнюю границу Exception (т.е. T должен быть супертипом Exception, или же Exception сам), который является проверенным исключением, из-за типа, с которым он вызывается, так что T выводится как Exception,

Если вывод типа создает единственную верхнюю границу для переменной типа, обычно в качестве решения выбирается верхняя граница. Например, если T<<Number решение T=Number, Хотя Integer, Float и т. д. также могут удовлетворить ограничения, нет веских причин для их выбора Number,

Это также относится и к throws T в Ява 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 быть выведен как?

    invoke( ()->{} ); 

Единственное ограничение на T верхняя граница Throwable, На ранней стадии Java8, T=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 как решение. В этом случае есть веская причина выбрать определенный подтип верхней границы.

С sneakyThrowтип T является ограниченной переменной универсального типа без определенного типа (потому что нет никакого места, откуда мог бы прийти тип).

С nonSneakyThrowтип T тот же тип, что и аргумент, поэтому в вашем примере T из nonSneakyThrow(e); является Exception, Как testSneaky() не объявляет бросок Exception, ошибка показана.

Обратите внимание, что это известное вмешательство Generics с проверенными исключениями.

Другие вопросы по тегам