Menu
Coddy logo textTech

Hiérarchies scellées

Fait partie de la section Programmation orientée objet du Journey Kotlin de Coddy. Leçon 34 sur 57.

Une classe ou une interface sealed possède un ensemble fermé de sous-classes directes, toutes déclarées dans le même package et le même module. Le compilateur connaît chacune d’elles, ce qui convient aux résultats ayant un nombre fixe de formes :

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

À l’intérieur de main :

println(parseAge("42"))
println(parseAge("abc"))
println(parseAge("200"))

Sortie :

Success(value=42)
Failure(reason=not a number: abc)
Failure(reason=out of range: 200)

Comme l’ensemble est fermé, un when sur un type scellé est exhaustif sans else. Si quelqu’un ajoute une sous-classe ultérieurement, chaque when de ce type cesse de compiler jusqu’à ce qu’il gère le nouveau cas, de sorte qu’aucun cas n’est oublié :

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

Dans main :

println(describe(Success(7)))
println(describe(Failure("timeout")))

Sortie :

ok: 7
error: timeout

Un cas sans données est un data object : une instance unique avec un toString() lisible. Sa branche when n’a pas besoin de is, puisqu’il n’existe qu’une seule instance. Les autres cas peuvent être des classes de données, des objets ou des classes ordinaires :

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

À l’intérieur de main :

for (line in listOf("move 3", "hello", "quit")) {
    val c = parse(line)
    println("$c: ${run(c)}")
}

Sortie :

Move(steps=3): moving 3
Say(text=hello): saying hello
Quit: bye

Énumération ou hiérarchie scellée ? Une énumération est un ensemble fixe d’instances uniques qui possèdent toutes les mêmes propriétés. Une hiérarchie scellée permet à chaque cas de transporter ses propres données, et un cas tel que Move peut avoir de nombreuses instances avec des valeurs différentes :

enum class Light { RED, YELLOW, GREEN }          // même forme, une instance chacune

sealed interface Payment                          // chaque cas a ses propres données
data class Card(val number: String) : Payment
data class Transfer(val iban: String, val reference: String) : Payment
data object Cash : Payment
challenge icon

Défi

Facile

Un robot comprend les commandes de l’interface scellée Command : Move(steps), Turn(left), Report et Invalid(text), qui sont fournies. Complétez parse(line) : move 3 donne Move(3), turn left et turn right donnent Turn, report donne Report, et tout le reste, y compris un déplacement sans nombre valide, donne Invalid avec la ligne. Complétez ensuite Robot.execute(command) avec un when exhaustif et sans else. Le robot commence en (0, 0) orienté vers N (le nord correspond à y + 1, l’est à x + 1). Il renvoie moved to (0, 3), facing W, at (0, 3) facing W ou ignored.

Le code fourni analyse chaque ligne et affiche Move(steps=3) -> moved to (0, 3).

Votre code doit être placé dans Robot.kt, Parser.kt, Command.kt, Move.kt, Turn.kt, Report.kt et Invalid.kt. Main.kt contient le code fourni pour les entrées et sorties et ne peut pas être modifié.

Essayez vous-même

fun main() {
    // Code d'entrée/sortie fourni : le laisser tel quel
    val input = generateSequence(::readLine).toList()
    val robot = Robot()
    for (line in input) {
        val command = parse(line)
        println("$command -> ${robot.execute(command)}")
    }
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation orientée objet

Entraînez-vous par vous-même : Playground Kotlin