Gerarchie sigillate
Fa parte della sezione Programmazione orientata agli oggetti del percorso Kotlin di Coddy. Lezione 34 di 57.
Una classe o un'interfaccia sealed ha un insieme chiuso di sottoclassi dirette, tutte dichiarate nello stesso pacchetto e modulo. Il compilatore le conosce tutte, il che è adatto a risultati che hanno un numero fisso di forme:
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")
}All'interno di main:
println(parseAge("42"))
println(parseAge("abc"))
println(parseAge("200"))Output:
Success(value=42)
Failure(reason=not a number: abc)
Failure(reason=out of range: 200)Poiché il set è chiuso, un when su un tipo sealed è esaustivo senza else. Se qualcuno aggiunge una sottoclasse in seguito, ogni when di questo tipo smette di compilare finché non gestisce il nuovo caso, quindi nessun caso viene dimenticato:
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}"
}All'interno di main:
println(describe(Success(7)))
println(describe(Failure("timeout")))Output:
ok: 7
error: timeoutUn caso senza dati è un data object: una singola istanza con un toString() leggibile. Il suo ramo when non ha bisogno di is, perché esiste una sola istanza. Gli altri casi possono essere classi di dati, oggetti o classi ordinarie:
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}"
}All'interno di main:
for (line in listOf("move 3", "hello", "quit")) {
val c = parse(line)
println("$c: ${run(c)}")
}Output:
Move(steps=3): moving 3
Say(text=hello): saying hello
Quit: byeEnum o gerarchia sealed? Un enum è un insieme fisso di singole istanze che hanno tutte le stesse proprietà. Una gerarchia sealed consente a ogni caso di contenere i propri dati, e un caso come Move può avere molte istanze con valori diversi:
enum class Light { RED, YELLOW, GREEN } // stessa struttura, un'istanza per ciascuno
sealed interface Payment // ogni caso ha i propri dati
data class Card(val number: String) : Payment
data class Transfer(val iban: String, val reference: String) : Payment
data object Cash : PaymentSfida
FacileUn robot comprende i comandi dell’interfaccia sealed Command: Move(steps), Turn(left), Report e Invalid(text), che sono forniti. Completa parse(line): move 3 restituisce Move(3), turn left e turn right restituiscono Turn, report restituisce Report e qualsiasi altra cosa, incluso un movimento senza un numero valido, restituisce Invalid con la riga. Poi completa Robot.execute(command) con un when esaustivo e senza else. Il robot parte da (0, 0) rivolto verso N (il nord è y + 1, l’est è x + 1). Restituisce moved to (0, 3), facing W, at (0, 3) facing W oppure ignored.
Il codice fornito analizza ogni riga e stampa Move(steps=3) -> moved to (0, 3).
Il tuo codice va inserito in Robot.kt, Parser.kt, Command.kt, Move.kt, Turn.kt, Report.kt e Invalid.kt. Main.kt contiene il codice di input/output fornito e non può essere modificato.
Provalo tu
fun main() {
// Codice di input/output fornito: lascialo così com'è
val input = generateSequence(::readLine).toList()
val robot = Robot()
for (line in input) {
val command = parse(line)
println("$command -> ${robot.execute(command)}")
}
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Classi e oggetti
Lavorare con i fileDefinire le classiCostruttori primariBlocchi init e convalidaCostruttori secondariRiepilogo - Tessera della biblioteca4Ereditarietà
Classi aperte e sottoclassiRidefinizione dei membrisuper e costruttoriClassi astratteRiepilogo - Elenco del personale7Classi speciali
Classi di datiClassi enumGerarchie sigillateequals, hashCode, toStringRiepilogo - Monitoraggio ordini2Proprietà e incapsulamento
Accessor personalizzatiModificatori di visibilitàSetter privatilazy e lateinitRipasso - Conto bancario5Interfacce
Dichiarare le interfacceMetodi predefinitiInterfacce multipleLe interfacce come tipiRiepilogo - Metodi di pagamento8Tipi generici
Funzioni genericheClassi genericheVincoli sui tipiVarianza con in e outRiepilogo - Coda con priorità11Progetto: Gestione della biblioteca
Libri e sociPrendere in prestito i libriEsercitati da solo: Playground Kotlin