Wyjątki, rzutowanie typów, operator instanceOf Małgorzata Tomczyk referat z JTP prowadzący: mgr inż. Dominik Radziszowski Wyjątki Błędy w programach Kiedy piszemy program nie zawsze możemy być stuprocentowo pewni, że podczas jego wykonywania nie wystąpią jakieś nieoczekiwane błędy, jak na przykład odwołanie się do nieistniejącego pliku lub dzielenie przez 0. Aby ustrzec nasz program przed możliwymi błędami często jesteśmy zmuszeni dodać do niego mnóstwo instrukcji warunkowych i wartowników, co zazwyczaj zmniejsza czytelność kodu. Czy zatem istnieje jakaś rozsądna alternatywa? Wyjątki Java posiada zapożyczony z języka Ada mechanizm informowania o błędach: wyjątki (ang. exceptions). Mechanizm obsługi wyjątków w Javie umożliwia zaprogramowanie "wyjścia" z takich sytuacji krytycznych, dzięki czemu program nie zawiesi się po wystąpieniu błędu wykonując ciąg operacji obsługujących wyjątek Rodzaje wyjątków W Javie wyróżniamy dwa rodzaje wyjątków: •wyjątki kontrolowane (ang. checked exeptions)– stanowią większość wyjątków w Javie. Znaczy to, że kompilator jest w stanie sprawić, że każda metoda zgłasza tylko te wyjątki, które są jawnie wymienione w jej deklaracji. •wyjątki niekontrolowane (ang. unchecked exeptions) – standardowe wyjątki i błędy zgłaszane przez system wykonawczy, które są podklasami klas RuntimeExeption i Error. Klasa Throwable Wszystkie wyjątki, jakie mogą wystąpić w programie muszą być podklasą klasy Throwable. Poniższy rysunek pokazuje hierarchię dziedziczenia klasy Throwable i jej najważniejszych podklas. Użyteczne metody klasy Throwable •String getMessage() - zwraca napis podany kontruktorowi wyjątku Exception •String toString() - zwraca napis reprezentujący nazwę wyjątku (klasy wyjątku) •void printStackTrace() - wypisuje call stack trace czyli sekwencję metod, która została wywołana do momentu wyrzucenia wyjątku Więcej informacji: http://java.sun.com/j2se/1.3/docs/api/java/lang/Throwable.html Klasa Error Wyjątki typu Error występują wtedy, gdy wystąpi sytuacja specjalna w maszynie wirtualnej (np. błąd podczas linkowania). Wyjątki tego typu nie powinny być obsługiwane w "zwykłych" programach Javy. Więcej informacji: http://java.sun.com/j2se/1.3/docs/api/java/lang/Error.html Klasa Exception W większości programów generowane są i obsługiwane obiekty, które dziedziczą z klasy Exception. Wyjątek tego typu oznacza, że w programie wystąpił błąd, lecz nie jest to poważny błąd systemowy. Więcej informacji: http://java.sun.com/j2se/1.3/docs/api/java/lang/Exception.html Klasa RuntimeException Szczególną podklasę klasy Exception stanowią wyjątki, które występują podczas wykonywania programu, są to wyjątki typu RunTimeExceptions (i jej podklas np.: NullPointerException, ClassCastException, IllegalThreadStateException i ArrayOutOfBoundsException) i występują np.: wtedy, gdy zostaną wyczerpane zasoby systemowe, nastąpi odwołanie do nie istniejącego elementu tablicy i inne. Więcej informacji: http://java.sun.com/j2se/1.3/docs/api/java/lang/RuntimeExcept ion.html Deklarowanie typów wyjątków Czasami do opisu sytuacji wyjątkowych wygodnie jest mieć więcej danych niż tylko napis, który udostępnia klasa Exception. W takich wypadkach możemy utworzyć podklasy klasy Exception, które będą zawierały dodatkowe dane (zwykle ustawione przez konstruktor). Przykład: Załóżmy, że chcemy zgłosić wyjątek StudentNieZdalEgzaminu. Zgłaszany wyjątek powinien zawierać informacje który student nie zdał egzaminu i z czego. public class StudentNieZdalEgzaminu extends Exeption{ public Student student; public String przedmiot; StudentNieZdalEgzaminu(Student nieszczesny, String przedm){ super("Student " + nieszczesny.nazwisko+ "nie zdal " + przedm); student=nieszczesny; przedmiot=przedm; } } Instrukcja throw i klauzula throws instrukcja throw – służy do zgłaszania wyjątków. Wymaga podania obiektu reprezentującego wątek. klauzula throws - w tej klauzuli podaje się wyjątki kontrolowane przez daną metodę jako listę nazw oddzielonych przecinkami nazwaMetody(zmienne) throws Wyjatek1, Wyjatek2 { instrukcje; ... throw Wyjatek1; ... } Przykład użycia instrukcji throw i klauzuli throws public void przeprowadzEgzamin(Student student, String przedmiot) throws StudentNieZdalEgzaminu { if(student.ocena==2) throw StudentNieZdalEgzaminu(student, przedmiot); else student.wpiszDoIndexu(przedmiot, student.ocena); } Blok try oraz klauzule catch i finally Wyjątki można wychwytywać za pomocą bloków try. Składnia bloku try jest następująca: try blok catch(typ_wyjatku id) blok catch(typ_wyjatku id) blok ... finally blok Treść instrukcji try jest wykonywana do chwili zgłoszenia wyjątku lub do szczęśliwego jej zakończenia. Jeśli zostaje zgłoszony wyjątek, to kolejne klauzule catch są przeszukiwane w celu znalezienia takiej, która wymienia klasę zgłoszonego wyjątku, bądź którąś z jej nadklas. Jeżeli klauzuli nie uda się znaleźć, to wyjątek jest propagowany na zewnątrz instrukcji try do otaczających instrukcji try, w których może być obsłużony. Instrukcja try może zawierać dowolną liczbę klauzul catch (np. ani jednej). Wyjątek, który nie został wychwycony jest, jest zgłaszany w miejscu wywołania danej metody. Jeżeli instrukcja try zawiera klauzulę finally to jest ona wykonywana po wszystkich czynnościach związanych z obsługą klauzul catch. Jest ona wykonywana zawsze bez względu na to, czy w bloku try zostały zgłoszone jakieś wyjątki, czy też nie i czy zostały one obsłużone. Przykład użycia bloku try oraz klauzul catch i finally try{ przeprowadzEgzamin(gotom,JTP); } catch(StudentNieZdalEgzaminu e){ System.out.println("Trudno, może następnym razem"); } finally{ System.out.println("Dziękuję, panie doktorze, do widzenia!"); } Kiedy używać wyjątków Wybór sytuacji, w których należy zgłosić wyjątek, nie podgala żadnym sztywnym regułom. Należy jednak pamiętać o tym, aby nie nadużywać wyjątków jako metody zgłaszania sytuacji normalnych i oczekiwanych. Konwersje typów Konwersje typów Java jest językiem ze ścisłą kontrolą typów, to znaczy, że w czasie kompilacji jest wykonywana kontrola zgodności typów dla niemal wszystkich wyrażeń. Java nie pozawala na przypisania wartości niewłaściwego typu przez odrzucenie wszystkich potencjalnie niepoprawnych fragmentów programów i wymaga użycia operatorów konwersji typu w tych przypadkach, gdy zgodność typów można sprawdzić tylko w czasie wykonywania programu. W Javie posługujemy się dwoma rodzajami konwersji typów: 1. Konwersją niejawną 2. Konwersją jawną Konwersje niejawne Konwersje niejawne, to takie, które odbywają się automatycznie, bez konieczności ingerencji programisty. Są dwa rodzaje konwersji niejawnych: 1. Konwersja typów prostych – każdą wartość liczbową można przypisać zmiennej, której typ obejmuje szerszy zakres wartości. 2. Konwersja wartości całkowitoliczbowych na zmiennopozycyjne – nie wiąże się ze stratą zakresu. Konwersja niejawna w drugą stronę jest niemożliwa Konwersje jawne Gdy wartość jednego typu nie może być bezpośrednio przypisana zmiennej innego typu, zachodzi często możliwość użycia jawnej konwersji typów (rzutowania). Operator konwersji powoduje utworzenie wartości nowego typu stanowiącej jak najdokładniejszą reprezentację wartości starego typu. Konwersje jawne różnych typów •boolean na int – niedozwolona •double na long – część ułamkowa odrzucana przez zaokrąglenie w kierunku 0 •double na float – może spowodować utratę dokładności •typy całkowitoliczbowe – konwersja przez obcięcie najstarszych bitów. Może spowodować b. dużą zmianę wartości •char na czałkowitoliczbową – przez wypełnienie starszych 16 bitów zerami •całkowitoliczbowa na char – używa się najmniej znaczących 16 bitów, pozostałe - ignorowane Konwersje jawne referencji Jawne konwersje mogą być również stosowane w przypadku referencji. O ile obiekt podtypu może być użyty wszędzie tam, gdzie obiekt jego nadtypu, o tyle przeciwne stwierdzenie nie jest zwykle prawdziwe. Możliwe są dwie konwersje referencji 1. konwersja w dół (zwężanie typu) – rzutowanie nadtypu na podtyp – nie zawsze poprawna 2. konwersja w górę (rozszeżenie typu) – rzutowanie podtypu na nadtyp – zawsze poprawna Wyjątek ClassCastExeption Dla przykładu załóżmy, że mamy trzy klasy: klasę Coffee i dziedziczące po niej klasy: Mocca i Latte. Jeżeli zatem dokonamy konwersji: Mocca fancy=(Mocca)joe; to nie będzie ona zawsze poprawna, ponieważ joe niekoniecznie wskazuje na obiekt typu Mocca. Jeżeli tak nie jest zgłoszony zostanie wyjątek ClassCastExeption. Aby uniknąć kłopotów moglibyśmy go wyłapać i obsłużyć, ale takie rozwiązanie uznawane jest za nieeleganckie Instrukcja instanceOf Aby uniknąć rozwiązania tej sytuacji przez wyjątki możemy użyć instrukcji instanceOf. Wywołanie objekt instanceOf Klasa zwraca wartość true jeżeli konwersja „obiekt” do klasy (albo interface’u) „Klasa” udałaby się. Przy pomocy tej instrukcji można przykładową konwersję przeprowadzić tak: if(joe instatceOf Mocca) Mocca fancy = (Mocca)joe; Bibliografia • Ken Arnold, James Gosling „Java TM”, WNT, Warszawa ’99 • http://www.javasoft.pl/java/ • http://www.weia.po.opole.pl/kaeii/ • http://java.sun.com/j2se/1.3/docs/api/