• Nie Znaleziono Wyników

Klasyabstrakcyjneiinterfejsy Klasyabstrakcyjne

N/A
N/A
Protected

Academic year: 2021

Share "Klasyabstrakcyjneiinterfejsy Klasyabstrakcyjne"

Copied!
31
0
0

Pełen tekst

(1)

Klasy abstrakcyjne i interfejsy

(2)

Klasy abstrakcyjne

Dana jest klasa bazowa Figura oraz dwie klasy potomne: Trójkąt i Prostokąt.

p u b l i c c l a s s F i g u r a {

f l o a t a , b ; f l o a t p o l e ( ) {

S y s t e m . o u t . p r i n t l n (" N i e z d e f i n i o w a n e ") ; r e t u r n −1;

} }

p u b l i c c l a s s P r o s t o k a t e x t e n d s F i g u r a {

f l o a t p o l e ( ) { r e t u r n a ∗b ; }

}

p u b l i c c l a s s T r o j k a t e x t e n d s F i g u r a {

f l o a t p o l e ( ) { r e t u r n a ∗b / 2 ; }

}

W takich przypadkach klasa bazowa często jest tylko atrapą, która nie wykonuje żadnych zadań, a służy jedynie do zdefiniowania zestawu metod, jakimi będą posługiwały się klasy potomne, oraz umożliwienia korzystania z wywołań polimorficznych. Często nie ma potrzeby lub jest wręcz niewskazane, aby były tworzone obiekty klasy bazowej.

Monika Wrzosek (IM UG) Programowanie obiektowe 150 / 189

(3)

Klasa abstrakcyjna - klasa, która została zadeklarowana z użyciem słowa abstract.

Klasa, w której przynajmniej jedna metoda jest abstrakcyjna (oznaczona słowem kluczowym abstract), musi być zadeklarowana jako abstrakcyjna. Może jednak zawierać również zwykłe (nieabstrakcyjne) metody.

[public]abstract class nazwa_klasy {

abstract typ_zwracany nazwa_metody(argumenty);

}

Metoda abstrakcyjna ma jedynie definicję, nie może zawierać żadnego kodu (nawet pustego nawiasu klamrowego; deklarację należy zakończyć średnikiem).

Metoda pole z klasy Figura

p u b l i c c l a s s F i g u r a { f l o a t a , b ;

f l o a t p o l e ( ) {

S y s t e m . o u t . p r i n t l n (" N i e z d e f i n i o w a n e ") ; r e t u r n −1;

} }

mogłaby być metodą abstrakcyjną:

p u b l i c a b s t r a c t c l a s s F i g u r a { f l o a t a , b ;

a b s t r a c t f l o a t p o l e ( ) ; }

(4)

p u b l i c a b s t r a c t c l a s s F i g u r a { f l o a t a , b ;

a b s t r a c t f l o a t p o l e ( ) ; }

Po takiej deklaracji nie można tworzyć obiektów klasy Figura.

Próba wykonania instrukcji

F i g u r a f i g = new F i g u r a ( ) ;

zakończy się komunikatem o błędzie:

Figura is abstract; cannot be instantiated.

Zadeklarowanie metody jako abstrakcyjnej wymusza jej redeklarację w klasie potomnej. Każda klasa wyprowadzona z klasy Figura musi zawierać metodę pole.

Jeżeli w którejś z klas tej metody zabraknie, program się nie skompiluje, np:

p u b l i c c l a s s T r o j k a t e x t e n d s F i g u r a { }

Jeśli klasa bazowa zawiera metodę abstrakcyjną, to każda klasa potomna również ją zawiera. Pozwala to bezpiecznie stosować wywołania polimorficzne.

Monika Wrzosek (IM UG) Programowanie obiektowe 152 / 189

(5)

p u b l i c a b s t r a c t c l a s s F i g u r a { f l o a t a =6 , h =4;

a b s t r a c t f l o a t p o l e ( ) ; S t r i n g f ( ) {

r e t u r n " Metoda z k l a s y F i g u r a "; }}

p u b l i c c l a s s P r o s t o k a t e x t e n d s F i g u r a { f l o a t p o l e ( ) {

r e t u r n a ∗h ; }

S t r i n g f ( ) {

r e t u r n " Metoda z k l a s y P r o s t o k a t "; }}

