web-dev-qa-db-fra.com

Kotlin et syndicats discriminés (types de somme)

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
41
ehnmark

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 .

33
Andrey Breslav

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()
}
35
Adeynack

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.

  • quand il peut être utilisé comme expression ou comme déclaration. S'il est utilisé comme expression, la valeur de la branche satisfaite devient la valeur de l'expression générale. Si elle est utilisée comme instruction, les valeurs des branches individuelles sont ignorées. Cela signifie que l'erreur de compilation en cas de manque d'une branche se produit uniquement lorsqu'elle est utilisée comme expression, en utilisant le résultat.

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/

5
xurxodev