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
Jak wykryć i naprawić błędne konfiguracje w działającym klastrze Kubernetes
-
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