Przesłanianie metod (w czasie wykonywania)
Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 29 z 87.
Podczas gdy przeciążanie metod jest rozstrzygane w czasie kompilacji, przesłanianie metod demonstruje polimorfizm czasu wykonania. W tym przypadku Java decyduje, którą metodę wykonać, gdy program jest uruchomiony, na podstawie rzeczywistego typu obiektu, a nie zadeklarowanego typu zmiennej.
Dowiedziałeś się o przesłanianiu metod w rozdziale Inheritance. O tym, że jest polimorficzne, decyduje sposób, w jaki Java obsługuje je, gdy referencja do klasy nadrzędnej wskazuje na obiekt klasy potomnej:
public class Animal {
public void speak() {
System.out.println("Some sound");
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Bark!");
}
}
public class Cat extends Animal {
@Override
public void speak() {
System.out.println("Meow!");
}
}Teraz zobacz, co się dzieje, gdy używamy odwołań do typu nadrzędnego:
Animal myPet = new Dog();
myPet.speak(); // Wynik: Bark!
myPet = new Cat();
myPet.speak(); // Wynik: Meow!Mimo że myPet jest zadeklarowane jako Animal, Java wywołuje nadpisaną metodę na podstawie rzeczywistego typu obiektu w czasie wykonywania. Nazywa się to dynamicznym wiązaniem metod. JVM sprawdza, czym naprawdę jest obiekt, a nie co mówi zmienna.
Umożliwia to tworzenie zaawansowanych wzorców, takich jak jednolite przetwarzanie różnych obiektów:
Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
pet.speak(); // Każdy wywołuje własną wersję
}Polimorfizm czasu wykonania pozwala pisać elastyczny kod, który działa z typami nadrzędnymi, a jednocześnie automatycznie korzysta z właściwego zachowania typów potomnych.
Wyzwanie
ŁatwyZbudujmy system powiadomień, który pokazuje polimorfizm czasu wykonania w praktyce. Utworzysz hierarchię typów powiadomień, w której to samo wywołanie metody daje różne wyniki, zależnie od rzeczywistego typu obiektu w czasie wykonania.
Rozmieścisz kod w czterech plikach:
Notification.java: Utwórz klasę bazową wspólną dla wszystkich powiadomień. Każde powiadomienie ma polerecipient(String). Dodaj konstruktor, który je inicjalizuje, metodę pobierającą oraz metodęsend(), która wyświetla:Sending notification to [recipient]EmailNotification.java: Utwórz klasę rozszerzającą Notification. Powiadomienia e-mail dodają polesubject. Użyjsuperdo inicjalizacji klasy nadrzędnej. Przesłoń metodęsend(), aby wyświetlała:Emailing [recipient]: [subject]SMSNotification.java: Utwórz kolejną klasę rozszerzającą Notification. Powiadomienia SMS dodają polephoneNumber. Przesłoń metodęsend(), aby wyświetlała:Texting [phoneNumber] for [recipient]Main.java: Tutaj polimorfizm czasu wykonania pokazuje swoje możliwości! Otrzymasz trzy dane wejściowe: nazwę odbiorcy, temat wiadomości e-mail i numer telefonu. Utwórz tablicę odwołań typuNotificationzawierającą trzy obiekty w następującej kolejności: podstawowy obiekt Notification, EmailNotification i SMSNotification (wszystkie z tym samym odbiorcą). Następnie przejdź pętlą przez tablicę i wywołajsend()dla każdego elementu. Zobacz, jak Java automatycznie wywołuje właściwą przesłoniętą wersję metody, zależnie od rzeczywistego typu każdego obiektu!
Otrzymasz trzy dane wejściowe: nazwę odbiorcy (String), temat wiadomości e-mail (String) i numer telefonu (String).
Wynik powinien zawierać trzy wiersze — każde wywołanie send() daje inny wynik, mimo że wywołujesz tę samą metodę na odwołaniach typu Notification. Właśnie tak działa dynamiczne wiązanie metod!
Spróbuj swoich sił
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Wczytaj dane wejściowe
String recipient = scanner.nextLine();
String subject = scanner.nextLine();
String phoneNumber = scanner.nextLine();
// TODO: Utwórz tablicę referencji do obiektów Notification zawierającą 3 elementy
// Tablica powinna zawierać (w tej kolejności):
// 1. Obiekt bazowej klasy Notification
// 2. Obiekt klasy EmailNotification
// 3. Obiekt klasy SMSNotification
// Wszystkie z tym samym odbiorcą
// TODO: Przejdź przez tablicę i wywołaj send() dla każdego elementu
// To ilustruje polimorfizm dynamiczny!
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektySłowo kluczowe thisMetodyPola (atrybuty)Metoda konstruktoraPrzeciążanie konstruktoraPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawy dziedziczenia (extends)Słowo kluczowe superPrzesłanianie metod (@Override)Łańcuch wywołań konstruktorówKlasa ObjectDziedziczenie jedno- i wielopoziomoweDlaczego Java nie obsługuje wielodziedziczenia klasPodsumowanie – hierarchia pracowników7Metody specjalne i klasa Object
Metoda toString()equals() i hashCode()Metoda clone()compareTo() i ComparableInterfejs ComparatorPodsumowanie — własne sortowanie10Obsługa wyjątków w programowaniu obiektowym
Hierarchia klas wyjątkówWłasne wyjątkiBłędy sprawdzane a niesprawdzaneWzorzec try-with-resourcesPodsumowanie – zweryfikowany użytkownik2Modyfikatory dostępu i enkapsulacja
Przegląd poziomów dostępuMetody pobierające i ustawiająceUkrywanie informacjiSłowo kluczowe finalPodsumowanie — menedżer kont bankowych5Polimorfizm
Podstawy przeciążania metodPrzesłanianie metod (w czasie wykonywania)Rzutowanie w górę i w dółOperator instanceofKlasy i metody abstrakcyjnePowtórka — kalkulator figur8Zaawansowane koncepcje OOP
Kompozycja a dziedziczenieAgregacja a kompozycjaKlasy wewnętrzne, zagnieżdżone i anonimoweEnumy i metody enumówRekordy (Java 16+)Klasy zapieczętowane (Java 17+)11Wzorce projektowe — część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec BuilderWzorzec ObserverWzorzec Strategy3Właściwości klas i składowe statyczne
Zmienne instancji a zmienne statyczneMetody statyczneBloki statyczneStałe (static final)Powtórka – Counter i Utility6Interfejsy i klasy abstrakcyjne
Wprowadzenie do interfejsówImplementowanie interfejsówImplementacja wielu interfejsówMetody domyślne i statyczne w interfejsieKlasy abstrakcyjne a interfejsyInterfejsy funkcyjnePodsumowanie — system płatnościPoćwicz samodzielnie: Kompilator Java online