Kotlin a-t-il quelque chose comme des syndicats discriminés (types de somme)? Quelle serait la traduction idiomatique de Kotlin de ceci (F #):
type OrderMessage =
| New of Id: int * Quantity: int
| Cancel of Id: int
let handleMessage msg =
match msg with
| New(id, qty) -> handleNew id qty
| Cancel(id) -> handleCxl id
La manière courante de mettre en œuvre ce type d'abstraction dans un langage OO (par exemple Kotlin ou Scala) serait par héritage:
open class OrderMessage private () { // private constructor to prevent creating more subclasses outside
class New(val id: Int, val quantity: Int) : OrderMessage()
class Cancel(val id: Int) : OrderMessage()
}
Vous pouvez pousser la partie commune vers la superclasse, si vous le souhaitez:
open class OrderMessage private (val id: Int) { // private constructor to prevent creating more subclasses outside
class New(id: Int, val quantity: Int) : OrderMessage(id)
class Cancel(id: Int) : OrderMessage(id)
}
Le vérificateur de type ne sait pas qu'une telle hiérarchie est fermée, donc lorsque vous faites une correspondance de type casse (when
- expression) dessus, il se plaindra qu'elle n'est pas exhaustive, mais cela sera corrigé bientôt.
Mise à jour: alors que Kotlin ne prend pas en charge correspondance de motifs, vous pouvez utiliser lorsque - expressions sous forme de lancers intelligents pour obtenir presque le même comportement:
when (message) {
is New -> println("new $id: $quantity")
is Cancel -> println("cancel $id")
}
En savoir plus sur les modèles intelligents ici .
Kotlin's sealed class
l'approche de ce problème est extrêmement similaire à la Scala sealed class
et sealed trait
.
Exemple (tiré de l'article Kotlin lié):
sealed class Expr {
class Const(val number: Double) : Expr()
class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
}
La classe scellée de Kotlin a été conçue pour pouvoir représenter des types de somme, comme cela arrive avec le trait scellé de Scala.
Exemple:
sealed class OrderStatus {
object Approved: OrderStatus()
class Rejected(val reason: String): OrderStatus()
}
L'avantage clé de l'utilisation de classes scellées entre en jeu lorsque vous les utilisez dans une expression when pour la correspondance.
S'il est possible de vérifier que l'instruction couvre tous les cas, vous n'avez pas besoin d'ajouter une clause else à l'instruction.
private fun getOrderNotification(orderStatus:OrderStatus): String{
return when(orderStatus) {
is OrderStatus.Approved -> "The order has been approved"
is OrderStatus.Rejected -> "The order has been rejected. Reason:" + orderStatus.reason
}
}
Il y a plusieurs choses à garder à l'esprit:
Dans Kotlin lors de l'exécution de la smartcast, ce qui signifie que dans cet exemple, il n'est pas nécessaire d'effectuer la conversion de OrderStatus en OrderStatus.Rejected pour accéder à la propriété reason.
Si nous n'avions pas défini quoi faire pour le cas rejeté, la compilation échouerait et dans le IDE un avertissement comme celui-ci apparaît:
'quand' l'expression doit être exhaustive, ajoutez nécessaire 'est la branche' rejetée 'ou la branche' sinon 'à la place.
Ceci est un lien vers mon blog (espagnol), où j'ai un article plus complet sur ADT avec des exemples de kotlin: http://xurxodev.com/tipos-de-datos-algebraicos/