Pobieranie prezentacji. Proszę czekać

Pobieranie prezentacji. Proszę czekać

(c) Krzysztof Barteczko 2014

Podobne prezentacje


Prezentacja na temat: "(c) Krzysztof Barteczko 2014"— Zapis prezentacji:

1 (c) Krzysztof Barteczko 2014
Interfejsy (c) Krzysztof Barteczko 2014

2 Metody i klasy abstrakcyjne
Metody abstrakcyjne - nie wiemy jeszcze jaka może być ich konkretna implementacja, ale wiemy, że powinny wystąpić w zestawie metod każdej konkretnej klasy dziedziczącej klasę abstrakcyjną. Konkretna implementacja (definicja kodu metody) może być bardzo różna, w zależności od konkretnego rodzaju obiektów, które opisuje dana klasa. Klasa abstrakcyjna nie musi mieć metod abstrakcyjnych. Wystarczy zadeklarować ją ze specyfikatorem abstract. Nie wolno tworzyć obiektów klas abstrakcyjnych. (c) Krzysztof Barteczko 2014

3 Przykład – abstrakcyjne metody
Polimorficzne odwołania do nieistniejących być może metod (c) Krzysztof Barteczko 2014

4 Implementacja metod abstrakcyjnych
(c) Krzysztof Barteczko 2014

5 (c) Krzysztof Barteczko 2014
Pojęcie interfejsu (c) Krzysztof Barteczko 2014

6 Problem wielodziedziczenia (1)
Nie wszystkie zwierzęta wydają głos. Zatem umieszczanie (abstrakcyjnej) metody getVoice() oraz metody speak()  w klasie Zwierz  nie jest "czystym rozwiązaniem". Co więcej nie tylko zwierzęta mówią. Chciałoby się więc mieć klasę obiektów wydających głos, którą mógłby dziedziczyć np. Wodospad i Pies. Ale Pies jest Zwierzem (dziedziczy Zwierza) i nie może odziedziczyć klasy obiektów "wydających głos". W Javie nie ma bowiem  wielodziedziczenia klas: każda klasa może dziedziczyć bezpośrednio tylko jedną klasę. (c) Krzysztof Barteczko 2014

7 Dlaczego w Javie nie ma wielodziedziczenia klas
abstract class Human {     public abstract String getSex(); } class Father extends Human {     public String getSex() { return "male"; } } class Mother extends Human {     public String getSex() { return "female"; } } // hipotetyczne wielodziedziczenie class Child extends Mother, Father  {     // klasa Child nie przedefiniowuje metody getSex() ! } class A {    public int a; } class B extends A {   // ma pole a } class C extends A {  //  ma pole a } // hipotetyczne wielodziedziczenie class D extends B i C { } jaki wynik dla obiektu-dziecka (klasy Child) zwróci wywołanie metody getSex() ? Obiekt d z klasy D ma element definiowany przez pole a. Jeden czy dwa? Jeśli jeden, to który? Poczynając od Java wersja 8 mamy możliwość wielodziedziczenia konkretnych implementacji metod (c) Krzysztof Barteczko 2014

8 (c) Krzysztof Barteczko 2014
Interfejsy na pomoc (c) Krzysztof Barteczko 2014

9 Interfejsy – definicja i implementacja
(c) Krzysztof Barteczko 2014

10 Można implementować wiele interfejsów
(c) Krzysztof Barteczko 2014

