Menu
Coddy logo textTech

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: Woof

El 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.0

El 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"
}
challenge icon

Desafío

Fácil

Character(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())
}
quiz iconPonte a prueba

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

Practica por tu cuenta: Playground de Kotlin