Comment modéliser les types enum de type sécurisé?

Scala n’a pas d’ enum type sécurisé comme Java. Étant donné un ensemble de constantes connexes, quelle serait la meilleure façon de représenter ces constantes à Scala?

http://www.scala-lang.org/docu/files/api/scala/Enumeration.html

Exemple d’utilisation

  object Main extends App { object WeekDay extends Enumeration { type WeekDay = Value val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value } import WeekDay._ def isWorkingDay(d: WeekDay) = ! (d == Sat || d == Sun) WeekDay.values filter isWorkingDay foreach println } 

Je dois dire que l’exemple copié de la documentation Scala par skaffman ci-dessus est d’une utilité limitée dans la pratique (vous pourriez aussi bien utiliser des case object ).

Pour obtenir quelque chose qui ressemble le plus à Java Enum (c.-à-d. Avec les méthodes toSsortingng et valueOf sensibles – vous conservez peut-être les valeurs d’énumération dans une firebase database), vous devez le modifier un peu. Si vous aviez utilisé le code skaffman :

 WeekDay.valueOf("Sun") //returns None WeekDay.Tue.toSsortingng //returns Weekday(2) 

Considérant qu’en utilisant la déclaration suivante:

 object WeekDay extends Enumeration { type WeekDay = Value val Mon = Value("Mon") val Tue = Value("Tue") ... etc } 

Vous obtenez des résultats plus sensibles:

 WeekDay.valueOf("Sun") //returns Some(Sun) WeekDay.Tue.toSsortingng //returns Tue 

Il y a plusieurs façons de faire.

1) Utilisez des symboles. Cela ne vous donnera aucun type de sécurité, en dehors de ne pas accepter les non-symboles où un symbole est attendu. Je le mentionne seulement ici pour être complet. Voici un exemple d’utilisation:

 def update(what: Symbol, where: Int, newValue: Array[Int]): MasortingxInt = what match { case 'row => replaceRow(where, newValue) case 'col | 'column => replaceCol(where, newValue) case _ => throw new IllegalArgumentException } // At REPL: scala> val a = unitMasortingxInt(3) a: teste7.MasortingxInt = / 1 0 0 \ | 0 1 0 | \ 0 0 1 / scala> a('row, 1) = a.row(0) res41: teste7.MasortingxInt = / 1 0 0 \ | 1 0 0 | \ 0 0 1 / scala> a('column, 2) = a.row(0) res42: teste7.MasortingxInt = / 1 0 1 \ | 0 1 0 | \ 0 0 0 / 

2) Utilisation de la classe Enumeration :

 object Dimension extends Enumeration { type Dimension = Value val Row, Column = Value } 

ou, si vous avez besoin de le sérialiser ou de l’afficher:

 object Dimension extends Enumeration("Row", "Column") { type Dimension = Value val Row, Column = Value } 

Cela peut être utilisé comme ceci:

 def update(what: Dimension, where: Int, newValue: Array[Int]): MasortingxInt = what match { case Row => replaceRow(where, newValue) case Column => replaceCol(where, newValue) } // At REPL: scala> a(Row, 2) = a.row(1) :13: error: not found: value Row a(Row, 2) = a.row(1) ^ scala> a(Dimension.Row, 2) = a.row(1) res1: teste.MasortingxInt = / 1 0 0 \ | 0 1 0 | \ 0 1 0 / scala> import Dimension._ import Dimension._ scala> a(Row, 2) = a.row(1) res2: teste.MasortingxInt = / 1 0 0 \ | 0 1 0 | \ 0 1 0 / 

Malheureusement, cela ne garantit pas que toutes les correspondances sont sockets en compte. Si j’oubliais de mettre Row ou Column dans le match, le compilateur Scala ne m’aurait pas prévenu. Donc, cela me donne une certaine sécurité, mais pas autant que possible.

3) Objets de cas:

 sealed abstract class Dimension case object Row extends Dimension case object Column extends Dimension 

Maintenant, si je laisse une casse sur une match , le compilateur me préviendra:

 MasortingxInt.scala:70: warning: match is not exhaustive! missing combination Column what match { ^ one warning found 

Il est utilisé à peu près de la même manière et n’a même pas besoin d’une import :

 scala> val a = unitMasortingxInt(3) a: teste3.MasortingxInt = / 1 0 0 \ | 0 1 0 | \ 0 0 1 / scala> a(Row,2) = a.row(0) res15: teste3.MasortingxInt = / 1 0 0 \ | 0 1 0 | \ 1 0 0 / 

Vous vous demandez peut-être alors pourquoi utiliser une énumération à la place des objects cas. En fait, les objects de cas ont souvent des avantages, comme ici. La classe Enumeration, cependant, possède de nombreuses méthodes de collecte, telles que des éléments (iterator sur Scala 2.8), qui renvoie un iterator, une carte, une flatMap, un filtre, etc.

Cette réponse est essentiellement une partie sélectionnée de cet article sur mon blog.

Une manière légèrement moins verbeuse de déclarer les énumérations nommées:

 object WeekDay extends Enumeration("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat") { type WeekDay = Value val Sun, Mon, Tue, Wed, Thu, Fri, Sat = Value } WeekDay.valueOf("Wed") // returns Some(Wed) WeekDay.Fri.toSsortingng // returns Fri 

Bien sûr, le problème est que vous devrez synchroniser le classement des noms et des valeurs, ce qui est plus facile si le nom et le val sont déclarés sur la même ligne.

Vous pouvez utiliser une classe abstraite scellée à la place de l’énumération, par exemple:

 sealed abstract class Constraint(val name: Ssortingng, val verifier: Int => Boolean) case object NotTooBig extends Constraint("NotTooBig", (_ < 1000)) case object NonZero extends Constraint("NonZero", (_ != 0)) case class NotEquals(x: Int) extends Constraint("NotEquals " + x, (_ != x)) object Main { def eval(ctrs: Seq[Constraint])(x: Int): Boolean = (true /: ctrs){ case (accum, ctr) => accum && ctr.verifier(x) } def main(args: Array[Ssortingng]) { val ctrs = NotTooBig :: NotEquals(5) :: Nil val evaluate = eval(ctrs) _ println(evaluate(3000)) println(evaluate(3)) println(evaluate(5)) } } 

vient de découvrir enumeratum . c’est assez incroyable et tout aussi incroyable ce n’est pas plus connu!

Après avoir effectué des recherches approfondies sur toutes les options autour des “énumérations” dans Scala, j’ai publié une présentation beaucoup plus complète de ce domaine sur un autre thread StackOverflow . Il inclut une solution au modèle “object traité + object cacheté” dans lequel j’ai résolu le problème de classement de l’initialisation de classe / object JVM.

Dotty (Scala 3) aura des énumérations natives supscopes. Vérifiez ici et ici .