Jak wywołać wyjątek bez dodawania throws
do 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 – throws
do 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); } }