Jak wywołać wyjątek bez dodawania throwsdo sygnatury metody bądź też bez opakowywania w try ... catch?
Załóżmy, że mamy metodę pozwalającą na zrzucenie dowolnego wyjątku:
private static void throwException(Throwable e) throws Throwable {
throw e;
}
Jeśli użyjemy metody throwException to musimy pamiętać o obsłużeniu wyjątku poprzez przekazanie go dalej – throwsdo nagłówka metody:
public static void doSomethingAndThrowException() throws Throwable {
throwException(new IOException());
}
lub poprzez opakowanie:
public static void doSomethingAndSourroundException() {
try {
throwException(new IOException());
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
Na pomoc przychodzi koncepcja sneaky throws, polegająca na wykorzystaniu mechanizmu wnioskowania typów.
public static <E extends Throwable> void sneakyThrow(Throwable e) throws E {
throw (E) e;
}
Teraz możemy wywołać wyjątek bezkarnie
public static void doSomethingSneaky() {
sneakyThrow(new IllegalArgumentException("Wrong argument"));
}
W projekcie Lombok znjadziesz użyteczną adnotacje @SneakyThrows, która realizuje tę koncepcję. Poniżej przykład użycia:
@SneakyThrows
public static void doSomethingAndUseLombookAnnotation() {
throwException(new IOException());
}
Musisz pamiętać, że korzystając ze @SneakyThrows stracisz możliwość złapania wyjątku:
try {
ThrowsSneaky.doSomethingAndUseLombookAnnotation();
} catch (Exception e) {
System.out.println(e.getMessage());
}
Na wyjściu nie znajdziesz informacji o wyjątku lecz null.
Gdzie korzystać ze sneaky throws? Spójrz na poniższą klasę:
public class SleepAndPrintThread extends Thread {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Metoda sleep z java.util.concurent.TimeUnit wymaga obsłużenia wyjątku InterruptedException.
Poniżej kod z wykorzystaniem @SneakyThrows
public class SleepAndPrintThread extends Thread {
@SneakyThrows
@Override
public void run() {
TimeUnit.SECONDS.sleep(3);
}
}
