Kurs Java

Przesłanianie metod (Overriding)

W poprzednim rozdziale omawialiśmy dziedziczenie i przedstawiliśmy tam jego najważniejsze aspekty. Nie oznacza to jednak, że temat został przez nas wyczerpany. Przesłanianie metod to kontynuacja tego zagadnienia i tym zajmiemy się już za chwilę. Najpierw jednak wprowadzimy mały suplement do samego dziedziczenia, tak aby używane przez nas słownictwo było zawsze zrozumiałe.

Zarówno klasa dziedzicząca, jak i klasa, z której dziedziczymy, bardzo często określane są różnymi nazwami i sami przyznajemy, że można się w tym nazewnictwie nieco pogubić. Postanowiliśmy więc przytoczyć wszystkie znane nam określenia dla tych klas.
Klasa, z której dziedziczymy:
  • klasa bazowa
  • klasa nadrzędna
  • nadklasa
  • superklasa
Klasa dziedzicząca:
  • klasa pochodna
  • klasa podrzędna
  • podklasa
  • subklasa
Postaramy się nie nadużywać różnorodności tych konstrukcji. Zawsze najbardziej podobały się nam określenia klasa bazowa i nadklasa oraz klasa pochodna i podklasa i to tych zwrotów będziemy się trzymać. Teraz, skoro poznaliśmy konwencję nazewniczą, możemy przystąpić do tematu przesłaniania metod.

Appa Notka. Zanim przystąpimy do omawiania metod, przypominamy, że modyfikator dostępu występujący zarówno w przypadku konstruktorów, metod, jak i klas jest opcjonalny. Nie musimy zawsze go wpisywać. Jednak jeśli go ominiemy, wspomniane elementy będą miały ograniczoną widoczność w projekcie. W naszych przykładach dodajemy do metod i konstruktorów najczęściej modyfikator public, aby ułatwić Wam naukę podstaw języka, tak byście nie musieli co chwilę zastanawiać się jaki rodzaj modyfikatora powinien zostać użyty w danym miejscu. Na takie zabawy przyjdzie jeszcze czas. Najpierw warto poznać zasady działania języka, a dopiero później wprowadzać do niego ograniczenia, które wymagają wiedzy na odpowiednim poziomie - między innymi tej znajdującej się w rozdziale Modyfikatory dostępu i niedostępowe.

Sygnatura metody

Najpierw wyjaśnijmy jedno podstawowe pojęcie, takie jak sygnatura metody. Sygnaturą nazywamy nazwę metody wraz z jej parametrami. W przypadku parametrów istotne jest, by miały taką samą liczebność, kolejność oraz takie same typy. Nie ma natomiast znaczenia, jakie mają nazwy.
public void setName(String name) {
	...
}
Do sygnatury metody nie zalicza się typ wartości zwracanej przez metodę. Zatem jeśli utworzymy metodę taką jak poniższa, wówczas będzie ona miała w dalszym ciągu tę samą sygnaturę, co metoda wyżej.
public String setName(String name) {
    ...
}

Taka sama metoda w klasie bazowej i pochodnej

Teraz wyobraźmy sobie sytuację, w której zarówno w klasie bazowej, jak i klasie pochodnej mamy metodę o tej samej sygnaturze. Dodatkowo niech obie metody mają albo dokładnie ten sam typ zwracanej wartości, albo niech metoda klasy pochodnej zwraca typ rozszerzający typ zwracany przez metodę w klasie bazowej.

Ten sam typ wartości zwracanej:
public class Item {

    String name;

    public void setName(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }
}
public class DocumentItem extends Item {

    @Override
    public String getName() {
        return "Document Item";
    }
}
Typ rozszerzający (String) typ wartości zwracanej (Object):
public class Item {

    String name;

    public void setName(String name) {
        this.name = name;
    }
    
    public Object getName() {
        return name;
    }
}
public class DocumentItem extends Item {

    @Override
    public String getName() {
        return "Document Item";
    }
}
Appa Notka. Jeśli nie wiesz, gdzie jest napisane to, że String rozszerza Object, to wróć do rozdziału Klasy - Dziedziczenie i przeczytaj raz jeszcze paragraf "Klasa Object". Dowiesz się z niego, skąd się bierze nasze stwierdzenie o dziedziczeniu z tej klasy.
W tym miejscu możemy powiedzieć, że właśnie zaimplementowaliśmy mechanizm nazywający się przesłonięciem metody. Metoda getName znajdująca się w klasie pochodnej przesłania metodę getName znajdującą się w klasie bazowej. Teraz zobaczmy, jakie to ma konsekwencje w kontekście wykonywanego programu.
public class Start {

    public static void main(String[] args) {
        
        Item item = new Item();
        item.setName("Simple Item");        
        System.out.println(item.getName());
        
        Item item2 = new DocumentItem();
        item2.setName("Simple Item");
        System.out.println(item2.getName());      
    }
}
Uruchamiając taki kod, najpierw wydrukujemy na konsoli tekst "Simple Item", a następnie..."Document Item". Dzieje się tak, mimo że wcześniej zarówno w obiekcie item jaki i w obiekcie item2 ustawiliśmy tę samą nazwę - "Simple Item". Powstaje więc pytanie, co się właściwie stało?

