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: WoofO 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.0O 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"
}Desafio
FácilCharacter(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())
}
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
1Classes e Objetos
Trabalhando com ArquivosDefinindo ClassesConstrutores PrimáriosBlocos init e ValidaçãoConstrutores SecundáriosRecapitulação - Cartão da Biblioteca4Herança
Classes Abertas e SubclassesSobrescrevendo Membrossuper e ConstrutoresClasses AbstratasRevisão - Diretório de Funcionários2Propriedades e Encapsulamento
Acessores PersonalizadosModificadores de VisibilidadeSetters Privadoslazy e lateinitRevisão - Conta Bancária5Interfaces
Declarando InterfacesMétodos PadrãoMúltiplas InterfacesInterfaces como TiposRecapitulação - Métodos de Pagamento8Genéricos
Funções genéricasClasses genéricasRestrições de tipoVariância com in e outRecapitulação - Fila de prioridade11Projeto: Gerenciamento de Biblioteca
Livros e MembrosEmpréstimo de Livros3Objetos e Companheiros
Declarações de ObjetosObjetos CompanionConstantes com constExpressões de ObjetosRevisão - Bilheteria6Polimorfismo
Despacho dinâmicoVerificações de tipo e smart castswhen com tiposFunções de extensãoRevisão - Cuidador do zoológicoPratique por conta própria: Playground de Kotlin