Menu
Coddy logo textTech

Despacho dinâmico

Parte da seção Programação Orientada a Objetos do Journey de Kotlin da Coddy. Lição 27 de 57.

Polimorfismo significa que uma chamada pode se comportar de várias maneiras. Quando você chama um método sobrescrito por meio de um tipo de superclasse ou interface, o Kotlin observa a classe real do objeto enquanto o programa é executado e executa a versão dessa classe. Isso é 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())

Saída:

Rex: Woof
Bess: Moo
Fido: Woof

O código chamador não muda quando uma nova subclasse surge. totalArea abaixo foi escrito para qualquer Shape, então também funciona com Triangle, embora ninguém o tenha alterado:

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

Saída:

10.0

O despacho observa o objeto, não a variável. Atribuir outro objeto à mesma variável altera qual código será executado na próxima chamada:

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

Saída:

hello
HELLO!

O polimorfismo substitui verificações como if (kind == "dog") que, de outra forma, seriam repetidas em todos os lugares onde animais são usados. Cada classe mantém seu próprio comportamento, e adicionar um tipo de animal significa adicionar uma classe:

// sem polimorfismo: cada função verifica o kind
fun sound(kind: String) = when (kind) { "dog" -> "Woof"; "cow" -> "Moo"; else -> "?" }

// com polimorfismo: o novo kind traz o seu próprio comportamento
class Sheep(name: String) : Animal(name) {
    override fun sound() = "Baa"
}
challenge icon

Desafio

Fácil

Character(name, hp) é uma classe aberta. takeHit(damage) subtrai o dano de hp (nunca abaixo de 0), e status() retorna Ada: 30 hp ou Ada: down quando chega a 0. hp pode ser lido em qualquer lugar, mas só pode ser alterado dentro de Character. Complete as subclasses sobrescrevendo takeHit e passando o dano alterado para super.takeHit: Warrior (40 hp) recebe metade do dano, arredondada para baixo; Mage (25 hp) tem um escudo que absorve todo o primeiro golpe; Rogue (30 hp) se esquiva de todo segundo golpe que recebe (o 2º, 4º, ...).

O código fornecido lê o grupo como warrior:Ada,mage:Bo,rogue:Cy e então desfere golpes com all 10 ou Ada 12. Ele chama takeHit por meio do tipo Character e, por fim, imprime cada status().

Seu código deve ficar em Character.kt, Warrior.kt, Mage.kt e Rogue.kt. Main.kt contém o código fornecido de entrada/saída e não pode ser editado.

Experimente você mesmo

fun main() {
    // Código de entrada/saída fornecido: mantenha-o 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 iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Playground de Kotlin