Odpowiedzią jest właśnie termin przesłonięcie metody. Zauważmy bowiem, że obiekt item, to instancja klasy Item, podczas gdy obiekt item2 to instancja klasy DocumentItem. Skoro więc przesłoniliśmy metodę getName w klasie DocumentItem, to w przypadku stworzenia obiektu tego typu, wykonana zostanie metoda przesłaniająca, a ona zwraca ustawiony na sztywno tekst "Document Item".
Appa Notka. Z tego co zauważyliśmy zdarzają się kursy, w których podczas przedstawiania tematu przesłaniania metody popełniany jest pewien błąd (wynikający pewnie z niedopatrzenia). Czasem przesłanianie metody opisywane jest jako zgodność tylko i wyłącznie sygnatury metody. Jak wiemy typ zwracanej wartości nie wchodzi w skład sygnatury, a on jest tutaj również niezwykle istotny. Zalecamy czujność w tym temacie. Pamiętajcie, że aby poprawnie przesłonić metodę musimy również zwracać uwagę na typ (musi być ten sam albo rozszerzający).

Super metoda w superklasie

Na początek mała uwaga. Co prawda mieliśmy zawsze używać słowa klasa bazowa albo nadklasa, ale jednak zrobimy jeden wyjątek. Jak wiemy, klasa bazowa nazywana jest czasem superklasą. Nie ma w tym nic dziwnego, bo żeby powstała jakakolwiek klasa pochodna musi istnieć najpierw klasa wzorcowa - superklasa. Termin ten przyda się teraz, aby zrozumieć, do czego służy w Javie słowo kluczowe o nazwie super.

Jeśli w podklasie chcemy wywołać kod przesłoniętej metody z superklasy, wówczas używamy słowa kluczowego super:
public class Item {

    String name;

    public void setName(String name) {
        this.name = name;
    }
    
    public String getName() {
        System.out.println("Super metoda w superklasie");
        return name;
    }
}
public class DocumentItem extends Item {

    @Override
    public String getName() {
        super.getName();
        return "Document Item";
    }
}
Wywołanie programu klasy Start (zdefiniowanej tak jak poprzednio) będzie szczególnie ciekawe w przypadku obiektu item2. Mamy tu do czynienia z instancją klasy DocumentItem, a to jak wiemy oznacza, że wywołujemy kod metody przesłaniającej. W obecnym przypadku pierwszą instrukcją tej metody jest "nakaz" wywołania metody z klasy bazowej (z superklasy). Z tego powodu proces wykonawczy programu przejdzie do metody getName w klasie Item i wydrukuje na konsoli tekst "Super metoda w superklasie".

Wykona się tu cała metoda getName, a więc jako wynik tej metody zwrócona zostanie nazwa ustawiona w polu name. Nie będzie to jednak miało wpływu na dalszą część programu, gdyż obecnie nie przechwytujemy tego, co zwraca ta metoda. Zaraz po tym proces wróci do klasy DocumentItem i zwróci jako wynik (wykonania metody przesłaniającej) tekst "Document Item", który następnie zostanie wydrukowany na konsoli.

Przykład w programie

Teraz zobaczmy jak kod naszego programu wygląda w IDE:
public class Start {

    public static void main(String[] args) {
        
        Item item = new Item();
        item.setName("Simple Item");        
        System.out.println(item.getName());
        
        Item item2 = new DocumentItem();
        item2.setName("Simple Item");
        System.out.println(item2.getName());      
    }
}
Program z wywołaniem metody przesłonionej
Zdjęcie autora
Autor: Jarek Klimas
Data: 03 stycznia 2024
Labele: Backend, Podstawowy, Java
Masz pytanie dotyczące tego rozdziału? Zadaj je nam!
Masz pytanie dotyczące prezentowanego materiału?
Coś jest dla Ciebie niejasne i Twoje wątpliwości przeszkadzają Ci w pełnym zrozumieniu treści?
Napisz do nas maila, a my chętnie znajdziemy odpowiednie rozwiązanie.
Najciekawsze pytania wraz z odpowiedziami będziemy publikować pod rozdziałem.
Nie czekaj. Naucz się programować jeszcze lepiej.
kursjava@javappa.com

Stale się rozwijamy, a więc bądź na bieżąco!
Na ten adres będziemy przesyłać informacje o ważniejszych aktualizacjach, a także o nowych materiałach pojawiających się na stronie.
Polub nas na Facebooku:
Nasi partnerzy: stackshare
Javappa to również profesjonalne usługi programistyczne oparte o technologie JAVA. Jeśli chesz nawiązać z nami kontakt w celu uzyskania doradztwa bądź stworzenia aplikacji webowej powinieneś poznać nasze doświadczenia.
Kliknij O nas .


Pozycjonowanie stron: Grupa TENSE