Czy wiesz, po co stosuje się @SneakyThrows z biblioteki Lombok?
Wyjątki w Javie dzielą się na checked exceptions
oraz unchecked exceptions
. Unchecked exception reprezentuje błąd w logice programu, który może wystąpić w dowolnym miejscu - przykładowo odwołanie się do nieistniejącego elementu tablicy spowoduje rzucenie wyjątku ArrayIndexOutOfBoundsException
. Kompilator nie jest w stanie przewidzieć błędów logicznych, które pojawiają się dopiero w czasie wykonywania programu, dlatego nie może sprawdzać tego typu problemów w czasie kompilacji, co sprawia, że wyjątki te nie muszą być obsłużone przez programistę.
Przykład unchecked exception
- dzielenie przez zero wyrzuci wyjątek ArithmeticException
:
private static void divideByZero() {
int numerator = 1;
int denominator = 0;
int result = numerator / denominator;
}
Checked exception
to wyjątek reprezentujący przewidywalną, błędną sytuację, która może wystąpić nawet w przypadku poprawnej logiki programu - przykładowo próba otwarcia pliku, który nie istnieje, spowoduje rzucenie wyjątku FileNotFoundException
. Wyjątki tego rodzaju są weryfikowane w czasie kompilacji, dlatego Java zmusza nas do ich obsługi - albo poprzez słowo kluczowe throws, albo poprzez złapanie wyjątku w bloku try-catch
:
- Przykład obsługi przez słowo kluczowe
throws
- przekazanie wyjątku w dół stosu wywołań:private static void openFile() throws FileNotFoundException { File file = new File("Nieistniejacy_plik.txt"); FileInputStream stream = new FileInputStream(file); }
- Przykład obsługi przez blok
try-catch
- złapanie wyjątku:private static void openFile() { File file = new File("Nieistniejacy_plik.txt"); try { FileInputStream stream = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } }
Koncepcja sneaky throws
Sneaky throws
to koncepcja pozwalająca na rzucenie dowolnego checked exception
bez jego jawnego definiowania w sygnaturze metody. Pozwala ona na ominięcie słowa kluczowego throws oraz imitowanie zachowania unchecked exception
i jest możliwa, ponieważ obsługa wyjątków checked exception
jest wymuszana tylko przez kompilator Javy. W kodzie bajtowym każdy wyjątek może zostać rzucony z dowolnego miejsca i jest traktowany przez JVM tak samo - zostaje on propagowany w dół stosu wywołań. Od Javy 8 każde użycie throws T
, gdzie T
jest typem generycznym rozszerzajacym Throwable
, oznacza, że metoda może rzucić unchecked exception
. Dzięki temu możemy stworzyć metodę pomocniczą, która będzie rzucała wyjątek typu checked
, jednak kompilator nie będzie wymagał jego przechwycenia.
Metoda pomocnicza realizująca koncepcję sneaky throws
:
private static <T extends Throwable> void sneakyThrow(Throwable t) throws T {
throw (T) t;
}
Metodę tę możemy następnie dowolnie wykorzystać w naszym kodzie:
private File getFile(String fileName) {
return null;
}
private void deleteFile(String fileName) {
File file = getFile(fileName);
if (file == null) {
sneakyThrow(new FileNotFoundException("Nie znaleziono pliku"));
}
file.delete();
}
public void tryToDeleteNotExistingFile() {
try {
deleteFile("Nieistniejacy_plik.txt");
} catch (Exception exception) {
exception.printStackTrace();
}
}
Warto zauważyć, że w takiej sytuacji w metodzie tryToDeleteNotExistingFile()
nie możemy złapać już wyjątku FileNotFoundException
, ponieważ nie jest on zadeklarowany - możemy jedynie ratować się złapaniem bardziej ogólnego Exception
.
Lombok - adnotacja @SneakyThrows
Biblioteka Lombok
udostępnia adnotację @SneakyThrows
, która wykorzystuje powyższą sztuczkę i dzięki oszukaniu kompilatora pozwala rzucać checked exception
bez deklarowania tego w sygnaturze metody.
Przykład - użycie adnotacji @SneakyThrows
:
private File getFile(String fileName) {
return null;
}
@SneakyThrows(FileNotFoundException.class)
private void deleteFile(String fileName) {
File file = getFile(fileName);
if (file == null) {
throw new FileNotFoundException("Nie znaleziono pliku");
}
file.delete();
}
public void tryToDeleteNotExistingFile() {
try {
deleteFile("Nieistniejacy_plik.txt");
} catch (Exception exception) {
exception.printStackTrace();
}
}
Do adnotacji @SneakyThrows
można przekazać dowolną liczbę wyjątków. Jeśli nie podamy żadnego, to adnotacja ta uwzględni dowolny wyjątek. Należy pamiętać także o tym, że @SneakyThrows
nie dziedziczy.
Kiedy używać?
Dokumentacja @SneakyThrows wspomina o dwóch częstych przypadkach użycia:
- niepotrzebnie rygorystyczne interfejsy takie jak
Runnable
, - “niemożliwe” wyjątki, które nie powinny nigdy być rzucone np. ze względu na specyfikację JVM.
Inną sytuacją, w której można zastanowić się nad użyciem @SneakyThrows
są wyrażenia lambda - użycie tej adnotacji pozwoli zwiększyć czytelność, ponieważ nie będziemy musieli przejmować się łapaniem wyjątków w blok try-catch
. Przykład z użyciem @SneakyThrows
:
@SneakyThrows
private static Instant sneakyParseStringDate(String date) {
return new SimpleDateFormat("yyyy-MM-dd").parse(date).toInstant();
}
public List<Instant> getInstants() {
return List.of("2022-05-18").stream().map(SneakyThrowsExample::sneakyParseStringDate)
.collect(Collectors.toList());
}
Ten sam przykład bez użycia @SneakyThrows:
private static Instant nonSneakyParseStringDate(String date) throws ParseException {
return new SimpleDateFormat("yyyy-MM-dd").parse(date).toInstant();
}
public List<Instant> getInstants() {
return List.of("2022-05-18").stream().map(date -> {
try {
return nonSneakyParseStringDate(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toList());
}
Dokumentacja
-
SENIOR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 14 900 - 20 590 PLN brutto
B2B 19 680 - 27 220 PLN netto -
REGULAR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 11 300 - 15 900 PLN brutto
B2B 14 950 - 21 000 PLN netto -
ZOBACZ WSZYSTKIE OGŁOSZENIA
newsletter
techniczny
Podobne wpisy
Czy wiesz, że w Angular 17 została wprowadzona alternatywa dla *ngFor?
-
SENIOR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 14 900 - 20 590 PLN brutto
B2B 19 680 - 27 220 PLN netto -
REGULAR FULLSTACK DEVELOPER (JAVA + ANGULAR) Poznań (hybrydowo) lub zdalnie UoP 11 300 - 15 900 PLN brutto
B2B 14 950 - 21 000 PLN netto -
ZOBACZ WSZYSTKIE OGŁOSZENIA