Menu
Coddy logo textTech

Dynamisches Dispatching

Teil des Abschnitts Objektorientierte Programmierung der Kotlin-Journey von Coddy. Lektion 27 von 57.

Polymorphismus bedeutet, dass ein Aufruf sich auf viele Arten verhalten kann. Wenn du eine überschriebene Methode über einen Oberklassen- oder Schnittstellentyp aufrufst, betrachtet Kotlin während der Programmausführung die tatsächliche Klasse des Objekts und führt die Version dieser Klasse aus. Dies ist dynamischer Dispatch:

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"
}

Innerhalb von main:

val barn: List<Animal> = listOf(Dog("Rex"), Cow("Bess"), Dog("Fido"))
for (a in barn) println(a.speak())

Ausgabe:

Rex: Woof
Bess: Moo
Fido: Woof

Der aufrufende Code ändert sich nicht, wenn eine neue Unterklasse hinzukommt. totalArea unten wurde für jede Shape geschrieben, daher funktioniert er auch mit Triangle, obwohl niemand ihn geändert hat:

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

Innerhalb von main:

println(totalArea(listOf(Square(2.0), Triangle(3.0, 4.0))))

Ausgabe:

10.0

Der Dispatch betrachtet das Objekt, nicht die Variable. Wenn du derselben Variable ein anderes Objekt zuweist, ändert sich, welcher Code beim nächsten Aufruf ausgeführt wird:

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() + "!"
}

Innerhalb von main:

var mode: Mode = Normal()
println(mode.format("hello"))
mode = Shout()
println(mode.format("hello"))

Ausgabe:

hello
HELLO!

Polymorphismus ersetzt Prüfungen wie if (kind == "dog"), die andernfalls überall dort wiederholt würden, wo Tiere verwendet werden. Jede Klasse behält ihr eigenes Verhalten, und das Hinzufügen einer Tierart bedeutet, eine Klasse hinzuzufügen:

// ohne Polymorphismus: jede Funktion prüft die Art
fun sound(kind: String) = when (kind) { "dog" -> "Woof"; "cow" -> "Moo"; else -> "?" }

// mit Polymorphismus: die neue Art bringt ihr eigenes Verhalten mit
class Sheep(name: String) : Animal(name) {
    override fun sound() = "Baa"
}
challenge icon

Aufgabe

Einfach

Character(name, hp) ist eine offene Klasse. takeHit(damage) zieht den Schaden von hp ab (niemals unter 0), und status() gibt bei 0 Ada: 30 hp oder Ada: down zurück. hp kann überall gelesen, aber nur innerhalb von Character geändert werden. Vervollständige die Unterklassen, indem du takeHit überschreibst und den geänderten Schaden an super.takeHit weitergibst: Warrior (40 hp) erleidet die Hälfte des Schadens, abgerundet; Mage (25 hp) besitzt einen Schild, der den gesamten ersten Treffer absorbiert; Rogue (30 hp) weicht jedem zweiten Treffer aus, den er erhält (dem 2., 4. ...).

Der bereitgestellte Code liest die Gruppe als warrior:Ada,mage:Bo,rogue:Cy ein und führt anschließend all 10 oder Ada 12 als Treffer aus. Er ruft takeHit über den Typ Character auf und gibt schließlich jedes status() aus.

Dein Code gehört in Character.kt, Warrior.kt, Mage.kt und Rogue.kt. Main.kt enthält den bereitgestellten Eingabe-/Ausgabecode und kann nicht bearbeitet werden.

Probier es selbst

fun main() {
    // Gelieferter Ein-/Ausgabecode: belasse ihn wie er ist
    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())
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Kotlin-Playground