Gotowi na dalsze wyjaśnienia związane z tym samym artykułem? Został jeszcze jeden element. Pamiętacie dopisek "throws" w nagłówku metody? Jest to nieco rzadziej omawiany element a szkoda, bo równie przydatny co samo przechwytywanie wyjątków. Obsługa wyjątków to jedno, a "przerzucenie" tej odpowiedzialności na metodę wywołującą za pomocą słowa kluczowego "throws" w języku Java, to zupełnie inna bajeczka.
Tweet |
"THROWS" W JĘZYKU JAVA TO "PRZERZUCENIE" ODPOWIEDZIALNOŚCI
Nowe słowo kluczowe do Waszego arsenału, tym razem to "throws". Jak zdążyłem już napisać jest ono umieszczane w nagłówku metody, po zdefiniowaniu ewentualnych parametrów formalnych. Przypomnę dla porządku:
void doSomething([parametry formalne]) throws [wyjątki, które mogą zgłosić "ryzykowne" metody] {
// instrukcje wraz z metodami mogącymi zgłosić wyjątek, już bez "try-catch"!
}
Chcę zaznaczyć, że "throws" a "throw" to są dwa odmienne słowa kluczowe! Obsługa wyjątków wymaga, aby po zawarciu słowa "throws" podać nazwę wyjątku, który może zostać zgłoszony przez metodę określaną jako "ryzykowna". Ryzykowna to taka, która wykonuje operacje mogące się nie powieść. Musicie jeszcze wiedzieć, że bezzasadne użycie słowa "throws" w języku Java spowoduje błąd kompilacji. Nie potraktuje tego jako bezużyteczność, tylko jak błąd składniowy. Wewnątrz takiej metody wprowadzacie już same instrukcje bez jakiegokolwiek przechwytywania wyjątków! Żadne "try-catch" nie jest potrzebne (chyba, że dotyczy ono takich wyjątków, które nie zostały zdefiniowane po "throws").
OK, tylko skąd macie wiedzieć jakie nazwy podać tych wyjątków? Żeby nie tracić czasu na pierdoły, sugerowałbym użycie IDE, które automatycznie podpowie co i jak należy wstawić, a jeśli nie wchodzi to w grę, to tylko dokumentacja. Jeśli nie chcecie się bawić w różnego rodzaju wyodrębnianie jednych przypadków od drugich, to możecie również wstawić samo "Exception", tylko wtedy będzie wszystko brać jak leci, więc będzie kłopot z odróżnianiem typów celem późniejszego zapobiegania na poszczególne z nich. Być może powinienem to napisać na samym początku, ale wyjątki w Javie to także obiekty!
A tak na marginesie, zastosowanie tego słowa pozwala na "przeniesienie" klauzuli "try-catch" do metody wywołującej. To jest tak, jakbyście "wydłubali" treść po słowie kluczowym "try" i włożyli do metody. Obsługa wyjątków pozwala na taki manewr. Kłopot w tym, że nie wszyscy o nim wiedzą...
PRZYKŁAD KODU ŹRÓDŁOWEGO
Aby już nie utrudniać, przedstawię teraz program z absurdalnym działaniem w postaci dzielenia przez zero w celu sprowokowania wyjątku, który tutaj akurat nosi nazwę "ArithmeticException". Cała operacja zostanie zawarta w osobnej metodzie z deklaracją "throws" w języku Java dla celów dydaktycznych. Obsługa wyjątków też może zaskakiwać o czym przekonacie się za chwilę:
KLASA "MAIN"
public class Main {
public static void main(String[] args) {
new IllegalOperation();
}
}
KLASA "ILLEGALOPERATION"
public class IllegalOperation {
public IllegalOperation() {
try {
divideByZero();
} catch (ArithmeticException e) {
System.out.println("Nie wolno dzielić przez zero!");
}
}
private void divideByZero() throws ArithmeticException {
int x = 12 / 0;
}
}
"throws" w języku Java jest wykorzystywane w metodzie "divideByZero", gdybyście nie mogli znaleźć głównego bohatera dzisiejszego artykułu. Próba policzenia absurdalnego wyrażenia skończy się zgłoszeniem wyjątku, który wewnątrz naszej metody spowoduje "poskarżenie się" z tym fantem do metody, która ją wywołała (blok "try-catch"). Dalej już wiecie co się stanie.
Mam dla Was ciekawostkę. Możecie uwierzyć lub nie, ale ten kod da się skompilować nawet bez użycia klauzuli "try-catch"! Jakim cudem? Takim, że "ArithmeticException" jest jednym z wyjątkowych wyjątków (nie ma to jak masło maślane), które należą do typu "RuntimeException" (wyjątek podczas wykonywania programu). Oznacza to, że kompilator nie wymaga od nas przewidywania zgłoszenia wyjątku, jeżeli dotyczy on typu "RuntimeException", gdyż nie ma pewności, że może wystąpić jakieś niebezpieczeństwo. "Kwiatki", które wychodzą podczas działania programu, mają typ "RuntimeException" wynikają zazwyczaj z niedopatrzeń programisty, a nie z ryzyka niepowodzenia jakiejś operacji, stąd nie wymagają obsługiwania. Widzicie jak bardzo sama obsługa wyjątków może być zagmatwana?
PRZERZUCANIE ODPOWIEDZIALNOŚCI W METODZIE "MAIN"?
Czy przyszła Wam do głowy jedna okoliczność związana ze słowem kluczowym "throws" w języku Java? Obsługa wyjątków kryje jeszcze jedną tajemnicę. Jak wspomniałem, "throws" "przerzuca" odpowiedzialność obsłużenia zgłoszonego wyjątku metodzie wywołującej. A co w przypadku gdy "main" posiada taką definicję? Jakie konsekwencje mogą z tego wyniknąć?
Gdy poznałem zastosowanie słowa kluczowego "throws", też nie myślałem o tym w taki sposób jaki efekt może to przynieść, jeżeli podstawimy to do funkcji od której się wszystko zaczyna, do funkcji "main". Któregoś dnia pozwoliłem sobie zrobić eksperyment. I jak myślicie, co się stało w przypadku wystąpienia wyjątku, przerzucenia go do "main", a funkcja "main" też postanowiła przerzucić ten wyjątek nie wiadomo w czyją stronę?
Wyskoczył komunikat?
Wyjątek został zignorowany?
Wybuchł komputer?
Hmmm...choć rzeczywiście zostawienie tego w takiej postaci jest błędem kardynalnym, to Java ochroni nas przed jakimikolwiek konsekwencjami. Sprawdźmy to sami prowokując wyjątek zgodnie z powyżej opisaną sytuacją.
KOD ŹRÓDŁOWY I SPROWOKOWANIE WYJĄTKU
Obsługa wyjątków lubi zaskakiwać nieoczekiwanymi skutkami naszych działań. Skopiujcie i skompilujcie sobie poniższy program:
KLASA "MAIN"
public class Main {
public static void main(String[] args) throws ArithmeticException {
new IllegalOperation();
}
}
KLASA "ILLEGALOPERATION"
public class IllegalOperation {
public IllegalOperation() throws ArithmeticException {
int x = 12 / 0;
}
}
Nawet z takiej rzeczy możemy wyciągnąć cenny wniosek. Zauważyliście, że konstruktory również mogą mieć klauzulę "throws" w języku Java? Obsługa wyjątków pozwala na to jak najbardziej. W każdym razie, uruchomcie i zobaczcie co się stanie. Funkcja "main" nie mając komu przerzucić zgłoszonego wyjątku po prostu...zakończy program! Tak samo "brutalnie" jak w języku C byśmy dopuścili się naruszenia ochrony pamięci. Pstryk i aplikacji nie ma!
W konsoli to może być niezauważalne, aczkolwiek jakbyście programowali graficzny interfejs użytkownika, spróbujcie wtedy to wywołać, a okno zamknie się Wam automatycznie "bez słowa wyjaśnienia". To akurat jedynie tytułem ciekawostki. Może to nie jest nic ekscytującego, aczkolwiek warto wiedzieć co się dzieje w sytuacji postawienia funkcji "main" w niekorzystnej sytuacji. Obsługa wyjątków nie może wtedy zostać przeprowadzona.
To było jedynie pokazanie Wam skutków ubocznych. Zaprezentowane działania są jak najbardziej karygodne i nie świadczą o Waszym profesjonalizmie. Nie próbujcie tego robić w domu (a w pracy to w szczególności)!
Dziękuję po raz trzeci dzisiaj za uwagę. Tu znajdziecie czwarty artykuł z rzędu przekraczając granice swoich możliwości.