Dispatch dinamico
Fa parte della sezione Programmazione orientata agli oggetti del percorso Kotlin di Coddy. Lezione 27 di 57.
Il polimorfismo significa che una chiamata può comportarsi in molti modi. Quando chiami un metodo sovrascritto tramite un tipo di superclasse o di interfaccia, Kotlin esamina la classe effettiva dell'oggetto mentre il programma è in esecuzione ed esegue la versione di quella classe. Questo è il dispatch dinamico:
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"
}Dentro main:
val barn: List<Animal> = listOf(Dog("Rex"), Cow("Bess"), Dog("Fido"))
for (a in barn) println(a.speak())Output:
Rex: Woof
Bess: Moo
Fido: WoofIl codice chiamante non cambia quando compare una nuova sottoclasse. totalArea qui sotto è stato scritto per qualsiasi Shape, quindi funziona anche con Triangle, anche se nessuno lo ha modificato:
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() }Dentro main:
println(totalArea(listOf(Square(2.0), Triangle(3.0, 4.0))))Output:
10.0La chiamata di metodo esamina l’oggetto, non la variabile. Assegnare un altro oggetto alla stessa variabile cambia quale codice viene eseguito alla chiamata successiva:
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() + "!"
}All’interno di main:
var mode: Mode = Normal()
println(mode.format("hello"))
mode = Shout()
println(mode.format("hello"))Output:
hello
HELLO!Il polimorfismo sostituisce controlli come if (kind == "dog") che altrimenti verrebbero ripetuti ovunque si usino gli animali. Ogni classe mantiene il proprio comportamento e aggiungere un tipo di animale significa aggiungere una classe:
// senza polimorfismo: ogni funzione controlla kind
fun sound(kind: String) = when (kind) { "dog" -> "Woof"; "cow" -> "Moo"; else -> "?" }
// con il polimorfismo: il nuovo tipo porta con sé il proprio comportamento
class Sheep(name: String) : Animal(name) {
override fun sound() = "Baa"
}Sfida
FacileCharacter(name, hp) è una classe aperta. takeHit(damage) sottrae il danno da hp (mai sotto 0) e status() restituisce Ada: 30 hp oppure Ada: down a 0. hp può essere letto ovunque, ma può essere modificato solo all'interno di Character. Completa le sottoclassi eseguendo l'override di takeHit e passando il danno modificato a super.takeHit: Warrior (40 hp) subisce metà del danno, arrotondata per difetto; Mage (25 hp) ha uno scudo che assorbe interamente il primo colpo; Rogue (30 hp) schiva un colpo ogni due che riceve (il 2°, il 4°, ...).
Il codice fornito legge il gruppo come warrior:Ada,mage:Bo,rogue:Cy e poi attacca con all 10 oppure Ada 12. Chiama takeHit tramite il tipo Character e infine stampa ogni status().
Il tuo codice va in Character.kt, Warrior.kt, Mage.kt e Rogue.kt. Main.kt contiene il codice fornito per l'input/output e non può essere modificato.
Provalo tu
fun main() {
// Codice di input/output fornito: lascialo così com'è
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())
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Classi e oggetti
Lavorare con i fileDefinire le classiCostruttori primariBlocchi init e convalidaCostruttori secondariRiepilogo - Tessera della biblioteca4Ereditarietà
Classi aperte e sottoclassiRidefinizione dei membrisuper e costruttoriClassi astratteRiepilogo - Elenco del personale2Proprietà e incapsulamento
Accessor personalizzatiModificatori di visibilitàSetter privatilazy e lateinitRipasso - Conto bancario5Interfacce
Dichiarare le interfacceMetodi predefinitiInterfacce multipleLe interfacce come tipiRiepilogo - Metodi di pagamento8Tipi generici
Funzioni genericheClassi genericheVincoli sui tipiVarianza con in e outRiepilogo - Coda con priorità11Progetto: Gestione della biblioteca
Libri e sociPrendere in prestito i libri3Oggetti e companion
Dichiarazioni di oggettiOggetti companionCostanti con constEspressioni oggettoRipasso - Biglietteria6Polimorfismo
Dispatch dinamicoControlli di tipo e smart castwhen con i tipiFunzioni di estensioneRipasso - Custode dello zooEsercitati da solo: Playground Kotlin