Java SneakyThrow исключений, стирание типа - программирование
Подтвердить что ты не робот

Java SneakyThrow исключений, стирание типа

Может кто-нибудь объяснить этот код?

public class SneakyThrow {


  public static void sneakyThrow(Throwable ex) {
    SneakyThrow.<RuntimeException>sneakyThrowInner(ex);
  }

  private static <T extends Throwable> T sneakyThrowInner(Throwable ex) throws T {
    throw (T) ex;
  }



  public static void main(String[] args) {
    SneakyThrow.sneakyThrow(new Exception());
  }


}

Это может показаться странным, но это не создает исключение cast и позволяет выкинуть проверенное исключение без необходимости объявлять его в сигнатуре или обернуть его в неконтролируемое исключение.

Обратите внимание, что ни sneakyThrow(...), ни main не объявляют ни одно проверенное исключение, но вывод:

Exception in thread "main" java.lang.Exception
    at com.xxx.SneakyThrow.main(SneakyThrow.java:20)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

Этот хак используется в Ломбоке, с аннотацией @SneakyThrow, которая позволяет выдавать проверенные исключения, не объявляя их.


Я знаю, что это имеет какое-то отношение к стиранию типа, но я не уверен, что понимаю каждую часть взлома.


Edit: Я знаю, что мы можем вставить Integer в List<String>, а выделенное исключение исключений - это функция времени компиляции.

При отливке от не общего типа типа List на общий тип типа List<XXX> компилятор выдает предупреждение. Но он менее распространен для приведения к родовому типу, прямо как (T) ex в приведенном выше коде.

Если вы хотите, часть, которая кажется мне странной, заключается в том, что я понимаю, что внутри JVM a List<Dog> и List<Cat> выглядит одинаково, но приведенный выше код означает, что, наконец, мы можем также присвоить значение типа Cat для переменной типа Dog или что-то в этом роде.

4b9b3361

Ответ 1

Если вы скомпилируете его с помощью -Xlint, вы получите предупреждение:

c:\Users\Jon\Test>javac -Xlint SneakyThrow.java
SneakyThrow.java:9: warning: [unchecked] unchecked cast
    throw (T) ex;
              ^
  required: T
  found:    Throwable
  where T is a type-variable:
    T extends Throwable declared in method <T>sneakyThrowInner(Throwable)
1 warning

В основном говоря: "Этот приказ не проверяется во время выполнения" (из-за стирания типа) - поэтому компилятор неохотно предполагает, что вы поступаете правильно, зная, что на самом деле это не будет проверено.

Теперь это только компилятор, который заботится о проверенных и непроверенных исключениях - он вообще не является частью JVM. Итак, как только вы закончите компилятор, вы свободны дома.

Я бы настоятельно советовал вам избегать этого.

Во многих случаях существует "реальная" проверка, когда вы используете дженерики, потому что что-то использует желаемый тип, но это не всегда так. Например:

List<String> strings = new ArrayList<String>();
List raw = strings;
raw.add(new Object()); // Haha! I've put a non-String in a List<String>!
Object x = strings.get(0); // This doesn't need a cast, so no exception...

Ответ 2

он выше кода, похоже, означает, что, наконец, мы также можем присвоить значение типа Cat переменной типа Dog или что-то в этом роде.

Вы должны думать о том, как структурируются классы. T extends Throwable, и вы передаете ему Exception. Это похоже на назначение Dog Animal не Dog - Cat.

У компилятора есть правила, по которым Throwable проверяется, а какие нет, основываясь на наследовании. Они применяются во время компиляции, и можно скомпрометировать компилятор, чтобы вы могли исключить исключение проверки. Во время выполнения это не влияет.


Проверенные исключения - это функция времени компиляции (например, генерические файлы)

BTW Throwable также является проверенным исключением. Если вы его подклассом, он будет проверяться, если он не является подклассом Error или RuntimeException.

Два других способа бросить проверенные исключения, если компилятор не знает, что вы это делаете.

Thread.currentThread().stop(throwable);

Unsafe.getUnsafe().throwException(throwable);

Единственное различие заключается в том, что оба используют собственный код.

Ответ 3

С Java 8 вспомогательный метод sneakyThrowInner больше не требуется. sneakyThrow может быть записана как:

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

См. "Специфическая особенность вывода типа исключения в Java 8".

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

Примечание. Функция sneakyThrow объявляется как возвращаемая RuntimeException, поэтому ее можно использовать следующим образом:

int example() { 
   if (a_problem_occurred) {
      throw sneakyThrow(new IOException("An I/O exception occurred"));
      // Without the throw here, we'd need a return statement!
   } else {
      return 42;
   }
}

Отбрасывая RuntimeException, возвращаемый sneakyThrow, компилятор Java знает, что этот путь выполнения завершается. (Конечно, sneakyThrow сам не возвращается.)