Programowanie obiektowe III. Refleksja Małgorzata Prolejko OBI JA16Z03
Plan Klasa Class. Analiza funkcjonalności klasy. Podstawy obsługi wyjątków. Zastosowanie refleksji do analizy obiektów. Wywoływanie dowolnych metod.
Refleksja Refleksja/odbicie to sposób programowania, w którym kod wykonywalny zależny jest od stanu programu. W trakcie wykonywania programu analizowane są nazwy zmiennych i funkcji, a algorytmy dostosowane są do różnych typów danych. Dzięki refleksji można tworzyć nowe schematy zachowania języka, operować na kodzie tak jak na danych, bądź korzystać z klas nieznanych w trakcie tworzenia programu. Refleksja służy np. diagnostyce, obsłudze wyjątków, dynamicznemu rozszerzaniu możliwości programu. http://docs.oracle.com/javase/tutorial/reflect/
Refleksja Reflaksja korzysta ze specjalnych klas z pakietu java.lang.reflect.*: Class przechowuje informacje o klasie i jej strukturze AccessibleObject klasa dostępowa do struktury klasy, nadklasa dla Field przechowuje informacje o polach klasy Method przechowuje informacje o metodach klasy Constructor przechowuje informacje o konstruktorach Modifier klasa dostarczająca statycznych metod odczytywania informacji o polach i metodach Proxy klasa dostarczająca statycznych metod tworzenia klas proxy oraz klasa bazowa dla wszystkich dynamicznych klas proxy.
Klasa Przechowywanie informacji o typie danego obiektu (a nie o jego stanie) ma miejsce w obiektach klasy Class. Czytelnik c = new Czytelnik(); Class kl = c.getclass(); przechowuje obiekt i informacje o obiekcie System.out.println(kl.getName()); //Czytelnik przechowuje informacje o typie obiektu Informacje o typie można pobrać znając nazwę klasy docelowej, Class kl = Class.forName("Czytelnik"); lub skrótowo (co działą też dla typów prostych). Class kl = Czytelnik.class; package test25
Analiza funkcjonalności klasy Dzięki klasie Class można tworzyć obiekty różnych klas podanych przez nazwę wykorzystując konstruktor bezargumentowy. Object o = Class.forName("Czytelnik").newInstance(); Można także wykonać rzutowanie: (mimo, że w tym kontekście niezbyt użyteczne) Class.forName("Czytelnik").cast(o); Class jest typem sparametryzowanym. Można zatem ograniczać typ, żeby lepiej korzystać z metod. Class<? extends Czytelnik> cl = c.getclass(); cl.cast(o).getnumer(); package test25
Zastosowanie refleksji do analizy obiektów Pakiet java.lang.reflect zawiera narzędzia do badania stanu programu w trakcie jego wykonywania. Dzięki niemu można pisać kod uniwersalny. W analizie możliwości klas wykorzystywane są trzy klasy z tego pakietu: Constructor opisuje konstruktory Field opisuje pola Method opisuje metody Klasa Class posiada metody getconstructors, getfields, getmethods zwracające tablice publicznych elementów oraz getdeclaredconstructors, getdeclaredfields, getdeclaredmethods zawierające tablice wszystkich elementów (z pominięciem dziedziczonych z nadklasy).
Zastosowanie refleksji do analizy obiektów Constructor, Field i Method posiadają metody: String getname() zwraca nazwę int getmodifiers() zwraca lista modyfikatorów dostępu zakodorana binarnie w formie inta Constructor i Method mają: Class[] getparametertypes() zwraca listę parametrów Method: Class getreturntype() zwraca typ zwracany danej metody Field: Class gettype() zwraca typ danego pola
Zastosowanie refleksji do analizy obiektów Drukowanie listy pól klasy Czytelnik: String nazwaklasy = "Czytelnik"; try{ Class klasa = Class.forName(nazwaKlasy); Field[] pola = klasa.getdeclaredfields(); for(int i=0; i<pola.length; i++){ Field f = pola[i]; Class typ = f.gettype(); String nazwa = f.getname(); String modyfikatory = Modifiers.toString(f.getModifiers()); System.out.println(modyfikatory+" "+typ+" "+nazwa+"\n"); catch(classnotfoundexception e){e.printstacktrace(); Package test27
Wywoływanie dowolnych metod Dzięki mechanizmowi refleksji możliwe jest wywoływanie dowolnych metod po nazwie. String nazwaklasy = "Czytelnik"; String nazwametody = "getnumerkarty"; try{ Class klasa = Class.forName(nazwaKlasy); Method metoda = klasa.getmethod(nazwametody); String s = (String) metoda.invoke(klasa.newinstance()); System.out.println(s); catch(classnotfoundexception... e){e.printstacktrace(); Package test28
Podstawy obsługi wyjątków Wyjątki są obiektami klasy Exception zwracanymi przez metody gdy następuje problem z wykonaniem pewnych poleceń. Wyjątki należy obsłużyć, aby nie przerywać działania programu. Polecenia które mogą zwracać wyjątki należy umieścić w bloku try/catch, aby je obsłużyć. try{ //polecenia zwracające wyjątki catch(exception e){ //obsługa wyjątku e Gdy w bloku try zostanie zwrócony wyjątek sterowanie przenosi się natychmiast do bloku catch. Package test25, test26
Podstawy obsługi wyjątków Wyjątki powinny zostać obsłużone najszybciej jak to jest możliwe. Wyświetlenie informacji na konsoli w wielu przypadkach nie jest wystarczające, ponieważ fragment kodu nie został wykonany. W bloku catch w najlepszym wypadku powinien się znajdować kod alternatywny, który korzysta z wiedzy, że dany wyjątek poleciał. Jeżeli wyjątek nie może zostać dobrze obsłużony należy zlecić jego obsługę metodzie wywołującej daną. Robi się to przekazując w deklaracji metody słowo kluczowe throws oraz listę wyjątków jakie powinny zostać obsłużone wyżej. public void jakasmetoda(lista argumentow) throws Wyjatek1, Wyjatek2 {...
Wyjątki przepływ sterowania Niech będzie dany kod, w którym linia 1 może zwrócić Wyjątek1 lub Wyjątek3, a linia 2 może zwrócić Wyjątek2. public throws Wyjatek3 linia 0 Jeżeli żaden wyjątek się nie pojawi, to kolejność wykonywania kodu wygląda następująco: try{ linia 1 linia 2 Linia 0 catch(wyjątek1 w1){ obsługa w1 catch(wyjątek2 w2){ obsługa w2 Linia 1 Linia 2 Linia 3 linia 3
Wyjątki przepływ sterowania Niech będzie dany kod, w którym linia 1 może zwrócić Wyjątek1 lub Wyjątek3, a linia 2 może zwrócić Wyjątek2. public throws Wyjatek3 linia 0 try{ linia 1 linia 2 Jeżeli Wyjątek1 zwróci linia 1, nie zostanie wykonana dana metoda i obsługa przejdzie do pierwszego bloku catch po czym wróci do linii 3. Linia 0 Linia 0 catch(wyjątek1 w1){ obsługa w1 Linia 1 Linia 2 Linia 1 Obsługa w1 catch(wyjątek2 w2){ obsługa w2 Linia 3 Linia 3 linia 3
Wyjątki przepływ sterowania Niech będzie dany kod, w którym linia 1 może zwrócić Wyjątek1 lub Wyjątek3, a linia 2 może zwrócić Wyjątek2. public throws Wyjatek3 linia 0 try{ linia 1 linia 2 Jeżeli Wyjątek2 zwróci linia 2, nie zostanie wykonana dana metoda i obsługa przejdzie do drugiego bloku catch po czym wróci do linii 3. Linia 0 Linia 0 catch(wyjątek1 w1){ obsługa w1 Linia 1 Linia 2 Linia 1 Linia 2 Obsługa w2 catch(wyjątek2 w2){ obsługa w2 Linia 3 Linia 3 linia 3
Wyjątki przepływ sterowania Niech będzie dany kod, w którym linia 1 może zwrócić Wyjątek1 lub Wyjątek3, a linia 2 może zwrócić Wyjątek2. public throws Wyjatek3 linia 0 try{ linia 1 linia 2 Jeżeli linia 1 zwróci Wyjątek3 nie zostanie wykonana dana metoda oraz reszta kodu tej metody, a obsługa programu przejdzie do metody nadrzędnej, gdzie Wyjątek3 powinien zostać obsłużony. Linia 0 Linia 0 catch(wyjątek1 w1){ obsługa w1 Linia 1 Linia 2 Linia 1 Wyjątek3 catch(wyjątek2 w2){ obsługa w2 Linia 3 linia 3
Wyjątki klauzula finally Czasem koniecznym jest wykonanie pewnych operacji bez względu na to jaki wyjątek oraz czy w ogóle jakiś został wygenerowany. Kod taki, np. zwalniający zasoby, zamykający strumienie danych, pisze się w klauzuli finally. try{ //polecenia zwracające wyjątki catch(exception e){ //obsługa wyjątku e finally{ //kod który zawsze zostanie wykonany
Wyjątki klauzula finally public throws Wyjatek3 linia 0 Przepływ bez wyjątków Linia 0 try{ linia 1 linia 2 Linia 1 Linia 2 catch(wyjątek1 w1 Wyjątek2 w2){ obsługa w1 lub w2 finally{ blok finally linia 3 blok finally Linia 3
Wyjątki klauzula finally public throws Wyjatek3 Obsłużony Wyjątek1 w linii 1 linia 0 Linia 0 Linia 0 try{ linia 1 linia 2 Linia 1 Linia 2 Linia 1 Obsługa w1 catch(wyjątek1 w1 Wyjątek2 w2){ obsługa w1 lub w2 finally{ blok finally linia 3 blok finally Linia 3 blok finally Linia 3
Wyjątki klauzula finally public throws Wyjatek3 Nieobsłużony Wyjątek3 w linii 1 linia 0 Linia 0 Linia 0 try{ linia 1 linia 2 Linia 1 Linia 2 Linia 1 Wyjątek3 catch(wyjątek1 w1 Wyjątek2 w2){ obsługa w1 lub w2 finally{ blok finally linia 3 blok finally Linia 3 blok finally
Własne wyjątki Można samemu napisać klasę wyjątku odpowiednią do danych i działania programu. Następnie takie wyjątki w kodzie się wywołuje. public class BrakKsiazkiException extends Exception { public BrakKsiazkiException(){ public BrakKsiazkiException(String info){ super(info); klasa wyjątku musi dziedziczyć po Exception public class LimitPrzekroczonyException extends Exception { public LimitPrzekroczonyException(){ super("przekroczono limit wypożyczeń"); public LimitPrzekroczonyException(String info){ super(info); Package test29 Konstruktor bezargumentowy lub z informacją o błędzie go której będzie można dotrzeć za pomocą.getmessage() Wywołanie konstruktora klasy bazowej
Własne wyjątki Można samemu napisać klasę wyjątku odpowiednią do danych i działania programu. Następnie takie wyjątki w kodzie się wywołuje. public class Ksiazka { private String tytul; private boolean napolce = true; public Ksiazka(String t){tytul = t; public String gettytul(){return tytul; public boolean isnapolce(){return napolce; public void setnapolce(boolean b){napolce = b; klasa Książka posiada pole dzięki któremu widać czy na półce dana książka jest dostępna Package test29
Własne wyjątki public class Czytelnik { private static final int MAX_WYPOZYCZEN = 2; private ArrayList<Ksiazka> listaksiazek;... lista wyjątków wyrzucanych przez metodę public void wypozycz(ksiazka k) throws LimitPrzekroczonyException, BrakKsiazkiException { if (!k.isnapolce()) throw new BrakKsiazkiException( Nie ma "+ k.gettytul()); else if (listaksiazek.size() == MAX_WYPOZYCZEN) throw new LimitPrzekroczonyException(); else { k.setnapolce(false); listaksiazek.add(k); wywołanie wyjątku Package test29
Własne wyjątki Ksiazka k = new Ksiazka("Ksiazka 1"); Czytelnik c = new Czytelnik(); metoda zwracająca wyjątki otoczona klauzulą try/catch try { System.out.println( "Czytelnik nr." + c.getnumer() + " chce wypożyczyć " + k.gettytul()); c.wypozycz(k); System.out.println("-Udało się!"); catch (LimitPrzekroczonyException e) { System.out.println("-Nie udało się ponieważ " + e.getmessage()); catch (BrakKsiazkiException e) { System.out.println("-Nie udało się ponieważ " + e.getmessage()); e.printstacktrace() działa asynchronicznie i komunikat nie musi zostać umieszczony w potoku wiadomości System.out Wyświetlenie informacji o wyjątku Package test29