p u b l i c c l a s s T r o j k a t e x t e n d s F i g u r a { f l o a t p o l e ( ) {

r e t u r n a ∗h / 2 ; }

}

p u b l i c c l a s s Main {

p u b l i c v o i d p o l e F i g u r a ( F i g u r a f i g ) { S y s t e m . o u t . p r i n t l n ( f i g . p o l e ( ) ) ; }

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { Main M = new Main ( ) ;

P r o s t o k a t p r o s t = new P r o s t o k a t ( ) ; T r o j k a t t r o j = new T r o j k a t ( ) ;

S y s t e m . o u t . p r i n t l n ("Wywo ł a n i e metod p o l e : ") ; M. p o l e F i g u r a ( p r o s t ) ; // w y w o l a n i e p o l i m o r f i c z n e M. p o l e F i g u r a ( t r o j ) ; // w y w o l a n i e p o l i m o r f i c z n e

S y s t e m . o u t . p r i n t l n ("Wywo ł a n i e metod f : ") ; S y s t e m . o u t . p r i n t l n ( p r o s t . f ( ) ) ;

S y s t e m . o u t . p r i n t l n ( t r o j . f ( ) ) ; }}

(6)

Wywołania konstruktorów

Wywołania konstruktorów nie są polimorficzne.

W klasie potomnej zawsze musi zostać wywołany konstruktor klasy bazowej. Zawsze najpierw powinien zostać wykonany konstruktor klasy bazowej, a potem klasy potomnej.

Przypadek 1: w klasie bazowej i potomnej jest konstruktor bezargumentowy.

p u b l i c c l a s s A { p u b l i c A ( ) {

S y s t e m . o u t . p r i n t l n (" K o n s t r u k t o r k l a s y A") ; }

}

p u b l i c c l a s s B e x t e n d s A { p u b l i c B ( ) {

S y s t e m . o u t . p r i n t l n (" K o n s t r u k t o r k l a s y B") ; }

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { B b = new B ( ) ;

} }

K o n s t r u k t o r k l a s y A K o n s t r u k t o r k l a s y B

Jeśli w konstruktorze klasy potomnej nie będzie jawnie wywołany żaden konstruktor klasy bazowej, automatycznie zostanie wywołany bezargumentowy konstruktor klasy bazowej.

Monika Wrzosek (IM UG) Programowanie obiektowe 154 / 189

(7)

Wywołania konstruktorów

Przypadek 2: w klasie bazowej nie ma żadnego jawnego konstruktora.

p u b l i c c l a s s A { }

p u b l i c c l a s s B e x t e n d s A { p u b l i c B ( ) {

S y s t e m . o u t . p r i n t l n (" K o n s t r u k t o r k l a s y B") ; }

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { B b = new B ( ) ;

} }

K o n s t r u k t o r k l a s y B

Czy w takim razie nie został wywołany konstruktor klasy A?

Konstruktor musi zostać wywołany, nawet jeśli nie zostanie umieszczony jawnie w ciele klasy. Wtedy Java dodaje własny pusty konstruktor domyślny i to on zostaje wywołany.

(8)

Wywołania konstruktorów

Przypadek 3: w klasie bazowej nie ma konstruktora bezargumentowego, ale jest dowolny inny konstruktor.

p u b l i c c l a s s A { p u b l i c A(i n t i ) {

S y s t e m . o u t . p r i n t l n (" K o n s t r u k t o r k l a s y A") ; }

}

p u b l i c c l a s s B e x t e n d s A { p u b l i c B ( ) {

S y s t e m . o u t . p r i n t l n (" K o n s t r u k t o r k l a s y B") ; }

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { B b = new B ( ) ;

} }

Ta konstrukcja jest niepoprawna (błąd kompilacji).

Konstruktor klasy B będzie próbował wywołać bezargumentowy konstruktor klasy A, którego nie ma (pusty konstruktor domyślny zostałby dodany przez kompilator tylko wtedy, gdyby w klasie A nie było żadnego innego konstruktora).

Monika Wrzosek (IM UG) Programowanie obiektowe 156 / 189

(9)

Kod z Przypadku 3 można poprawić na dwa sposoby:

- dopisać konstruktor domyślny do klasy A;

