Menu
Coddy logo textTech

Dlaczego Java nie obsługuje wielodziedziczenia klas

Część sekcji Programowanie obiektowe ścieżki Java w Coddy. Lekcja 26 z 87.

Java celowo uniemożliwia klasie dziedziczenie po więcej niż jednej klasie. To ograniczenie wynika z problemu znanego jako problem diamentu.

Wyobraź sobie, że Java pozwala na wielokrotne dziedziczenie. Rozważmy ten hipotetyczny scenariusz:

// Ten kod NIE jest poprawny w Java
public class A {
    public void greet() {
        System.out.println("Hello from A");
    }
}

public class B extends A {
    @Override
    public void greet() {
        System.out.println("Hello from B");
    }
}

public class C extends A {
    @Override
    public void greet() {
        System.out.println("Hello from C");
    }
}

// NIEDOZWOLONE w Java!
public class D extends B, C { }

Jeśli wywołasz greet() na obiekcie D, która wersja powinna się uruchomić? Ta z B czy z C? Ta niejednoznaczność to problem diamentu, nazwany tak ze względu na kształt diamentu utworzony przez diagram dziedziczenia.

Rozwiązanie w Javie jest proste: zezwolić na dziedziczenie tylko po jednej klasie. Java oferuje jednak alternatywę. Klasa może implementować wiele interfejsów, o których dowiesz się w jednym z kolejnych rozdziałów.

Interfejsy rozwiązują problem diamentowy w inny sposób i zapewniają elastyczność bez niejednoznaczności.

// To JEST poprawne w Java
public class Dog extends Animal implements Runnable, Comparable<Dog> {
    // Może implementować wiele interfejsów
}

Ten wybór projektowy sprawia, że model dziedziczenia w Javie pozostaje przejrzysty i przewidywalny, a jednocześnie umożliwia ponowne wykorzystanie kodu za pomocą interfejsów.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system odtwarzacza muzyki, który demonstruje zasadę pojedynczego dziedziczenia w Javie i pokazuje, jak mimo tego ograniczenia można tworzyć elastyczne projekty.

Wyobraź sobie, że chcesz stworzyć inteligentny głośnik łączący funkcje odtwarzacza muzyki i asystenta głosowego. Ponieważ Java nie pozwala dziedziczyć po wielu klasach, musisz starannie zaprojektować hierarchię, korzystając z pojedynczego dziedziczenia.

Uporządkujesz kod w czterech plikach:

  • AudioDevice.java: Utwórz klasę bazową wspólną dla wszystkich urządzeń odtwarzających dźwięk. Powinna zawierać:
    • Prywatne pole deviceName (String)
    • Konstruktor przyjmujący nazwę urządzenia
    • Metodę getDeviceName(), która zwraca nazwę
    • Metodę playSound(), która wyświetla: [deviceName]: Playing audio
  • MusicPlayer.java: Utwórz klasę dziedziczącą po AudioDevice i dodającą funkcje związane z muzyką:
    • Prywatne pole currentSong (String)
    • Konstruktor przyjmujący nazwę urządzenia i używający super(deviceName)
    • Metodę setSong(String song), która ustawia bieżący utwór
    • Przesłoń playSound(), aby wyświetlała: [deviceName]: Now playing - [currentSong]
  • SmartSpeaker.java: Utwórz klasę dziedziczącą po MusicPlayer (a nie jednocześnie po MusicPlayer i jakiejś klasie VoiceAssistant — to byłoby dziedziczenie wielokrotne!). Inteligentny głośnik rozszerza możliwości odtwarzacza muzyki o funkcje głosowe:
    • Prywatne pole assistantName (String)
    • Konstruktor przyjmujący nazwę urządzenia i nazwę asystenta oraz używający super(deviceName)
    • Metodę voiceCommand(String command), która wyświetla: [assistantName]: Processing "[command]"
    • Metodę getFullInfo(), która wyświetla: [deviceName] with [assistantName] assistant
  • Main.java: Zademonstruj swój łańcuch dziedziczenia wielopoziomowego. Otrzymasz trzy dane wejściowe: nazwę urządzenia (String), nazwę asystenta (String) i nazwę utworu (String). Utwórz obiekt SmartSpeaker, ustaw utwór, a następnie wywołaj kolejno te metody:
    • getFullInfo()
    • playSound()
    • voiceCommand("skip to next")

Otrzymasz trzy dane wejściowe: nazwę urządzenia, nazwę asystenta i nazwę utworu.

Zwróć uwagę, jak SmartSpeaker zyskuje możliwości odtwarzania muzyki, dziedzicząc po MusicPlayer w łańcuchu (AudioDevice -> MusicPlayer -> SmartSpeaker), zamiast próbować dziedziczyć po wielu niepowiązanych klasach. Tak Java dba o to, by dziedziczenie było przejrzyste i jednoznaczne!

Spróbuj swoich sił

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Wczytaj dane wejściowe
        String deviceName = scanner.nextLine();
        String assistantName = scanner.nextLine();
        String songName = scanner.nextLine();
        
        // TODO: Utwórz SmartSpeaker z deviceName i assistantName
        
        // TODO: Ustaw utwór za pomocą setSong()
        
        // TODO: Wywołaj getFullInfo()
        
        // TODO: Wywołaj playSound()
        
        // TODO: Wywołaj voiceCommand("skip to next")
    }
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator Java online