Dynamiczne wiązanie
Część sekcji Programowanie obiektowe ścieżki Kotlin w Coddy. Lekcja 27 z 57.
Polimorfizm oznacza, że jedno wywołanie może działać na wiele sposobów. Gdy wywołujesz przesłoniętą metodę za pośrednictwem typu nadklasy lub interfejsu, Kotlin sprawdza rzeczywistą klasę obiektu podczas działania programu i wykonuje wersję tej klasy. To jest dynamiczne wiązanie:
abstract class Animal(val name: String) {
abstract fun sound(): String
fun speak() = "$name: ${sound()}"
}
class Dog(name: String) : Animal(name) {
override fun sound() = "Woof"
}
class Cow(name: String) : Animal(name) {
override fun sound() = "Moo"
}Wewnątrz main:
val barn: List<Animal> = listOf(Dog("Rex"), Cow("Bess"), Dog("Fido"))
for (a in barn) println(a.speak())Wynik:
Rex: Woof
Bess: Moo
Fido: WoofKod wywołujący nie zmienia się, gdy pojawia się nowa podklasa. Poniższa funkcja totalArea została napisana z myślą o dowolnym Shape, więc działa również z Triangle, mimo że nikt jej nie zmieniał:
interface Shape {
fun area(): Double
}
class Square(val side: Double) : Shape {
override fun area() = side * side
}
class Triangle(val base: Double, val height: Double) : Shape {
override fun area() = base * height / 2
}
fun totalArea(shapes: List<Shape>) = shapes.sumOf { it.area() }Wewnątrz main:
println(totalArea(listOf(Square(2.0), Triangle(3.0, 4.0))))Wynik:
10.0Wysyłanie wywołania zależy od obiektu, a nie od zmiennej. Przypisanie innego obiektu do tej samej zmiennej zmienia, który kod zostanie uruchomiony przy następnym wywołaniu:
interface Mode {
fun format(text: String): String
}
class Normal : Mode {
override fun format(text: String) = text
}
class Shout : Mode {
override fun format(text: String) = text.uppercase() + "!"
}Wewnątrz main:
var mode: Mode = Normal()
println(mode.format("hello"))
mode = Shout()
println(mode.format("hello"))Wynik:
hello
HELLO!Polimorfizm zastępuje sprawdzanie warunków, takich jak if (kind == "dog"), które w przeciwnym razie trzeba by powtarzać wszędzie tam, gdzie używane są zwierzęta. Każda klasa zachowuje własne zachowanie, a dodanie nowego rodzaju zwierzęcia oznacza dodanie jednej klasy:
// bez polimorfizmu: każda funkcja sprawdza typ
fun sound(kind: String) = when (kind) { "dog" -> "Woof"; "cow" -> "Moo"; else -> "?" }
// z polimorfizmem: nowy typ ma własne zachowanie
class Sheep(name: String) : Animal(name) {
override fun sound() = "Baa"
}Wyzwanie
ŁatwyCharacter(name, hp) jest klasą open. takeHit(damage) odejmuje obrażenia od hp (nigdy nie schodzi poniżej 0), a status() zwraca Ada: 30 hp lub Ada: down przy wartości 0. Wartość hp można odczytać z dowolnego miejsca, ale zmieniać ją można tylko wewnątrz Character. Uzupełnij podklasy, nadpisując takeHit i przekazując zmienione obrażenia do super.takeHit: Warrior (40 hp) otrzymuje połowę obrażeń, zaokrągloną w dół; Mage (25 hp) ma tarczę, która pochłania całe obrażenia z pierwszego trafienia; Rogue (30 hp) unika co drugiego otrzymanego trafienia (2., 4. itd.).
Dostarczony kod wczytuje drużynę w postaci warrior:Ada,mage:Bo,rogue:Cy, a następnie zadaje obrażenia za pomocą all 10 lub Ada 12. Wywołuje takeHit za pośrednictwem typu Character, a na końcu wyświetla każdy wynik status().
Twój kod umieść w plikach Character.kt, Warrior.kt, Mage.kt i Rogue.kt. Main.kt zawiera dostarczony kod wejścia/wyjścia i nie można go edytować.
Spróbuj swoich sił
fun main() {
// Dostarczony kod wejścia/wyjścia: pozostaw go bez zmian
val input = generateSequence(::readLine).toList()
val party = input[0].split(",").map { spec ->
val (kind, name) = spec.split(":")
when (kind) {
"warrior" -> Warrior(name)
"mage" -> Mage(name)
else -> Rogue(name)
}
}
for (cmd in input.drop(1)) {
val (target, damage) = cmd.split(" ")
for (c in party) {
if (target == "all" || target == c.name) c.takeHit(damage.toInt())
}
}
for (c in party) println(c.status())
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Klasy i obiekty
Praca z plikamiDefiniowanie klasKonstruktory główneBloki init i walidacjaKonstruktory dodatkowePodsumowanie — karta biblioteczna4Dziedziczenie
Klasy otwarte i podklasyNadpisywanie składowychsuper i konstruktoryKlasy abstrakcyjnePodsumowanie – katalog pracowników7Klasy specjalne
Klasy danychKlasy wyliczenioweHierarchie zapieczętowaneequals, hashCode, toStringPodsumowanie — śledzenie zamówień10Wzorce projektowe, część 2
Delegowanie za pomocą byWzorzec dekoratoraWzorzec budowniczegoWzorzec stanu2Właściwości i hermetyzacja
Własne akcesoryModyfikatory widocznościPrywatne setterylazy i lateinitPowtórka – konto bankowe5Interfejsy
Deklarowanie interfejsówMetody domyślneWiele interfejsówInterfejsy jako typyPowtórzenie – metody płatności8Typy generyczne
Funkcje generyczneKlasy generyczneOgraniczenia typówKowariancja i kontrawariancja: in i outPodsumowanie — kolejka priorytetowa11Projekt: Zarządzanie biblioteką
Książki i czytelnicyWypożyczanie książek3Obiekty i obiekty towarzyszące
Deklaracje obiektówObiekty towarzysząceStałe z constWyrażenia obiektowePowtórzenie — kasa biletowa6Polimorfizm
Dynamiczne wiązanieSprawdzanie typów i inteligentne rzutowaniewhen z typamiFunkcje rozszerzającePodsumowanie – opiekun zooPoćwicz samodzielnie: Edytor online Kotlin