Sobes.tech
Junior

Jak zorganizowane są dziedziczenie i polimorfizm w Kotlin?

sobes.tech AI

Odpowiedź od AI

W Kotlinie dziedziczenie realizowane jest za pomocą mechanizmów klas i interfejsów. Klasy mogą dziedziczyć tylko po jednej innej klasie (dziedziczenie pojedyncze), ale mogą implementować wiele interfejsów.

Domyślnie wszystkie klasy w Kotlinie są 'finalne', czyli nie mogą być dziedziczone. Aby umożliwić dziedziczenie, klasa musi być wyraźnie oznaczona słowem kluczowym open.

Dziedziczenie klas:

open class Shape { // Klasa bazowa, oznaczona jako open
    open fun draw() { // Metoda, którą można nadpisać
        println("Rysowanie kształtu")
    }
}

class Circle : Shape() { // Dziedziczenie po Shape
    override fun draw() { // Nadpisanie metody
        println("Rysowanie koła")
    }
}

W konstruktorze klasy pochodnej konieczne jest wywołanie konstruktora klasy bazowej jawnie lub niejawnie.

Interfejsy:

Interfejsy w Kotlinie są podobne do tych w Java 8 i mogą zawierać metody abstrakcyjne oraz metody z implementacją. Klasy mogą implementować jeden lub wiele interfejsów.

interface Drawable {
    fun draw() // Metoda abstrakcyjna
    fun description() { // Metoda z domyślną implementacją
        println("To jest obiekt do rysowania")
    }
}

class Square : Drawable {
    override fun draw() { // Implementacja metody abstrakcyjnej
        println("Rysowanie kwadratu")
    }
    // description() może być nadpisane, ale nie musi
}

Polimorfizm:

Polimorfizm w Kotlinie osiąga się dzięki możliwości pracy z obiektami różnych klas przez wspólny typ bazowy (klasę lub interfejs). Pozwala to wywołać metody specyficzne dla rzeczywistego typu obiektu w czasie wykonywania.

fun render(drawable: Drawable) {
    drawable.draw() // Wywołanie metody draw() w zależności od rzeczywistego typu obiektu
    drawable.description()
}

fun main() {
    val circle: Shape = Circle() // Obiekt Circle traktowany jako Shape
    circle.draw() // Wywołanie draw() z Circle dzięki polimorfizmowi

    val square: Drawable = Square() // Obiekt Square traktowany jako Drawable
    render(square) // Wywołanie draw() i description() z Square
}

W tym przykładzie render działa z dowolnym obiektem implementującym Drawable. Wywołanie drawable.draw() wykona implementację metody draw dla konkretnego typu obiektu (Square w tym przypadku).

Różnice między dziedziczeniem klas a implementacją interfejsów:

  • Dziedziczenie klasy: Ustanawia relację "jest" (is-a). Klasa pochodna dziedziczy stan i zachowanie klasy bazowej.
  • Implementacja interfejsu: Definiuje "zdolność" lub "kontrakt". Klasa obiecuje dostarczyć implementację wszystkich abstrakcyjnych metod interfejsu.

Tabela porównawcza:

Cecha Dziedziczenie klasy Implementacja interfejsu
Typ relacji jest-a Zdolność / Kontrakt
Wielokrotność Pojedyncza Wielokrotna
Stan Dziedziczy stan klasy bazowej Nie dziedziczy stanu
Implementacja metod Może zawierać implementację Może zawierać domyślną implementację (od Java 8)