Hierarquias Seladas
Parte da seção Programação Orientada a Objetos do Journey de Kotlin da Coddy. Lição 34 de 57.
Uma classe ou interface sealed tem um conjunto fechado de subclasses diretas, todas declaradas no mesmo pacote e módulo. O compilador conhece cada uma delas, o que é adequado para resultados que têm um número fixo de formas:
sealed interface Result
data class Success(val value: Int) : Result
data class Failure(val reason: String) : Result
fun parseAge(text: String): Result {
val n = text.toIntOrNull() ?: return Failure("not a number: $text")
return if (n in 0..150) Success(n) else Failure("out of range: $n")
}Dentro de main:
println(parseAge("42"))
println(parseAge("abc"))
println(parseAge("200"))Saída:
Success(value=42)
Failure(reason=not a number: abc)
Failure(reason=out of range: 200)Como o conjunto é fechado, um when sobre um tipo selado é exaustivo sem else. Se alguém adicionar uma subclasse posteriormente, todo when desse tipo deixa de compilar até tratar o novo caso, portanto nenhum caso é esquecido:
sealed interface Result
data class Success(val value: Int) : Result
data class Failure(val reason: String) : Result
fun describe(r: Result) = when (r) {
is Success -> "ok: ${r.value}"
is Failure -> "error: ${r.reason}"
}Dentro de main:
println(describe(Success(7)))
println(describe(Failure("timeout")))Saída:
ok: 7
error: timeoutUm caso sem dados é um data object: uma única instância com um toString() legível. Sua ramificação when não precisa de is, pois há apenas uma instância. Os outros casos podem ser classes de dados, objetos ou classes comuns:
sealed interface Command
data object Quit : Command
data class Move(val steps: Int) : Command
data class Say(val text: String) : Command
fun parse(line: String): Command = when {
line == "quit" -> Quit
line.startsWith("move ") -> Move(line.drop(5).toInt())
else -> Say(line)
}
fun run(c: Command) = when (c) {
Quit -> "bye"
is Move -> "moving ${c.steps}"
is Say -> "saying ${c.text}"
}Dentro de main:
for (line in listOf("move 3", "hello", "quit")) {
val c = parse(line)
println("$c: ${run(c)}")
}Saída:
Move(steps=3): moving 3
Say(text=hello): saying hello
Quit: byeEnumeração ou hierarquia selada? Uma enumeração é um conjunto fixo de instâncias únicas que têm todas as mesmas propriedades. Uma hierarquia selada permite que cada caso carregue seus próprios dados, e um caso como Move pode ter muitas instâncias com valores diferentes:
enum class Light { RED, YELLOW, GREEN } // mesma forma, uma instância de cada
sealed interface Payment // cada caso tem seus próprios dados
data class Card(val number: String) : Payment
data class Transfer(val iban: String, val reference: String) : Payment
data object Cash : PaymentDesafio
FácilUm robô entende os comandos da interface selada Command: Move(steps), Turn(left), Report e Invalid(text), que são fornecidos. Complete parse(line): move 3 produz Move(3), turn left e turn right produzem Turn, report produz Report, e qualquer outra coisa, incluindo um movimento sem um número válido, produz Invalid com a linha. Em seguida, complete Robot.execute(command) com um when exaustivo e sem else. O robô começa em (0, 0), voltado para N (norte é y + 1, leste é x + 1). Ele retorna moved to (0, 3), facing W, at (0, 3) facing W ou ignored.
O código fornecido analisa cada linha e imprime Move(steps=3) -> moved to (0, 3).
Seu código deve estar em Robot.kt, Parser.kt, Command.kt, Move.kt, Turn.kt, Report.kt e Invalid.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 robot = Robot()
for (line in input) {
val command = parse(line)
println("$command -> ${robot.execute(command)}")
}
}
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ários7Classes Especiais
Classes de DadosClasses EnumHierarquias Seladasequals, hashCode, toStringRecapitulação - Rastreador de Pedidos2Propriedades 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 LivrosPratique por conta própria: Playground de Kotlin