Scala n'a pas de type sûr enumcomme Java. Étant donné un ensemble de constantes liées, quelle serait la meilleure façon dans Scala de représenter ces constantes?
Sérieusement, l'application ne doit pas être utilisée. Ce n'était PAS corrigé; une nouvelle classe, App, a été introduite, qui n'a pas les problèmes mentionnés par Schildmeijer. Il en est de même de "l'objet foo étend l'application {...}" et vous avez un accès immédiat aux arguments de ligne de commande via la variable args.
AmigoNico
scala.Enumeration (qui est ce que vous utilisez dans votre exemple de code "object WeekDay" ci-dessus) n'offre pas de correspondance de modèle exhaustive. J'ai recherché tous les différents modèles d'énumération actuellement utilisés dans Scala et j'en donne et un aperçu dans cette réponse StackOverflow (y compris un nouveau modèle qui offre le meilleur des deux scala.Enumeration et le modèle "scellé trait + objet de cas": stackoverflow. com / a / 25923651/501113
chaotic3quilibrium
377
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 case objects).
Afin d'obtenir quelque chose qui ressemble le plus à un Java Enum(c'est-à-dire avec des méthodes sensées toStringet valueOf- peut-être que vous persistez les valeurs d'énumération dans une base de données), vous devez le modifier un peu. Si vous aviez utilisé le code de skaffman :
valueOfLe remplacement de @macias est withName, qui ne retourne pas d'option, et lance un NSE s'il n'y a pas de correspondance. Qu'est-ce que!
Bluu
6
@Bluu Vous pouvez ajouter valueOf yourself: def valueOf (name: String) = WeekDay.values.find (_. ToString == name) pour avoir une option
centr
@centr Lorsque j'essaye de créer un Map[Weekday.Weekday, Long]et d'ajouter une valeur, Monle compilateur génère une erreur de type non valide. Jour de semaine prévu. Pourquoi cela arrive-t-il?
Sohaib
@Sohaib Ce devrait être Map [Weekday.Value, Long].
CENTR
99
Il y a plusieurs façons de faire.
1) Utilisez des symboles. Cependant, cela ne vous donnera aucune sécurité de type, à part qu'il n'accepte pas les non-symboles où un symbole est attendu. Je ne le mentionne ici que pour être complet. Voici un exemple d'utilisation:
def update(what:Symbol, where:Int, newValue:Array[Int]):MatrixInt=
what match{case'row=> replaceRow(where, newValue)case'col|'column=> replaceCol(where, newValue)case _ =>thrownewIllegalArgumentException}// At REPL:
scala>val a = unitMatrixInt(3)
a: teste7.MatrixInt=/100\|010|\001/
scala> a('row,1)= a.row(0)
res41: teste7.MatrixInt=/100\|100|\001/
scala> a('column,2)= a.row(0)
res42: teste7.MatrixInt=/101\|010|\000/
def update(what:Dimension, where:Int, newValue:Array[Int]):MatrixInt=
what match{caseRow=> replaceRow(where, newValue)caseColumn=> replaceCol(where, newValue)}// At REPL:
scala> a(Row,2)= a.row(1)<console>:13: error: not found: value Row
a(Row,2)= a.row(1)^
scala> a(Dimension.Row,2)= a.row(1)
res1: teste.MatrixInt=/100\|010|\010/
scala>importDimension._
importDimension._
scala> a(Row,2)= a.row(1)
res2: teste.MatrixInt=/100\|010|\010/
Malheureusement, cela ne garantit pas que tous les matchs sont pris en compte. Si j'avais oublié de mettre Row ou Column dans le match, le compilateur Scala ne m'aurait pas prévenu. Cela me donne donc une certaine sécurité de type, mais pas autant que ce qui peut être gagné.
Vous vous demandez peut-être alors pourquoi utiliser une énumération plutôt que des objets de cas. En fait, les objets de cas présentent de nombreux avantages, comme ici. La classe Enumeration, cependant, possède de nombreuses méthodes Collection, telles que des éléments (itérateur sur Scala 2.8), qui renvoie un Iterator, une carte, un flatMap, un filtre, etc.
Cette réponse est essentiellement une partie sélectionnée de cet article dans mon blog.
"... n'acceptant pas les non-symboles où un symbole est attendu"> Je suppose que vous voulez dire que les Symbolinstances ne peuvent pas avoir d'espaces ou de caractères spéciaux. La plupart des gens lors de leur première rencontre avec la Symbolclasse le pensent probablement, mais c'est en fait incorrect. Symbol("foo !% bar -* baz")compile et fonctionne parfaitement bien. En d'autres termes, vous pouvez parfaitement créer des Symbolinstances enveloppant n'importe quelle chaîne (vous ne pouvez tout simplement pas le faire avec le sucre syntaxique "coma unique"). La seule chose qui Symbolgarantit est l'unicité d'un symbole donné, ce qui le rend légèrement plus rapide à comparer et à faire correspondre.
Régis Jean-Gilles
@ RégisJean-Gilles Non, je veux dire que vous ne pouvez pas passer un String, par exemple, comme argument à un Symbolparamètre.
Daniel C.Sobral
Oui, j'ai compris cette partie, mais c'est un point discutable si vous remplacez Stringpar une autre classe qui est essentiellement un wrapper autour d'une chaîne et peut être librement convertie dans les deux sens (comme c'est le cas pour Symbol). Je suppose que c'est ce que vous vouliez dire en disant "Cela ne vous donnera aucune sécurité de type", ce n'était tout simplement pas très clair étant donné que OP a explicitement demandé des solutions sûres de type. Je ne savais pas si au moment de la rédaction de cet article, vous saviez que non seulement ce type n'est pas sûr car ce ne sont pas du tout des énumérations, mais aussiSymbol qu'il ne garantit même pas que l'argument passé n'aura pas de caractères spéciaux.
Régis Jean-Gilles
1
Pour élaborer, lorsque vous dites «n'accepter pas de non-symboles où un symbole est attendu», cela peut être lu comme «n'acceptant pas de valeurs qui ne sont pas des instances de Symbol» (ce qui est évidemment vrai) ou «n'acceptant pas de valeurs qui ne sont pas identifiant comme simple des chaînes, alias « symboles » »( ce qui est faux, et est une idée fausse que à peu près tout le monde a la première fois que nous rencontrons des symboles scala, en raison du fait que la première rencontre est bien la spéciale 'foonotation qui ne se opposent chaînes non identifiantes). C'est cette idée fausse que je voulais dissiper pour tout futur lecteur.
Régis Jean-Gilles
@ RégisJean-Gilles Je voulais dire le premier, celui qui est évidemment vrai. Je veux dire, c'est évidemment vrai pour toute personne habituée à la frappe statique. À l'époque, il y avait beaucoup de discussions sur les mérites relatifs du typage statique et "dynamique", et beaucoup de gens intéressés par Scala venaient d'un milieu de typage dynamique, donc j'ai pensé que cela n'allait pas de soi. Je ne penserais même pas à faire cette remarque de nos jours. Personnellement, je pense que le symbole de Scala est moche et redondant, et ne l'utilise jamais. Je vote pour votre dernier commentaire, car c'est un bon point.
Daniel C.Sobral
52
Une façon un peu moins verbeuse de déclarer les énumérations nommées:
Bien sûr, le problème ici est que vous devrez synchroniser l'ordre des noms et des valeurs, ce qui est plus facile à faire si le nom et la valeur sont déclarés sur la même ligne.
Cela semble plus propre à première vue, mais présente l'inconvénient d'exiger du responsable qu'il synchronise l'odeur des deux listes. Pour l'exemple des jours de la semaine, cela ne semble pas probable. Mais en général, la nouvelle valeur pourrait être insérée, ou supprimée et les deux listes pourraient être désynchronisées, auquel cas, des bogues subtils pourraient être introduits.
Brent Faust
1
Selon le commentaire précédent, le risque est que les deux listes différentes puissent se désynchroniser en silence. Bien que ce ne soit pas un problème pour votre petit exemple actuel, s'il y a beaucoup plus de membres (comme des dizaines à des centaines), les chances que les deux listes se désynchronisent silencieusement sont considérablement plus élevées. De plus, scala.Enumeration ne peut pas bénéficier des avertissements / erreurs de correspondance de motifs exhaustifs de Scala. J'ai créé une réponse StackOverflow qui contient une solution effectuant une vérification de l'exécution pour s'assurer que les deux listes restent synchronisées: stackoverflow.com/a/25923651/501113
chaotic3quilibrium
17
Vous pouvez utiliser une classe abstraite scellée au lieu de l'énumération, par exemple:
Après avoir fait des recherches approfondies sur toutes les options autour des "énumérations" dans Scala, j'ai posté un aperçu beaucoup plus complet de ce domaine sur un autre thread StackOverflow . Il comprend une solution au modèle "trait scellé + objet cas" où j'ai résolu le problème de commande d'initialisation de classe / objet JVM.
Le projet est vraiment bon avec des exemples et de la documentation
Cet exemple de leur documentation devrait vous intéresser
import enumeratum._
sealedtraitGreetingextendsEnumEntryobjectGreetingextendsEnum[Greeting]{/*
`findValues` is a protected method that invokes a macro to find all `Greeting` object declarations inside an `Enum`
You use it to implement the `val values` member
*/val values = findValues
caseobjectHelloextendsGreetingcaseobjectGoodByeextendsGreetingcaseobjectHiextendsGreetingcaseobjectByeextendsGreeting}// Object Greeting has a `withName(name: String)` methodGreeting.withName("Hello")// => res0: Greeting = HelloGreeting.withName("Haro")// => java.lang.IllegalArgumentException: Haro is not a member of Enum (Hello, GoodBye, Hi, Bye)// A safer alternative would be to use `withNameOption(name: String)` method which returns an Option[Greeting]Greeting.withNameOption("Hello")// => res1: Option[Greeting] = Some(Hello)Greeting.withNameOption("Haro")// => res2: Option[Greeting] = None// It is also possible to use strings case insensitivelyGreeting.withNameInsensitive("HeLLo")// => res3: Greeting = HelloGreeting.withNameInsensitiveOption("HeLLo")// => res4: Option[Greeting] = Some(Hello)// Uppercase-only strings may also be usedGreeting.withNameUppercaseOnly("HELLO")// => res5: Greeting = HelloGreeting.withNameUppercaseOnlyOption("HeLLo")// => res6: Option[Greeting] = None// Similarly, lowercase-only strings may also be usedGreeting.withNameLowercaseOnly("hello")// => res7: Greeting = HelloGreeting.withNameLowercaseOnlyOption("hello")// => res8: Option[Greeting] = Some(Hello)
Réponses:
http://www.scala-lang.org/docu/files/api/scala/Enumeration.html
Exemple d'utilisation
la source
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
case object
s).Afin d'obtenir quelque chose qui ressemble le plus à un Java
Enum
(c'est-à-dire avec des méthodes senséestoString
etvalueOf
- peut-être que vous persistez les valeurs d'énumération dans une base de données), vous devez le modifier un peu. Si vous aviez utilisé le code de skaffman :Attendu qu'en utilisant la déclaration suivante:
Vous obtenez des résultats plus sensibles:
la source
valueOf
Le remplacement de @macias estwithName
, qui ne retourne pas d'option, et lance un NSE s'il n'y a pas de correspondance. Qu'est-ce que!Map[Weekday.Weekday, Long]
et d'ajouter une valeur,Mon
le compilateur génère une erreur de type non valide. Jour de semaine prévu. Pourquoi cela arrive-t-il?Il y a plusieurs façons de faire.
1) Utilisez des symboles. Cependant, cela ne vous donnera aucune sécurité de type, à part qu'il n'accepte pas les non-symboles où un symbole est attendu. Je ne le mentionne ici que pour être complet. Voici un exemple d'utilisation:
2) Utilisation de la classe
Enumeration
:ou, si vous devez le sérialiser ou l'afficher:
Cela peut être utilisé comme ceci:
Malheureusement, cela ne garantit pas que tous les matchs sont pris en compte. Si j'avais oublié de mettre Row ou Column dans le match, le compilateur Scala ne m'aurait pas prévenu. Cela me donne donc une certaine sécurité de type, mais pas autant que ce qui peut être gagné.
3) Objets de cas:
Maintenant, si je laisse un cas sur a
match
, le compilateur m'avertira:Il est utilisé à peu près de la même manière et n'a même pas besoin d'un
import
:Vous vous demandez peut-être alors pourquoi utiliser une énumération plutôt que des objets de cas. En fait, les objets de cas présentent de nombreux avantages, comme ici. La classe Enumeration, cependant, possède de nombreuses méthodes Collection, telles que des éléments (itérateur sur Scala 2.8), qui renvoie un Iterator, une carte, un flatMap, un filtre, etc.
Cette réponse est essentiellement une partie sélectionnée de cet article dans mon blog.
la source
Symbol
instances ne peuvent pas avoir d'espaces ou de caractères spéciaux. La plupart des gens lors de leur première rencontre avec laSymbol
classe le pensent probablement, mais c'est en fait incorrect.Symbol("foo !% bar -* baz")
compile et fonctionne parfaitement bien. En d'autres termes, vous pouvez parfaitement créer desSymbol
instances enveloppant n'importe quelle chaîne (vous ne pouvez tout simplement pas le faire avec le sucre syntaxique "coma unique"). La seule chose quiSymbol
garantit est l'unicité d'un symbole donné, ce qui le rend légèrement plus rapide à comparer et à faire correspondre.String
, par exemple, comme argument à unSymbol
paramètre.String
par une autre classe qui est essentiellement un wrapper autour d'une chaîne et peut être librement convertie dans les deux sens (comme c'est le cas pourSymbol
). Je suppose que c'est ce que vous vouliez dire en disant "Cela ne vous donnera aucune sécurité de type", ce n'était tout simplement pas très clair étant donné que OP a explicitement demandé des solutions sûres de type. Je ne savais pas si au moment de la rédaction de cet article, vous saviez que non seulement ce type n'est pas sûr car ce ne sont pas du tout des énumérations, mais aussiSymbol
qu'il ne garantit même pas que l'argument passé n'aura pas de caractères spéciaux.'foo
notation qui ne se opposent chaînes non identifiantes). C'est cette idée fausse que je voulais dissiper pour tout futur lecteur.Une façon un peu moins verbeuse de déclarer les énumérations nommées:
Bien sûr, le problème ici est que vous devrez synchroniser l'ordre des noms et des valeurs, ce qui est plus facile à faire si le nom et la valeur sont déclarés sur la même ligne.
la source
Vous pouvez utiliser une classe abstraite scellée au lieu de l'énumération, par exemple:
la source
vient de découvrir enumeratum . c'est assez étonnant et tout aussi étonnant ce n'est pas plus connu!
la source
Après avoir fait des recherches approfondies sur toutes les options autour des "énumérations" dans Scala, j'ai posté un aperçu beaucoup plus complet de ce domaine sur un autre thread StackOverflow . Il comprend une solution au modèle "trait scellé + objet cas" où j'ai résolu le problème de commande d'initialisation de classe / objet JVM.
la source
Dotty (Scala 3) aura des énumérations natives prises en charge. Vérifiez ici et ici .
la source
À Scala, il est très à l'aise avec https://github.com/lloydmeta/enumeratum
Le projet est vraiment bon avec des exemples et de la documentation
Cet exemple de leur documentation devrait vous intéresser
la source