11 (c) Krzysztof Barteczko 2014
Nowy Zwierz i nowy Pies public class Pies extends Zwierz implements Speakable, Moveable { public Pies() {} public Pies(String s) { super(s); } public String getTyp() { return "Pies"; public String getVoice(int voice) { if (voice == LOUD) return "HAU... HAU... HAU... "; else return "Hau... Hau..."; public Pies start() { System.out.println("Pies " + getName() + " biegnie"); return this; public Pies stop() { System.out.println("Pies " + getName() + " stanął"); public Pies merda() { System.out.println("Merda ogonem"); Zmieniając nieco definicję klasy Zwierz (nie wszystkie zwierzęta wydają głos, więc usunęliśmy metody getVoice() i speak()): public abstract class Zwierz { private String name = "bez imienia"; public Zwierz() {} public Zwierz(String s) { name = s; } public abstract String getTyp(); public String getName() { return name; Możemy teraz tak zdefiniować klasę Pies Używana dalej klasa Kot jest zdefiniowana podobnie. (c) Krzysztof Barteczko 2014

12 Interfejsy też wyznaczają typy
(c) Krzysztof Barteczko 2014

13 Jeśli interfejsy wyznaczają typy, to ...
Możemy:  wykonywać konwersje w górę do typu wyznaczanego przez implementowany interfejs,  używać operatora instanceof,  by stwierdzić czy obiekt jest obiektem klasy implementującej dany interfejs. Implementacja interfejsów działa podobnie jak przedefiniowanie metod: zauważmy, że metody start() i stop() interfejsu Moveable mają typ wyniku Moveable. W klasie Pies implementowaliśmy je z typem wyniku Pies, czyli przy implementacji interfejsów dopuszczalna jest kowariancja typów wyniku implementowanych metod. (c) Krzysztof Barteczko 2014

14 Polimorfizm przy użyciu interfejsów
public class Vehicle implements Moveable { // ... public Vehicle start() { setState(MOVING); return this; } public Vehicle stop() { setState(STOPPED); Dzięki interfejsom polimorficzne odwołania są możliwe nie tylko "wzdłuż" hierarchii dziedziczenia klas, ale również "w poprzek" tych hierarchii. Klasy Pies i Kot należą do innej hierarchii dziedziczenia niż klasa Car i Rower. Dzięki temu, że wszystkie te klasy implementują interfejs Moveable dla wszystkich tych klas uzyskujemy możliwość polimorficznych odwołań do metody start(). public class Wyscig { static void wyscig(Moveable ... moveables) { for (Moveable m : moveables) { m.start(); if (m instanceof Vehicle) System.out.println(m); } public static void main(String[] args) { wyscig(new Pies("Kuba"), new Car("WB4545", new Person("Janek", " "),100, 100, 100, 100, 100).fill(10), new Kot("Mruczek"), new Bicycle(new Person("Ala", " "),100, 100, 100, 100) ); Pies Kuba biegnie Samochód nr rej WB JEDZIE Kot Mruczek się skrada Pojazd 2, właścicielem którego jest Ala jest w stanie JEDZIE (c) Krzysztof Barteczko 2014

15 Mechanizm konwersji zawężających
Konwersje zawężające Przypomnienie: jeśli mamy referencję do obiektu typu Zwierz na którą podstawiono odniesienie do obiektu typu Pies, to możemy zrobić konwersję "w dół" hierarchii dziedziczenia.      Pies p = new Pies();     Zwierz z = p;     Pies p1 = (Pies) z; // Konwersja z typu Zwierz do typu Pies Mówiąc obrazowo (ale pamiętając o tym, że mamy do czynienia z konwersjami referencyjnymi i tak naprawdę jest tu tylko jeden obiekt, który po prostu, w kolejnych przekształceniach referencyjnych, traktujemy inaczej):  Pies pochodzi od Zwierza, możemy więc z Psa uzyskać Zwierza, a później z tego Zwierza z powrotem Psa. Mechanizm konwersji zawężających w równym stopniu dotyczy interfejsów. (c) Krzysztof Barteczko 2014

16 Konwersje referencyjne - przykład
(c) Krzysztof Barteczko 2014

17 (c) Krzysztof Barteczko 2014
... przykład c.d. dla obiektu hipotetycznej klasy Ryba - info( new Ryba()) - możemy dostać w wyniku tylko: public class Ryba extends Zwierz implements Moveable { public Ryba() { super(""); } public Ryba(String s) { super(s); @Override public String getTyp() { return "Ryba"; public Moveable start() { System.out.println("płynie"); return this; public Moveable stop() { System.out.println("śpi"); Ryba Bo wartość wyrażenia z instanceof Speakable jest false (Ryba nie implementuje interfejsu Speakable) i oczywiście nie jest też Psem. Natomiast  dla Psa kuby (kuba = new Pies("Kuba")) po info(kuba) dostaniemy pewnie: Pies Kuba HAU... HAU... HAU... Merda ogonem (c) Krzysztof Barteczko 2014

18 Konwersje referencyjne – przykład 2
Przywróćmy w klasie Zwierz metodę speak() public void speak(int ... v) { int vol = Speakable.QUIET; if (v.length == 1) vol = v[0]; String voice; if (this instanceof Speakable) voice = ((Speakable) this).getVoice(vol); else voice = "... (cisza) ..."; System.out.println(getTyp()+" "+getName()+ " mówi " + voice); } static void animalDialog(Zwierz z1, Zwierz z2) { z1.speak(); z2.speak(); System.out.println(" "); } // ... Pies kuba = new Pies("Kuba"), reksio = new Pies("Reksio"); Kot kot = new Kot("Mruczek"); Ryba ryba = new Ryba(); animalDialog(kuba, reksio); animalDialog(kuba, kot); animalDialog(reksio, ryba); Pies Kuba mówi Hau... Hau... Pies Reksio mówi Hau... Hau... Kot Mruczek mówi Miau... Ryba bez imienia mówi ... (cisza) ... (c) Krzysztof Barteczko 2014

19 Implementacje metod w interfejsach
W Javie 8 wprowadzono możliwość dostarczania gotowych definicji metod w interfejsach (publicznych metod statycznych oraz publicznych niestatycznych metod z użyciem słowa kluczowego default). Istotą tych zmian jest umożliwienie rozbudowy już istniejących interfejsów w taki sposób, by nie zakłócić zgodności z klasami, które już implementują te interfejsy. Rzeczywiście, do interfejsów, które już zostały zaimplementowane przez jakieś klasy nie powinniśmy dodawać metod abstrakcyjnych, bowiem w takim przypadku klasy te przestaną działać i będą wymagać zmian w kodzie - implementacji nowych metod interfejsu. Z drugiej strony czasem dodanie do istniejącego interfejsu nowych metod byłoby wskazane. Oczywiście, to nie mogą być metody abstrakcyjne. (c) Krzysztof Barteczko 2014

20 (c) Krzysztof Barteczko 2014
Przykład (c) Krzysztof Barteczko 2014

21 (c) Krzysztof Barteczko 2014
Mixiny (c) Krzysztof Barteczko 2014

22 (c) Krzysztof Barteczko 2014
Mixiny w użyciu (c) Krzysztof Barteczko 2014

23 Co teraz z rombem wielodziedziczenia ?
(c) Krzysztof Barteczko 2014

24 Metody domyślne można przedefiniowywać i przeciążać
class Child implements Mother, Father { public String getSex() { return "?"; } public String fatherName() { return "Tata: Jan"; } public String motherName() { return "Mama: Ala"; } public String fatherName(int age) { return "Jan, lat " + age; } public String toString() { return "Rodzice - " + fatherName() + " " + motherName(); } // ... Child child = new Child(); System.out.println(child); System.out.println(child.fatherName(30)); Rodzice - Tata: Jan Mama: Ala Jan, lat 30 (c) Krzysztof Barteczko 2014

25 Domyślne metody są polimorficzne
interface BaseIf { default String get() { return this.getClass().getSimpleName(); } //brak abstrakcyjnych metod do implementacji! class A implements BaseIf {} class B implements BaseIf {} public class WhatIsThis { public static void main(String[] args) { BaseIf a = new A(); System.out.println(a.get()); BaseIf b = new B(); System.out.println(b.get()); A B (c) Krzysztof Barteczko 2014

26 (c) Krzysztof Barteczko 2014
A jak użyć super ? class Child implements Mother, Father { public String getSex() { return "?"; } public String fatherName() { return "Tata: " + Father.super.fatherName(); } public String motherName() { return "Mama: " + Mother.super.motherName(); } public String toString() { return "Rodzice - " + fatherName() + " " + motherName(); } // ... Child child = new Child(); System.out.println(child); Rodzice - Tata: Jan Mama: Ala (c) Krzysztof Barteczko 2014

27 (c) Krzysztof Barteczko 2014
Jeszcze raz: kolizje Gdy te same domyślne metody w kilku implementowanych interfajsach – wystąpi błąd w kompilacji (romb wielodziedziczenia). (c) Krzysztof Barteczko 2014

28 Różne implementacje tych samych metod
Jeśli ta sama metoda domyślna jest implementowana na różnych poziomach hierarchii dziedziczenia, to:  priorytet ma metoda przedefiniowana w klasie obiektu,  jeśli jej nie ma, to metoda implementowana w nadklasie obiektu, jeśli jej nie ma, to metoda najbliższego w hierarchii interfejsu interface IfA { default void m() { System.out.println("Z IfA"); } } interface IfB extends IfA { default void m() { System.out.println("Z IfB"); } class A implements IfA, IfB { } class B implements IfA, IfB { public void m() { System.out.println("Z B"); class C extends B implements IfA, IfB {} // ... new A().m(); IfA b = new B(); b.m(); IfA c= new C(); c.m(); Z IfB Z B (c) Krzysztof Barteczko 2014

29 Nie nadużywać metod domyślnych
Głównym przeznaczeniem metod domyślnych jest bezkolizyjne modyfikowanie istniejących interfejsów. Inne przypadki uzycia (mixiny) trzeba stosować z umiarem. W przeciwnym razie możemy nadmiernie skomplikować hierarchię dziedziczenia w programie. (c) Krzysztof Barteczko 2014


Pobierz ppt "(c) Krzysztof Barteczko 2014"

Podobne prezentacje


Reklamy Google