Wybierz region
pl
  • PL
  • EN
Wydrukuj

Cykl: Podstawy Javy. Epizod 2: Przesłanianie metod

W ostatnim wpisie, dotyczącym Javy, znalazło się kilka uwag na temat przesłaniania metod. Mechanizm ten warto omówić nieco szerzej, ponieważ z jednej strony trudno wyobrazić sobie system stworzony w Javie, w którym nie byłby on szeroko wykorzystywany, z drugiej zaś – jest on stosunkowo prosty, choć nie jest całkiem pozbawiony pułapek.

Zacznijmy od odświeżenia podstaw. Stwórzmy klasę reprezentującą specjalistę IT:

oraz dziedziczącą po niej klasę reprezentującą programistę Java:

Jeżeli następnie stworzymy obiekty: ItSpecialist oraz JavaDeveloper:

to po wywołaniu metody introduceYourself() na każdym z nich:

otrzymamy na wyjściu spodziewane komunikaty:

I'm an IT specialist

I'm Java Developer

Powyższy, rozgrzewkowy przykład można nieco skomplikować. Java pozwala nam utworzyć obiekt w następujący sposób:

W tym wypadku można mieć już pewne wątpliwości, czy metoda introduceYourself() poinformuje nas, że utworzony obiekt jest specjalistą IT czy programistą Java. Po uruchomieniu, na wyjściu ukaże się jednak zawsze komunikat: I'm Java Developer. Przyczyną takiego stanu rzeczy jest jeden z aspektów polimorfizmu określany jako virtual method invocation. Jego istota polega na tym, że decyzja o tym, która metoda ma zostać wywołana podejmowana jest dopiero w trakcie działania programu (a nie na etapie kompilacji) i zależy ona nie od typu zmiennej, ale od obiektu, do którego odnosi się referencja. W przytoczonym przykładzie referencja wskazuje na obiekt JavaDeveloper, niezależnie od tego, że przypisano ją do zmiennej typu ItSpecialist. W konsekwencji wywołana zostaje metoda introduceYourself() z klasy JavaDeveloper.

Patrząc na przykład z poprzedniego akapitu możemy zapytać: jaka jest korzyść z virtual method invocation, skoro znamy typ tworzonego obiektu już na etapie kompilacji? Po co zatem sprawdzać go dopiero w run time? Skomplikujmy zatem nasz przykład nieco bardziej. Do klasy ItSpecialist dodajmy metodę:

Metodę tę przesłońmy w klasie JavaDeveloper:

oraz dodajmy kolejną klasę dziedziczącą po ItSpecialist i tam także przesłońmy powyższą metodę:

Nie wdając się w szczegóły implementacyjne widzimy, że na obiektach każdej z dwóch klas dziedziczących po ItSpecialist możemy wykonać metodę doYourJob(), przy czym zachowanie każdego z nich będzie inne – zależnie od klasy.

W tym momencie możemy przejść do prawdziwej korzyści z virtual method invocation. Stwórzmy metodę:

Przyjmuje ona jako argument obiekt ItSpecialist i następnie wykonuje na nim metodę doYourJob(). Do metody tej przekazać możemy jednak także obiekty klas dziedziczących, tj. JavaDeveloper oraz SystemAdministrator. Decyzja dotycząca tego, która z metod doYourJob() ma zostać wykonana podjęta zostanie dopiero w run time, na podstawie typu obiektu, do którego odnosi się referencja.

Gdyby Java nie dysponowała mechanizmem virtual method invocation, to powyższa, prosta metoda sendItSpeialistToWork(ItSpecialist itSpecialist) przybrać musiałaby mniej więcej taką formę:

Każda kolejna klasa dziedzicząca po ItSpecialist wymagałaby dodania swojego własnego else if.

Innym ważnym aspektem przesłaniania metod jest dostęp do nich w poszczególnych klasach Zasadą jest, iż metoda w klasie dziedziczącej nie może być mniej dostępna niż metoda w klasie bazowej. Jeżeli zatem mielibyśmy następującą metodę w klasie bazowej:

to w klasie dziedziczącej możemy przesłonić ją np. w ten sposób:

Ale już próba takiego przesłonięcia wywoła błąd kompilacji:

Jak widać, Java dostarcza programiście możliwość wygodnej pracy z metodami w systemie dziedziczących po sobie klas. Implementowanie przesłaniania metod nie należy do trudnych, może jednak niekiedy sprawiać niespodzianki, z uwagi na fakt, że decyzja do wykonaniu konkretnej metody zapada dopiero w run time.

Galeria


Michał Karmelita

Z wykształcenia prawnik i informatyk. Zawodowo i z zamiłowania programista Java. W wolnym czasie stara się oddawać przyjemnościom i unikać przykrości. Zapalony podróżnik.


Wydrukuj