Programowanie obiektowe IV. Interfejsy i klasy wewnętrzne Małgorzata Prolejko OBI JA16Z03
Plan Właściwości interfejsów. Interfejsy a klasy abstrakcyjne. Klonowanie obiektów. Klasy wewnętrzne. Dostęp do stanu obiektu w klasie wewnętrznej. Lokalne klasy wewnętrzne. Dostęp do zmiennych finalnych z metod zewnętrznych. Anonimowe klasy wewnętrzne. Statyczne klasy wewnętrzne. Klasy proxy.
Właściwości interfejsów W Javie nie ma wielokrotnego dziedziczenia (każda klasa może posiadać tylko jednego rodzica), ale za to istnieje mechanizm interfejsów dzięki któremu klasa może posiadać różne predefiniowane właściwości. Interfejs jest zbiorem wymagań dotyczącym przyszłych klas. Posiada deklaracje metod, ale nie ich implementacje oraz może posiadać pola statyczne (nigdy pola składowe). public interface Comparable{ int compareto(object o); Metody interfejsu są zawsze publiczne, więc nie trzeba ich oznaczać modyfikatorem public.
Właściwości interfejsów Aby klasa skorzystała z interfejsu należy użyć słowa kluczowego implements oraz nazwy interfejsu, a także zaimplementować wszystkie metody tego interfejsu. public class Czytelnik implements Comparable{ private int liczbawypozyczen;... public int compareto(object inny){ Czytelnik c = (Czytelnik) inny; if(liczbawypozyczen > c.getliczbawypozyczen()) return 1; if(liczbawypozyczen < c.getliczbawypozyczen()) return -1; return 0; Package test30
Interfejsy a klasy abstrakcyjne Interfejsy przypominają klasy abstrakcyjne, ale nimi nie są. Z powodu braku wielokrotnego dziedziczenia w Javie należało stworzyć nowy mechanizm pozwalający na dowolne rozszerzanie obiektów. Zatem klasa może posiadać jednego rodzica, ale wiele interfejsów public class Student extends Czytelnik implements Cloneable,Comparable {...
Klonowanie obiektów Klonowanie poprzez implementację interfejsu Cloneable public class Test implements Cloneable{... public Test clone() throws CloneNotSupportedException{ Test t = (Test) super.clone();... return t; deklaracja implementacji interfejsu
Klonowanie obiektów Klonowanie poprzez implementację interfejsu Cloneable public class Test implements Cloneable{... public Test clone() throws CloneNotSupportedException{ Test t = (Test) super.clone();... return t; implementacja metody z interfejsu wraz z wyrzucanym wyjątkiem
Klonowanie obiektów Klonowanie poprzez implementację interfejsu Cloneable public class Test implements Cloneable{... public Test clone() throws CloneNotSupportedException{ Test t = (Test) super.clone();... return t; wywołanie metody z klasy bazowej
Klonowanie obiektów Klonowanie poprzez implementację interfejsu Cloneable public class Test implements Cloneable{... public Test clone() throws CloneNotSupportedException{ Test t = (Test) super.clone();... return t; zwrot referencji do nowego obiektu
Klonowanie obiektów public class Pracownik extends Czytelnik implements Cloneable{... private Date dzienzatrudnienia; public Pracownik clone() throws CloneNotSupportedException{ Pracownik sklonowany = (Pracownik) super.clone(); sklonowany.dzienzatrudnienia = (Date)dzienZatrudnienia.clone(); return sklonowany; Pracownik p1 = new Pracownik("Adam Abacki"); try{ Pracownik p2 = p1.clone(); System.out.println(p1.getNazwisko()+", "+p2.getnazwisko()); catch(clonenotsupportedexception e){e.printstacktrace(); Package test31
Klonowanie obiektów Ćwiczenie 14. Zaimplementuj interfejs Comparable dla klasy opisującej punkt na płaszczyźnie. Jakie cechy punktu mogą być porównywane?
Klasy wewnętrzne Wewnątrz ciała klasy można zdefiniować nową klasę (wewnętrzną). Będzie ona niewidoczna dla pakietu, ale za to wszystkie jej pola i metody będą dostępne dla klasy zewnętrznej. public class Czytelnik{ private class Karta1{... public class Karta2{... Tylko klasy wewnętrzne mogą być prywatne. Taka klasa nie będzie widoczna w ogóle na zewnątrz zawierającej jej klasy. Package test32
Klasy wewnętrzne Wszystkie pola i metody klasy zewnętrznej są widoczne w klasie wewnętrznej (bez względu na to jaki modyfikator dostępu miała) public class Czytelnik{ private String nazwisko; private class Karta1{... private int numerkarty; public String identyfikator(){ return nazwisko + numerkarty; public class Karta2{ private int numerkarty; public String identyfikator(){ return nazwisko + numerkarty; Package test32
Klasy wewnętrzne Obiekty klas wewnętrznych nie są tworzone automatycznie. Jeśli zostaną zaś utworzone, to mają pełny dostęp do pól i metod klas wewnętrznych (bez względu na to jaki modyfikator dostępu miała). public class Czytelnik{ private String nazwisko; private class Karta1{... public class Karta2{... public void wypisz(){ Karta1 k1 = new Karta1(1); Karta2 k2 = new Karta2(2); System.out.println(k1.numerKarty); System.out.println(k2.numerKarty); System.out.println(k1.identyfikator()); System.out.println(k2.identyfikator()); Package test32
Publiczne klasy wewnętrzne Do klas wewnętrznych oznaczonych jako publiczne będzie można się dostać spoza kodu klasy zawierającej. Służy temu specjalna składnia KlasaZewnętrzna.KlasaWewnętrzna Można także tworzyć obiekty tej klasy za pomocą operatora obiektklasyzewnętrznej.new KlasaWewnętrzna( ) Czytelnik c = new Czytelnik("abba"); Czytelnik.Karta2 k3 = c.new Karta2(3); System.out.println(k3.identyfikator()); Tak utworzonymi obiektami można manipulować w zakresie dostępności jej pól i metod: pola i metody oznaczone jako private są ukryte. Package test32
Dostęp do stanu obiektu z klasy wewnętrznej Klasa wewnętrzna może odwoływać się do pól swojej klasy zewnętrznej tak jakby były jej własnymi. public class Czytelnik{ private String nazwisko; private Karta karta;... private class Identyfikator{ private String identyfikator; public Identyfikator(){ identyfikator = nazwisko + karta.getnumerkarty();... Package test33
Dostęp do stanu obiektu z klasy wewnętrznej Klasa zewnętrzna może odwoływać się do pól swojej klasy zewnętrznej tak jakby były jej własnymi. public class Czytelnik{ private String nazwisko; private Karta karta; private class Identyfikator{ private String identyfikator; public Identyfikator(){ this.identyfikator = Czytelnik.this.nazwisko + Czytelnik.this.karta.toString(); Rozszerzona składnia
Klasy wewnętrzne Ćwiczenie 15. Zaimplementuj klasę opisującą dowolny czworobok oraz metody pozwalające na sprawdzenie czy jest to równoległobok, prostokąt, kwadrat, romb lub żaden z nich. p2 p6 p2 p4 p4 p7 p1 p8 p5 p1 p3 p1 p5 p8 p9 p2 p4 p6 p7 p9 p1 p6 p1 p3 p5
Lokalne klasy wewnętrzne Jeżeli obiekt klasy wewnętrznej używany jest tylko w jednej metodzie, to taką klasę można zdefiniować bezpośrednio w tej metodzie stanie się wtedy klasą lokalną. public void wyświetlkomunikat(){ class Sluchacz implements ActionListener { public void actionperformed( ActionEvent e ) { if(!karta.sprawdzaktywnosc()) System.out.println("Karta stracila waznosc!"); ActionListener sluchacz = new Sluchacz(); Timer t = new Timer(1000000,sluchacz); t.start(); Package test34
Dostęp do zmiennych finalnych z metod zewnętrznych. W klasach wewnętrznych jest bezpośredni dostęp do stanu obiektu (karta), ale nie ma dostępu do zmiennych lokalnych (komunikat). public void wyswietlkomunikat(string komunikat){ class Sluchacz implements ActionListener { private String wiadomosc; public Sluchacz(String s){wiadomosc = s; public void actionperformed( ActionEvent e ) { if(!karta.sprawdzaktywnosc()) System.out.println(wiadomosc); ActionListener sluchacz = new Sluchacz(komunikat); Timer t = new Timer(1000000,sluchacz); t.start(); Package test35 Obsługa dla zmiennej lokalnej Zmienna lokalna przekazana do obiektu klasy lokalnej
Dostęp do zmiennych finalnych z metod zewnętrznych. Zadeklarowanie zmiennej jako finalnej umożliwia jej widoczność wewnątrz klas lokalnych. public void wyswietlkomunikat(fianl String komunikat){ class Sluchacz implements ActionListener { private String wiadomosc; public Sluchacz(String s){wiadomosc = s; public void actionperformed( ActionEvent e ) { if(!karta.sprawdzaktywnosc()) System.out.println(wiadomosckomunikat;) ActionListener sluchacz = new Sluchacz(komunikat); Timer t = new Timer(1000000,sluchacz); t.start(); Package test36
Anonimowe klasy wewnętrzne Klasy użyte jednokrotnie nie muszą posiadać nazwy wtedy są anonimowe. Konstrukcja obiektu takiej klasy ma specjalną składnię. new typpierwotny(argumenty konstrukcji){ metody i pola klasy wewnętrznej Typem pierwotnym może być interfejs bądź klasa bazowa. Argumenty konstrukcji podawane są tylko gdy typem jest klasa wtedy wywoływany jest odpowiedni konstruktor tej klasy. Klasy anonimowe nie mogą posiadać konstruktorów ponieważ nie mają nazwy.
Anonimowe klasy wewnętrzne Klasę lokalną Sluchacz można zamienic na anonimową ponieważ nie ma konstruktora, implementuje tylko jeden interfejs i używany jest tylko jeden jej obiekt. public void wyswietlkomunikat(fianl String komunikat){ ActionListener sluchacz = new ActionListener() { public void actionperformed( ActionEvent e ) { if(!karta.sprawdzaktywnosc()) System.out.println(komunikat;) Timer t = new Timer(1000000,sluchacz); t.start(); Package test37
Statyczne klasy wewnętrzne Klasa wewnętrzna która nie potrzebuje odniesienia do swojej klasy zewnętrznej może zostać opatrzona modyfikatorem static. Dzięki temu będzie mogła być używana w metodach statycznych. Może także służyć uporządkwaniu danych statycznych. public class Czytelnik{ public static class Karta{ private Integer numerkarty; private Date datawygasniecia; public Karta(){... public bool sprawdzaktywnosc(){ return (datawygasniecia.compareto(new Date())>=0)? true:false;
Klasy proxy Mechanizm proxy ma na celu utworzenie pewnej warstwy pomiędzy wywoływaniem metod obiektów a miejscem wywołania. Dzięki temu można monitorować, nadzorować i modyfikować wywołania. Może też być używany by tworzyć obiekty implementujące pewne interfejsy w trakcie wykonywania programu.
Klasy proxy Obiekty klasy Proxy posiadają wszystkie metody z klasy Object oraz metody z interesujących nas interfejsów. Jednak nie ma gdzie zapisać jak powinny być zaimplementowane metody z tych interfejsów. Należy za to dostarczyć uchwyt wywołań obiekt klasy implementującej interfejs InvocationHandler, a z nim metodę invoke(). public interface InvocationHandler{ Object invoke(object proxy, Method metoda, Object[] argumenty); Aby utworzyć obiekt proxy, oprócz uchwytu będzie jeszcze potrzebny obiekt mechanizmu ładowania klas null, pozwala na zastosowanie domyślnego mechanizmu; oraz lista interfejsów.
Tworzenie obiektu proxy Utworzymy obiekt proxy dla obiektu Integer. Integer i = new Integer(3); Class[] interfejsyintegera = Integer.class.getInterfaces(); Object proxy = Proxy.newProxyInstance(null, interfejsyintegera, new InvocationHandler(){ @Override public Object invoke(object p, Method m, Object[] arg) throws Throwable { System.out.println("metoda:"+ m.getname()); return m.invoke(i, arg); ); System.out.println(i.toString()); System.out.println("..."); System.out.println(proxy.toString()); Miejsce gdzie można monitorować, zmieniać argumenty lub obiekt wywołujący Obiekt dla którego tworzone jest proxy, bo jego metody będą wywoływane Package test38
Tworzenie obiektu proxy Utworzymy obiekt proxy dla obiektu Integer. public static class UchwytWywolania implements InvocationHandler{ private Object obiektobudowany; public UchwytWywolania(Object o){ obiektobudowany = o; @Override public Object invoke(object p, Method m, Object[] arg) throws Throwable { System.out.println("metoda:"+ m.getname()); return m.invoke(obiektobudowany, arg); Integer i = new Integer(3); Class[] interfejsyintegera = Integer.class.getInterfaces(); Object proxy = Proxy.newProxyInstance(null, interfejsyintegera, new UchwytWywolania(i)); Obiekt dla którego tworzone jest proxy Package test38