Dispatch dynamique
Fait partie de la section Programmation orientée objet du Journey Kotlin de Coddy. Leçon 27 sur 57.
Le polymorphisme signifie qu’un même appel peut se comporter de nombreuses façons. Lorsque vous appelez une méthode redéfinie via un type de superclasse ou d’interface, Kotlin examine la classe réelle de l’objet pendant l’exécution du programme et exécute la version de cette classe. C’est la liaison dynamique :
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"
}À l’intérieur de main :
val barn: List<Animal> = listOf(Dog("Rex"), Cow("Bess"), Dog("Fido"))
for (a in barn) println(a.speak())Sortie :
Rex: Woof
Bess: Moo
Fido: WoofLe code appelant ne change pas lorsqu’une nouvelle sous-classe apparaît. totalArea ci-dessous a été écrit pour n’importe quelle Shape, il fonctionne donc aussi avec Triangle, bien que personne ne l’ait modifié :
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() }À l’intérieur de main :
println(totalArea(listOf(Square(2.0), Triangle(3.0, 4.0))))Sortie :
10.0Le dispatch examine l’objet, et non la variable. Affecter un autre objet à la même variable modifie le code qui s’exécute lors de l’appel suivant :
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() + "!"
}Dans main :
var mode: Mode = Normal()
println(mode.format("hello"))
mode = Shout()
println(mode.format("hello"))Sortie :
hello
HELLO!Le polymorphisme remplace les vérifications comme if (kind == "dog") qui seraient autrement répétées partout où des animaux sont utilisés. Chaque classe conserve son propre comportement, et ajouter un type d’animal signifie ajouter une classe :
// sans polymorphisme : chaque fonction vérifie le kind
fun sound(kind: String) = when (kind) { "dog" -> "Woof"; "cow" -> "Moo"; else -> "?" }
// avec polymorphisme : le nouveau kind apporte son propre comportement
class Sheep(name: String) : Animal(name) {
override fun sound() = "Baa"
}Défi
FacileCharacter(name, hp) est une classe open. takeHit(damage) soustrait les dégâts de hp (sans jamais descendre en dessous de 0), et status() renvoie Ada: 30 hp ou Ada: down lorsque la valeur est 0. hp peut être lu partout, mais ne peut être modifié qu'à l'intérieur de Character. Complète les sous-classes en redéfinissant takeHit et en transmettant les dégâts modifiés à super.takeHit : Warrior (40 hp) ne subit que la moitié des dégâts, arrondie à l'entier inférieur ; Mage (25 hp) possède un bouclier qui absorbe intégralement le premier coup ; Rogue (30 hp) esquive un coup sur deux qu'il reçoit (le 2e, le 4e, ...).
Le code fourni lit le groupe sous la forme warrior:Ada,mage:Bo,rogue:Cy, puis applique all 10 ou Ada 12. Il appelle takeHit via le type Character et affiche finalement chaque status().
Ton code va dans Character.kt, Warrior.kt, Mage.kt et Rogue.kt. Main.kt contient le code fourni pour les entrées et les sorties et ne peut pas être modifié.
Essayez vous-même
fun main() {
// Code d'entrée/sortie fourni : le garder tel quel
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())
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation orientée objet
1Classes et objets
Travailler avec des fichiersDéfinir des classesConstructeurs primairesBlocs init et validationConstructeurs secondairesRécapitulatif - Carte de bibliothèque4Héritage
Classes ouvertes et sous-classesRedéfinition des membressuper et constructeursClasses abstraitesRécapitulatif – Annuaire du personnel2Propriétés et encapsulation
Accesseurs personnalisésModificateurs de visibilitéSetters privéslazy et lateinitRécapitulatif - Compte bancaire5Interfaces
Déclarer des interfacesMéthodes par défautInterfaces multiplesInterfaces en tant que typesRécapitulatif - Méthodes de paiement8Génériques
Fonctions génériquesClasses génériquesContraintes de typeVariance avec in et outRécapitulatif - File de priorité11Projet : Gestion de bibliothèque
Livres et membresEmprunter des livresEntraînez-vous par vous-même : Playground Kotlin