Sneaky throws

  • Post author:
  • Post category:Java

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);
    }

}