Despacho dinámico
Parte de la sección Programación orientada a objetos del Journey de Kotlin de Coddy. Lección 27 de 57.
El polimorfismo significa que una llamada puede comportarse de muchas maneras. Cuando llamas a un método sobrescrito a través de un tipo de superclase o interfaz, Kotlin observa la clase real del objeto mientras se ejecuta el programa y ejecuta la versión de esa clase. Esto es el despacho dinámico:
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 de main:
val barn: List<Animal> = listOf(Dog("Rex"), Cow("Bess"), Dog("Fido"))
for (a in barn) println(a.speak())Salida:
Rex: Woof
Bess: Moo
Fido: WoofEl código que realiza la llamada no cambia cuando aparece una nueva subclase. totalArea se escribió para cualquier Shape, por lo que también funciona con Triangle, aunque nadie lo haya cambiado:
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 de main:
println(totalArea(listOf(Square(2.0), Triangle(3.0, 4.0))))Salida:
10.0El despacho observa el objeto, no la variable. Asignar otro objeto a la misma variable cambia qué código se ejecuta en la siguiente llamada:
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() + "!"
}Dentro de main:
var mode: Mode = Normal()
println(mode.format("hello"))
mode = Shout()
println(mode.format("hello"))Salida:
hello
HELLO!El polimorfismo reemplaza comprobaciones como if (kind == "dog") que, de otro modo, se repetirían dondequiera que se usen animales. Cada clase mantiene su propio comportamiento, y añadir un tipo de animal significa añadir una clase:
// sin polimorfismo: cada función comprueba el tipo
fun sound(kind: String) = when (kind) { "dog" -> "Woof"; "cow" -> "Moo"; else -> "?" }
// con polimorfismo: el nuevo tipo aporta su propio comportamiento
class Sheep(name: String) : Animal(name) {
override fun sound() = "Baa"
}Desafío
FácilCharacter(name, hp) es una clase abierta. takeHit(damage) resta el daño de hp (nunca por debajo de 0), y status() devuelve Ada: 30 hp o Ada: down cuando llega a 0. hp se puede leer en cualquier lugar, pero solo se puede modificar dentro de Character. Completa las subclases sobrescribiendo takeHit y pasando el daño modificado a super.takeHit: Warrior (40 hp) recibe la mitad del daño, redondeada hacia abajo; Mage (25 hp) tiene un escudo que absorbe por completo el primer golpe; Rogue (30 hp) esquiva cada segundo golpe que recibe (el 2.º, 4.º, ...).
El código proporcionado lee el grupo como warrior:Ada,mage:Bo,rogue:Cy y después aplica all 10 o Ada 12. Llama a takeHit mediante el tipo Character y finalmente imprime cada status().
Tu código va en Character.kt, Warrior.kt, Mage.kt y Rogue.kt. Main.kt contiene el código proporcionado de entrada/salida y no se puede editar.
Pruébalo tú mismo
fun main() {
// Código de entrada/salida suministrado: déjalo como está
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())
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Clases y objetos
Trabajar con archivosDefinir clasesConstructores primariosBloques init y validaciónConstructores secundariosRepaso - Tarjeta de biblioteca4Herencia
Clases abiertas y subclasesSobrescritura de miembrossuper y constructoresClases abstractasRepaso: directorio del personal2Propiedades y encapsulación
Accesores personalizadosModificadores de visibilidadSetters privadoslazy y lateinitRepaso: cuenta bancaria5Interfaces
Declaración de interfacesMétodos predeterminadosInterfaces múltiplesInterfaces como tiposRepaso: métodos de pago8Genéricos
Funciones genéricasClases genéricasRestricciones de tiposVariancia con in y outRepaso - Cola de prioridad11Proyecto: Gestión de biblioteca
Libros y miembrosPréstamo de librosPractica por tu cuenta: Playground de Kotlin