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) |