- jawnie wywołać istniejący w klasie A konstruktor jednoargumentowy.

Drugi sposób:

p u b l i c c l a s s A { p u b l i c A(i n t i ) {

S y s t e m . o u t . p r i n t l n (" K o n s t r u k t o r k l a s y A") ; }

}

p u b l i c c l a s s B e x t e n d s A { p u b l i c B ( ) {

s u p e r( 1 0 0 ) ; // l u b d o w o l n a i n n a l i c z b a i n t S y s t e m . o u t . p r i n t l n (" K o n s t r u k t o r k l a s y B") ; }

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { B b = new B ( ) ;

} }

K o n s t r u k t o r k l a s y A K o n s t r u k t o r k l a s y B

(10)

p u b l i c c l a s s A { p u b l i c A ( ) {

S y s t e m . o u t . p r i n t l n (" K o n s t r u k t o r k l a s y A") ; }}

p u b l i c c l a s s B e x t e n d s A { p u b l i c B ( ) {

S y s t e m . o u t . p r i n t l n (" K o n s t r u k t o r k l a s y B") ; }}

p u b l i c c l a s s C { A a ;

B b ;

p u b l i c C ( ) {

S y s t e m . o u t . p r i n t l n (" K o n s t r u k t o r k l a s y C") ; a = new A ( ) ;

b = new B ( ) ; }

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { C c = new C ( ) ;

}}

K o n s t r u k t o r k l a s y C K o n s t r u k t o r k l a s y A K o n s t r u k t o r k l a s y A K o n s t r u k t o r k l a s y B

Monika Wrzosek (IM UG) Programowanie obiektowe 158 / 189

(11)

Wywoływanie metod w konstruktorach

Wywoływanie metod w konstruktorach w połączeniu z polimorfizmem może wprowadzić w pułapkę.

p u b l i c c l a s s A { p u b l i c A ( ) {

f ( ) ; }

p u b l i c v o i d f ( ) { }}

p u b l i c c l a s s B e x t e n d s A { i n t d z i e l n i k ;

p u b l i c B(i n t d z i e l n i k ) { t h i s. d z i e l n i k = d z i e l n i k ; }

p u b l i c v o i d f ( ) {

f l o a t w y n i k = 1 / d z i e l n i k ; }

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { B b = new B ( 1 ) ;

b . f ( ) ; }}

E x c e p t i o n i n t h r e a d " main " j a v a . l a n g . A r i t h m e t i c E x c e p t i o n : / by z e r o

(12)

p u b l i c c l a s s A { p u b l i c A ( ) {

S y s t e m . o u t . p r i n t l n (" K o n s t r u k t o r k l a s y A") ; f ( ) ;

}

p u b l i c v o i d f ( ) {

S y s t e m . o u t . p r i n t l n ("A : f ") ; }

}

p u b l i c c l a s s B e x t e n d s A { i n t d z i e l n i k ;

p u b l i c B(i n t d z i e l n i k ) {

S y s t e m . o u t . p r i n t l n (" K o n s t r u k t o r k l a s y B") ; t h i s. d z i e l n i k = d z i e l n i k ;

}

p u b l i c v o i d f ( ) {

S y s t e m . o u t . p r i n t l n ("B : f ( ) ") ; f l o a t w y n i k = 1 / d z i e l n i k ;

S y s t e m . o u t . p r i n t l n (" 1 / d z i e l n i k t o : " + w y n i k ) ; }

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { B b = new B ( 1 ) ;

b . f ( ) ; } }

K o n s t r u k t o r k l a s y A B : f ( )

E x c e p t i o n i n t h r e a d " main " j a v a . l a n g . A r i t h m e t i c E x c e p t i o n : / by z e r o

Monika Wrzosek (IM UG) Programowanie obiektowe 160 / 189

(13)

Wyjaśnienie przykładu.

W metodzie main jest tworzony nowy obiekt klasy B.

Konstruktorowi przekazywana jest wartość 1, zatem pole dzielnik przyjmuje wartość 1.

Następnie zostaje wywołana metoda f. Można się spodziewać, że metoda ta wykona dzielenie 1 / 1.

Ponieważ klasa B dziedziczy po A, przed wykonaniem jej konstruktora jest wywoływany konstruktor bezargumentowy klasy A.

Jest tam wywoływana metoda f.

Skoro jednak sam obiekt jest typu B, wywołanie to będzie polimorficzne, a zatem zostanie wywołana metoda f z klasy B.

Oznacza to, że fragment kodu:

d o u b l e w y n i k = 1 / d z i e l n i k ;

S y s t e m . o u t . p r i n t l n (" 1 / d z i e l n i k t o : " + w y n i k ) ;

zostanie wykonany, zanim jeszcze polu dzielnik zostanie przypisana jakakolwiek wartość. Pole dzielnik nie jest jawnie zainicjowane - czyli równe 0. Dlatego został wygenerowany wyjątek ArithmeticException (dzielenie przez zero).

Zatem również w konstruktorach wywołania metod są polimorficzne, czyli skojarzenie treści metody odbywa się w trakcie działania programu i brany jest pod uwagę rzeczywisty typ obiektu.

(14)

Interfejsy

W Javie - do wersji 7 włącznie - interfejs to klasa czysto abstrakcyjna, czyli taka, w której wszystkie metody są traktowane jako abstrakcyjne.

W wersji 8 wprowadzono jednak (ze względu na inne zmiany dotyczące języka) możliwość definiowania tzw. metod domyślnych.

Interfejs deklaruje się za pomocą słowa kluczowego interface.

Interfejs może być publiczny (o ile jest zdefiniowany w pliku o takiej samej nazwie jak nazwa interfejsu) lub pakietowy.

Schemat konstrukcji interfejsu

[public] interface nazwa_interfejsu {

typ_zwracany nazwa_metody1 (argumenty);

typ_zwracany nazwa_metody2 (argumenty);

/*dalsze metody intefejsu*/

typ_zwracany nazwa_metodyN (argumenty);

}

Monika Wrzosek (IM UG) Programowanie obiektowe 162 / 189

(15)

Interfejsy

p u b l i c i n t e r f a c e R y s o w a n i e { p u b l i c v o i d r y s u j ( ) ; }

Tak zdefiniowany interfejs może być implementowany przez dowolną klasę.

Jeśli mówimy, że dana klasa implementuje interfejs, oznacza to, że zawiera ona definicje wszystkich metod zadeklarowanych w interfejsie.

Jeśli choć jedna metoda zostanie pominięta, kompilator zgłosi błąd.

Jeśli klasa ma implementować interfejs, piszemy słowo kluczowe implements.

Schemat implementowania interfejsu przez klasę

[specyfikator dostępu] [abstract] class nazwa_klasy implements nazwa_interfejsu {

/*pola i metody klasy*/

}

p u b l i c c l a s s M o j a K l a s a i m p l e m e n t s R y s o w a n i e { p u b l i c v o i d r y s u j ( ) {

S y s t e m . o u t . p r i n t l n (" M o j a K l a s a ") ; }

}

(16)

p u b l i c i n t e r f a c e L i c z P o l e { p u b l i c f l o a t p o l e ( ) ; }

p u b l i c c l a s s P r o s t o k a t i m p l e m e n t s L i c z P o l e { f l o a t a , h ;

p u b l i c f l o a t p o l e ( ) { r e t u r n a ∗ h ;

} }

p u b l i c c l a s s T r o j k a t i m p l e m e n t s L i c z P o l e { f l o a t a , h ;

p u b l i c f l o a t p o l e ( ) { r e t u r n a ∗ h / 2 ; }

}

p u b l i c c l a s s T r a p e z i m p l e m e n t s L i c z P o l e { f l o a t a , b , h ;

p u b l i c f l o a t p o l e ( ) { r e t u r n ( ( a+b ) ∗ h ) / 2 ; }

}

Monika Wrzosek (IM UG) Programowanie obiektowe 164 / 189

(17)

Interfejsy a dziedziczenie

Problem 1. Poniższy program kompiluje się, jednak ma pewną wadę.

p u b l i c c l a s s K s z t a l t { p u b l i c v o i d r y s u j ( ) { }

}

p u b l i c i n t e r f a c e R y s o w a n i e { p u b l i c v o i d r y s u j ( ) ; }

p u b l i c c l a s s P r o s t o k a t e x t e n d s K s z t a l t i m p l e m e n t s R y s o w a n i e { p u b l i c v o i d r y s u j ( ) {

S y s t e m . o u t . p r i n t l n (" Metoda r y s u j z k l a s y P r o s t o k a t ") ; }}

p u b l i c c l a s s T r o j k a t e x t e n d s K s z t a l t i m p l e m e n t s R y s o w a n i e { p u b l i c v o i d r y s u j ( ) {

S y s t e m . o u t . p r i n t l n (" Metoda r y s u j z k l a s y T r o j k a t ") ; }}

Klasy Prostokat i Trojkat (czyli pochodne od klasy Ksztalt) implementują interfejs Rysowanie, ale sama klasa Ksztalt już nie. Zatem w klasach potomnych wymuszana jest implementacja metody rysuj, a w klasie bazowej nie. Nawet jeżeli klasa Ksztalt zawiera metodę rysuj, to nie będzie można wywołać jej w sposób polimorficzny.

(18)

Rozwiązanie: Klasa bazowa powinna również implementować interfejs Rysowanie:

p u b l i c c l a s s K s z t a l t i m p l e m e n t s R y s o w a n i e { p u b l i c v o i d r y s u j ( ) {

} }

Problem 2. Jeśli jest wiele klas potomnych i każda z nich ma implementować dany interfejs, łatwo o pominięcie słowa implements w jednej z nich (i spowodowanie błędu).

Rozwiązanie: Niech klasa bazowa będzie abstrakcyjna. Wtedy klasa ta nie będzie musiała implementować metod zawartych w interfejsie, ale będą je musiały implementować wszystkie nieabstrakcyjne klasy pochodne.

p u b l i c a b s t r a c t c l a s s K s z t a l t i m p l e m e n t s R y s o w a n i e { }

p u b l i c c l a s s P r o s t o k a t e x t e n d s K s z t a l t { p u b l i c v o i d r y s u j ( ) {

S y s t e m . o u t . p r i n t l n (" Metoda r y s u j k l a s y P r o s t o k a t ") ; }}

p u b l i c c l a s s T r o j k a t e x t e n d s K s z t a l t { p u b l i c v o i d r y s u j ( ) {

S y s t e m . o u t . p r i n t l n (" Metoda r y s u j k l a s y T r o j k a t ") ; }}

Próba usunięcia metody rysuj z klasy Prostokat lub Trojkat, mimo że bezpośrednio nie implementują one interfejsu Rysowanie, skończyłaby się błędem kompilacji.

Monika Wrzosek (IM UG) Programowanie obiektowe 166 / 189

(19)

Pola interfejsów

Interfejsy, oprócz deklaracji metod, mogą również zawierać pola.

Pola interfejsu są zawsze publiczne, statyczne oraz finalne, czyli trzeba im przypisać wartości już w momencie ich deklaracji.

Można stosować typy proste i złożone.

Pola interfejsów są najczęściej wykorzystywane do tworzenia wyliczeń.

p u b l i c i n t e r f a c e n o w y I n t e r f e j s { i n t i = 1 0 0 ;

f l o a t f = 5 . 5 ; c h a r c = ’ a ’;

P r o s t o k a t p r o s t = new P r o s t o k a t ( ) ; }

Pola interfejsu muszą być zainicjowane w chwili ich deklaracji:

p u b l i c i n t e r f a c e n o w y I n t e r f e j s {

i n t i ; // b l a d !

f l o a t f ; // b l a d !

c h a r c ; // b l a d !

P r o s t o k a t p r o s t ; // b l a d ! }

(20)

Metody domyślne

Załóżmy, że w trakcie pracy nad interfejsem Rysowanie zaszła potrzeba jego unowocześnienia i dodania metody rysuj3D. Jeżeli napiszemy

p u b l i c i n t e r f a c e R y s o w a n i e { p u b l i c v o i d r y s u j ( ) ; p u b l i c v o i d r y s u j 3 D ( ) ; }

przestaną działać wszystkie dotychczas napisane klasy (bo nie będą

implementowały metody rysuj3D). Trzeba więc będzie albo dokonać poprawek we wszystkich klasach implementujących interfejs, albo utworzyć zupełnie nowy (np.

o nazwie Rysowanie3D).

Jednak (od wersji 8. Javy) da się poprawić interfejs Rysowanie w taki sposób, aby

"stare" klasy (Prostokat, Trojkat itd.) mogły działać bez problemów:

p u b l i c i n t e r f a c e R y s o w a n i e { p u b l i c v o i d r y s u j ( ) ;

d e f a u l t p u b l i c v o i d r y s u j 3 D ( ) {

S y s t e m . o u t . p r i n t l n ("Domy ś l n y k s z t a ł t 3D") ; }

}

Monika Wrzosek (IM UG) Programowanie obiektowe 168 / 189

(21)

Metoda domyślna (ang. default method ) może mieć definicję (treść) i ta definicja zostanie odziedziczona przez wszystkie klasy implementujące dany interfejs.

Aby utworzyć metodę domyślną, używamy słowa kluczowego default.

p u b l i c i n t e r f a c e R y s o w a n i e { p u b l i c v o i d r y s u j ( ) ;

d e f a u l t p u b l i c v o i d r y s u j 3 D ( ) {

S y s t e m . o u t . p r i n t l n ("Domy ś l n y k s z t a ł t 3D") ; }

}

p u b l i c a b s t r a c t c l a s s K s z t a l t i m p l e m e n t s R y s o w a n i e { }

p u b l i c c l a s s P r o s t o k a t e x t e n d s K s z t a l t { p u b l i c v o i d r y s u j ( ) {

S y s t e m . o u t . p r i n t l n (" Metoda r y s u j k l a s y P r o s t o k a t ") ; }

}

p u b l i c c l a s s T r o j k a t e x t e n d s K s z t a l t { p u b l i c v o i d r y s u j ( ) {

S y s t e m . o u t . p r i n t l n (" Metoda r y s u j k l a s y T r o j k a t ") ; }

p u b l i c v o i d r y s u j 3 D ( ) {

S y s t e m . o u t . p r i n t l n (" Metoda r y s u j 3 D k l a s y T r o j k a t ") ; }

}

(22)

p u b l i c c l a s s Main {

p u b l i c v o i d r y s u j K s z t a l t ( K s z t a l t k ) { k . r y s u j ( ) ;

}

p u b l i c v o i d R y s u j K s z t a l t 3 D ( K s z t a l t k ) { k . r y s u j 3 D ( ) ;

}

p u b l i c s t a t i c v o i d main ( S t r i n g a r g s [ ] ) { Main M = new Main ( ) ;

P r o s t o k a t p r o s t = new P r o s t o k a t ( ) ; T r o j k a t t r = new T r o j k a t ( ) ;

S y s t e m . o u t . p r i n t l n ("Wywo ł a n i e metod r y s u j i r y s u j 3 D : ") ; M. r y s u j K s z t a l t ( p r o s t ) ; // w y w o l a n i e p o l i m o r f i c z n e M. r y s u j K s z t a l t 3 D ( p r o s t ) ; // w y w o l a n i e p o l i m o r f i c z n e M. r y s u j K s z t a l t 3 D ( t r ) ; // w y w o l a n i e p o l i m o r f i c z n e }

}

Metoda r y s u j k l a s y P r o s t o k a t Domy ś l n y k s z t a ł t 3D

Metoda r y s u j 3 D k l a s y T r o j k a t

Monika Wrzosek (IM UG) Programowanie obiektowe 170 / 189

(23)

Wiele interfejsów

W Javie klasa potomna może dziedziczyć tylko po jednej klasie bazowej, nie ma więc znanego m.in. z C++ wielodziedziczenia.

Istnieje natomiast możliwość implementowania wielu interfejsów.

Schemat implementowania wielu interfejsów

[public] [abstract] class nazwa_klasy implements interfejs1, interfejs2, . . . , interfejsN /* pola i metody klasy */

p u b l i c i n t e r f a c e P i e r w s z y I n t e r f e j s { p u b l i c v o i d f ( ) ;

}

p u b l i c i n t e r f a c e D r u g i I n t e r f e j s { p u b l i c v o i d g ( ) ;

}

p u b l i c c l a s s M o j a K l a s a i m p l e m e n t s P i e r w s z y I n t e r f e j s , D r u g i I n t e r f e j s { p u b l i c v o i d f ( ) {

}

p u b l i c v o i d g ( ) { }

}

(24)

Wiele interfejsów

Po co jednak klasa miałaby implementować wiele interfejsów? Czy nie lepiej byłoby napisać jeden? Nie, ponieważ traci się wtedy uniwersalność kodu.

p u b l i c i n t e r f a c e WydajeDzwiek { p u b l i c v o i d g r a j ( ) ;

}

p u b l i c i n t e r f a c e W y s w i e t l a O b r a z { p u b l i c v o i d w y s w i e t l ( ) ;

}

p u b l i c c l a s s R a d i o i m p l e m e n t s WydajeDzwiek { p u b l i c v o i d g r a j ( ) {

// i n s t r u k c j e metody g r a j }

}

p u b l i c c l a s s T e l e w i z o r i m p l e m e n t s WydajeDzwiek , W y s w i e t l a O b r a z { p u b l i c v o i d g r a j ( ) {

// i n s t r u k c j e metody g r a j }

p u b l i c v o i d w y s w i e t l ( ) { // i n s t r u k c j e metody w y s w i e t l }

}

Gdyby nie istniała możliwość implementowania wielu interfejsów, dla telewizora musiałby powstać interfejs np. o nazwie WyswietlaObrazIWydajeDzwiek, w którym zostałyby zadeklarowane metody graj i wyswietl, co ograniczałoby jego zastosowania. Nie byłoby sensu, aby np. klasa Radio implementowała taki interfejs.

Monika Wrzosek (IM UG) Programowanie obiektowe 172 / 189

(25)

Konflikty nazw

Przykład 1.

p u b l i c i n t e r f a c e I { p u b l i c v o i d f ( ) ; }

p u b l i c i n t e r f a c e J { p u b l i c v o i d f ( ) ; }

Czy klasa implementująca oba interfejsy będzie musiała zawierać dwie metody f?

Nie (nie byłoby to możliwe).

p u b l i c c l a s s M o j a K l a s a i m p l e m e n t s I , J { p u b l i c v o i d f ( ) {

S y s t e m . o u t . p r i n t l n (" f ") ; }

}

(26)

Konflikty nazw

Przykład 2.

p u b l i c i n t e r f a c e I { p u b l i c v o i d f (i n t i ) ; }

p u b l i c i n t e r f a c e J { p u b l i c v o i d f (f l o a t x ) ; }

Klasa implementująca oba interfejsy musi zawierać obie metody (będzie więc wyposażona w przeciążone metody f).

p u b l i c c l a s s M o j a K l a s a i m p l e m e n t s I , J { p u b l i c v o i d f (i n t i ) {

S y s t e m . o u t . p r i n t l n (" Argument f u n k c j i : " + i ) }

p u b l i c v o i d f (f l o a t x ) {

S y s t e m . o u t . p r i n t l n (" Argument f u n k c j i : " + x ) }

}

Monika Wrzosek (IM UG) Programowanie obiektowe 174 / 189

(27)

Konflikty nazw

Przykład 3.

p u b l i c i n t e r f a c e I { p u b l i c v o i d f ( ) ; }

p u b l i c i n t e r f a c e J { p u b l i c i n t f ( ) ; }

Poniższa klasa jest nieprawidłowa. W jednej klasie nie mogą istnieć dwie metody o takiej samej nazwie różniące się jedynie typem zwracanego wyniku. Kompilacja zakończy się błędami.

p u b l i c c l a s s M o j a K l a s a i m p l e m e n t s I , J { p u b l i c v o i d f ( ) {

S y s t e m . o u t . p r i n t l n (" F u n k c j a n i c n i e z w r a c a . ") }

p u b l i c i n t f ( ) { r e t u r n 5 ; }

}

(28)

Konflikty nazw

Przykład 4.

p u b l i c i n t e r f a c e I { p u b l i c v o i d f (i n t i ) ; }

p u b l i c c l a s s M o j a K l a s a { p u b l i c i n t f (i n t i ) {

r e t u r n i ; }

}

p u b l i c c l a s s M o j a D r u g a K l a s a e x t e n d s M o j a K l a s a i m p l e m e n t s I { // p u b l i c v o i d f ( i n t i ) {

// } }

Kompilacja powyższego kodu nie uda się niezależnie od tego, czy kod metody będzie ujęty w komentarz, czy nie. Ponieważ klasa MojaDrugaKlasa dziedziczy po MojaKlasa, zawiera metodę public int f(int i), a tym samym nie może implementować interfejsu I.

Monika Wrzosek (IM UG) Programowanie obiektowe 176 / 189

(29)

Dziedziczenie interfejsów

Interfejsy można budować, stosując mechanizm dziedziczenia. Odbywa się to podobnie jak w przypadku klas.

p u b l i c i n t e r f a c e I 1 { p u b l i c v o i d f ( ) ; }

p u b l i c i n t e r f a c e I 2 e x t e n d s I 1 { p u b l i c v o i d g ( ) ;

}

p u b l i c c l a s s M o j a K l a s a i m p l e m e n t s I 2 { p u b l i c v o i d f ( ) {

// t r e ś ć metody f }

p u b l i c v o i d g ( ) { // t r e ś ć metody g }

}

(30)

Interfejs, inaczej niż zwykła klasa, może dziedziczyć nie tylko po jednym, ale po wielu interfejsach.

p u b l i c i n t e r f a c e I 1 { p u b l i c v o i d f ( ) ; }

p u b l i c i n t e r f a c e I 2 { p u b l i c v o i d g ( ) ; }

p u b l i c i n t e r f a c e I 3 e x t e n d s I 1 , I 2 { p u b l i c v o i d h ( ) ;

}

p u b l i c c l a s s M o j a K l a s a i m p l e m e n t s I 3 { p u b l i c v o i d f ( ) { // wymuszona p r z e z I 1

// t r e ś ć metody f }

p u b l i c v o i d g ( ) { // wymuszona p r z e z I 2 // t r e ś ć metody g

}

p u b l i c v o i d h ( ) { // wymuszona p r z e z I 3 // t r e ś ć metody h

} }

Monika Wrzosek (IM UG) Programowanie obiektowe 178 / 189

(31)

Dziedziczenie interfejsów a konflikty nazw

Przykład 1.

p u b l i c i n t e r f a c e I 1 { p u b l i c v o i d f ( ) ; }

p u b l i c i n t e r f a c e I 2 e x t e n d s I 1 { // N i e p r a w i d ł owo ! p u b l i c i n t f ( ) ;

}

Przykład 2.

p u b l i c i n t e r f a c e I 1 { p u b l i c i n t f ( ) ; }

p u b l i c i n t e r f a c e I 2 { p u b l i c d o u b l e f ( ) ; }

p u b l i c i n t e r f a c e I 3 e x t e n d s I 1 , I 2 { // N i e p r a w i d ł owo ! p u b l i c v o i d h ( ) ;

}

Cytaty

Powiązane dokumenty

Do sterowania zaworem służy zewnętrzne elektroniczne urządzenie sterujące (wzmacniacz serwomechanizmu), któ- re wzmacnia analogowy sygnał wejścia (wartość zadaną) w taki

Nie powinno to jednak sprowadzać się wyłącznie do utylitarnego traktowania badań w obrębie nauki o stosunkach międzynarodowych, bowiem zbyt dosłowna interpretacja postulatu

Choć z chwilą upadku Związku Radzieckiego i tym samym proklamowania niepodległości Azerbejdżanu pojawiały się próby jego redefinicji opartej o budowę państwa

Choć z chwilą upadku Związku Radzieckiego i tym samym proklamowania niepodległości Azerbejdżanu pojawiały się próby jego redefinicji opartej o budowę

6–31 grudnia 2021 Zębowice, Knieja, Łąka, Kosice Radawie, Kadłub Wolny, Poczoł- ków, Prusków, Siedliska, Osiecko. Zadanie współfinansowane z budżetu Samorządu

Recent purchases and studies of collections allowed the Russian Museum of Ethnography to extend the collection of artefacts produced by the Bukhari Jews as well as develop a plan for

Jest to przy tym bardzo szerokie, interdyscyplinarne pole badawcze, w którego ramach mieści się „szereg relacji między pracownikami medycznymi oraz między nimi a pacjentami;

elektryczne szyby przednie elektryczne szyby tylne elektrycznie sterowana klapa bagażnika elektrycznie ustawiane fotele elektryczny starter kierownica wielofunkcyjna