Existe-t-il des lignes directrices de bonnes pratiques sur quand utiliser les classes de cas vs s’étendant de l’énumération Scala ?
Ils semblent offrir certains des mêmes avantages.
Existe-t-il des lignes directrices de bonnes pratiques sur quand utiliser les classes de cas vs s’étendant de l’énumération Scala ?
Ils semblent offrir certains des mêmes avantages.
Une grande différence est qu' Enumeration
s viennent avec le soutien de l'instanciation de certains name
Chaîne de caractères. Par exemple:
object Currency extends Enumeration {
val GBP = Value("GBP")
val EUR = Value("EUR") //etc.
}
Ensuite, vous pouvez faire:
val ccy = Currency.withName("EUR")
Ceci est utile lorsque l'on souhaite faire persister les énumérations (par exemple, pour une base de données) ou les créer à partir de données contenues dans des fichiers résidant. Cependant, je trouve en général que les énumérations sont un peu maladroit dans Scala et avoir la sensation d'un maladroit add-on, donc j'ai maintenant tendance à utiliser case object
s. Un case object
est plus souple qu'un enum:
sealed trait Currency { def name: String }
case object EUR extends Currency { val name = "EUR" } //etc.
case class UnknownCurrency(name: String) extends Currency
Maintenant, j'ai l'avantage de...
trade.ccy match {
case EUR =>
case UnknownCurrency(code) =>
}
Pour continuer à suivre les autres réponses ici, les principaux inconvénients de l' case object
s sur Enumeration
s sont:
Ne peut pas effectuer une itération sur toutes les instances de la "énumération". C'est certainement le cas, mais j'ai trouvé ça extrêmement rare en pratique qu'il est nécessaire.
Ne peut pas instancier facilement à partir persisté valeur. C'est également vrai, mais, sauf dans le cas de grand énumérations (par exemple, toutes les devises), ce n'est pas de présenter une surcharge énorme.
Cas des objets déjà le retour de leur nom pour leur toString méthodes, afin de la passer sous séparément, il est inutile. Voici une version similaire à jho (méthodes pratiques omis par souci de clarté):
trait Enum[A] {
trait Value { self: A => }
val values: List[A]
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
val values = List(EUR, GBP)
}
Les objets sont paresseux; à l'aide de vals-les-bains au lieu de cela, nous pouvons supprimer la liste, mais avoir à répéter le nom de:
trait Enum[A <: {def name: String}] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed abstract class Currency(val name: String) extends Currency.Value
object Currency extends Enum[Currency] {
val EUR = new Currency("EUR") {}
val GBP = new Currency("GBP") {}
}
Si vous n'avez pas l'esprit de la tricherie certains, vous pouvez pré-charger votre énumération de valeurs à l'aide de l'API reflection ou quelque chose comme Google Réflexions. Non paresseux cas des objets de vous donner le plus propre syntaxe:
trait Enum[A] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
}
Agréable et propre, avec tous les avantages de classes Java et les énumérations. Personnellement, j'définir les valeurs d'énumération à l'extérieur de l'objet, pour mieux correspondre à idiomatiques Scala code:
object Currency extends Enum[Currency]
sealed trait Currency extends Currency.Value
case object EUR extends Currency
case object GBP extends Currency
Les avantages de l'utilisation des classes de cas sur les Énumérations sont:
Les avantages de l'utilisation des Énumérations à la place de classes de cas sont les suivants:
Donc, en général, si vous avez besoin d'une liste de constantes simple par nom, utiliser les énumérations. Sinon, si vous avez besoin de quelque chose d'un peu plus complexes ou qui veulent le plus de sécurité, le compilateur vous dire si vous avez tous les matches spécifié, cas d'utilisation des classes.
Mise à JOUR: Le code ci-dessous a un bug, décrit ici. Le programme de test ci-dessous fonctionne, mais si vous étiez à utiliser DayOfWeek.Mon (par exemple) avant de DayOfWeek lui-même, il ne pourrait pas parce que DayOfWeek n'a pas été initialisé (utilisation intérieure d'un objet ne cause pas un objet extérieur à être initialisé). Vous pouvez toujours utiliser ce code si vous faites quelque chose comme val enums = Seq( DayOfWeek )
dans votre classe principale, forcer l'initialisation de votre énumérations, ou vous pouvez utiliser chaotic3quilibrium modifications. Impatient à une macro-fonction enum!
Si vous voulez
les mesures suivantes peuvent être d'intérêt. Commentaires de bienvenue.
Dans cette application il y a abstrait Enum et EnumVal les classes de base, qui vous étendre. Nous allons voir ces classes dans une minute, mais d'abord, voici comment vous définiriez enum:
object DayOfWeek extends Enum {
sealed abstract class Val extends EnumVal
case object Mon extends Val; Mon()
case object Tue extends Val; Tue()
case object Wed extends Val; Wed()
case object Thu extends Val; Thu()
case object Fri extends Val; Fri()
case object Sat extends Val; Sat()
case object Sun extends Val; Sun()
}
Notez que vous devez utiliser chaque valeur d'enum (appeler sa méthode apply) pour l'amener à la vie. [Je tiens intérieure d'objets n'étaient pas paresseux à moins de me demander spécifiquement pour eux. Je pense.]
On pourrait bien sûr ajouter des méthodes/données à DayOfWeek, Val, ou le cas particulier des objets si on le désire.
Et voici comment vous pourriez utiliser un enum:
object DayOfWeekTest extends App {
// To get a map from Int id to enum:
println( DayOfWeek.valuesById )
// To get a map from String name to enum:
println( DayOfWeek.valuesByName )
// To iterate through a list of the enum values in definition order,
// which can be made different from ID order, and get their IDs and names:
DayOfWeek.values foreach { v => println( v.id + " = " + v ) }
// To sort by ID or name:
println( DayOfWeek.values.sorted mkString ", " )
println( DayOfWeek.values.sortBy(_.toString) mkString ", " )
// To look up enum values by name:
println( DayOfWeek("Tue") ) // Some[DayOfWeek.Val]
println( DayOfWeek("Xyz") ) // None
// To look up enum values by id:
println( DayOfWeek(3) ) // Some[DayOfWeek.Val]
println( DayOfWeek(9) ) // None
import DayOfWeek._
// To compare enums as ordinals:
println( Tue < Fri )
// Warnings about non-exhaustive pattern matches:
def aufDeutsch( day: DayOfWeek.Val ) = day match {
case Mon => "Montag"
case Tue => "Dienstag"
case Wed => "Mittwoch"
case Thu => "Donnerstag"
case Fri => "Freitag"
// Commenting these out causes compiler warning: "match is not exhaustive!"
// case Sat => "Samstag"
// case Sun => "Sonntag"
}
}
Voici ce que vous obtenez lorsque vous compilez:
DayOfWeekTest.scala:31: warning: match is not exhaustive!
missing combination Sat
missing combination Sun
def aufDeutsch( day: DayOfWeek.Val ) = day match {
^
one warning found
Vous pouvez remplacer "jour de match" avec "( jour: @décochée ) match" où vous ne voulez pas de tels avertissements, ou simplement inclure un fourre-tout à la fin.
Lorsque vous exécutez le programme ci-dessus, vous obtenez ce résultat:
Map(0 -> Mon, 5 -> Sat, 1 -> Tue, 6 -> Sun, 2 -> Wed, 3 -> Thu, 4 -> Fri)
Map(Thu -> Thu, Sat -> Sat, Tue -> Tue, Sun -> Sun, Mon -> Mon, Wed -> Wed, Fri -> Fri)
0 = Mon
1 = Tue
2 = Wed
3 = Thu
4 = Fri
5 = Sat
6 = Sun
Mon, Tue, Wed, Thu, Fri, Sat, Sun
Fri, Mon, Sat, Sun, Thu, Tue, Wed
Some(Tue)
None
Some(Thu)
None
true
À noter que depuis la Liste et les Cartes sont immuables, vous pouvez facilement supprimer des éléments pour créer des sous-ensembles, sans casser la enum lui-même.
Voici l'énumération de la classe elle-même (et EnumVal à l'intérieur):
abstract class Enum {
type Val <: EnumVal
protected var nextId: Int = 0
private var values_ = List[Val]()
private var valuesById_ = Map[Int ,Val]()
private var valuesByName_ = Map[String,Val]()
def values = values_
def valuesById = valuesById_
def valuesByName = valuesByName_
def apply( id : Int ) = valuesById .get(id ) // Some|None
def apply( name: String ) = valuesByName.get(name) // Some|None
// Base class for enum values; it registers the value with the Enum.
protected abstract class EnumVal extends Ordered[Val] {
val theVal = this.asInstanceOf[Val] // only extend EnumVal to Val
val id = nextId
def bumpId { nextId += 1 }
def compare( that:Val ) = this.id - that.id
def apply() {
if ( valuesById_.get(id) != None )
throw new Exception( "cannot init " + this + " enum value twice" )
bumpId
values_ ++= List(theVal)
valuesById_ += ( id -> theVal )
valuesByName_ += ( toString -> theVal )
}
}
}
Et voici une utilisation plus poussée de ce qui contrôle l'Id et ajoute des données/méthodes du Val de l'abstraction et de l'enum lui-même:
object DayOfWeek extends Enum {
sealed abstract class Val( val isWeekday:Boolean = true ) extends EnumVal {
def isWeekend = !isWeekday
val abbrev = toString take 3
}
case object Monday extends Val; Monday()
case object Tuesday extends Val; Tuesday()
case object Wednesday extends Val; Wednesday()
case object Thursday extends Val; Thursday()
case object Friday extends Val; Friday()
nextId = -2
case object Saturday extends Val(false); Saturday()
case object Sunday extends Val(false); Sunday()
val (weekDays,weekendDays) = values partition (_.isWeekday)
}
Un autre inconvénient des classes affaire contre énumérations lorsque vous devez effectuer une itération ou filtrer dans toutes les instances. Il s’agit d’une fonctionnalité d’énumération (et les énumérations Java aussi bien) alors que les classes affaire automatiquement ne supportent pas cette fonctionnalité.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.