Recherche…


Remarques

Une approche avec un sealed trait et des case objects est préférable car l'énumération Scala pose quelques problèmes:

  1. Les énumérations ont le même type après l'effacement.
  2. Le compilateur ne se plaint pas du fait que "Match n'est pas exhaustif", si un cas est manqué, il échouera lors de l'exécution scala.MatchError :
def isWeekendWithBug(day: WeekDays.Value): Boolean = day match {
  case WeekDays.Sun | WeekDays.Sat => true
}

isWeekendWithBug(WeekDays.Fri)
scala.MatchError: Fri (of class scala.Enumeration$Val)

Comparer avec:

def isWeekendWithBug(day: WeekDay): Boolean = day match {
  case WeekDay.Sun | WeekDay.Sat => true
}

Warning: match may not be exhaustive.
It would fail on the following inputs: Fri, Mon, Thu, Tue, Wed
def isWeekendWithBug(day: WeekDay): Boolean = day match {
                                          ^

Une explication plus détaillée est présentée dans cet article sur l'énumération Scala .

Jours de la semaine en utilisant l'énumération Scala

Les énumérations de type Java peuvent être créées en étendant l' énumération .

object WeekDays extends Enumeration {
  val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
}

def isWeekend(day: WeekDays.Value): Boolean = day match {
  case WeekDays.Sat | WeekDays.Sun => true
  case _ => false
}

isWeekend(WeekDays.Sun)
res0: Boolean = true

Il est également possible d'ajouter un nom lisible par l'homme pour les valeurs dans une énumération:

object WeekDays extends Enumeration {
      val Mon = Value("Monday")
      val Tue = Value("Tuesday")
      val Wed = Value("Wednesday")
      val Thu = Value("Thursday")
      val Fri = Value("Friday")
      val Sat = Value("Saturday")
      val Sun = Value("Sunday")
}

println(WeekDays.Mon)
>> Monday

WeekDays.withName("Monday") == WeekDays.Mon
>> res0: Boolean = true

Méfiez-vous du comportement peu fiable, dans lequel différentes énumérations peuvent être évaluées comme le même type d’instance:

object Parity extends Enumeration {
   val Even, Odd = Value
}
  
WeekDays.Mon.isInstanceOf[Parity.Value]
>> res1: Boolean = true

Utiliser des objets et des traits scellés

Une alternative à l'extension de l' Enumeration consiste à utiliser sealed objets de boîtier sealed :

sealed trait WeekDay

object WeekDay {
  case object Mon extends WeekDay
  case object Tue extends WeekDay
  case object Wed extends WeekDay
  case object Thu extends WeekDay
  case object Fri extends WeekDay
  case object Sun extends WeekDay
  case object Sat extends WeekDay
}

Le mot-clé sealed garantit que le trait WeekDay ne peut pas être étendu dans un autre fichier. Cela permet au compilateur de faire certaines hypothèses, y compris que toutes les valeurs possibles de WeekDay sont déjà énumérées.

Un inconvénient est que cette méthode ne vous permet pas d'obtenir une liste de toutes les valeurs possibles. Pour obtenir une telle liste, elle doit être fournie explicitement:

val allWeekDays = Seq(Mon, Tue, Wed, Thu, Fri, Sun, Sat)

Les classes de cas peuvent également étendre un trait sealed . Ainsi, les objets et les classes de cas peuvent être mélangés pour créer des hiérarchies complexes:

sealed trait CelestialBody
    
object CelestialBody {
  case object Earth extends CelestialBody
  case object Sun extends CelestialBody
  case object Moon extends CelestialBody
  case class Asteroid(name: String) extends CelestialBody
}

Un autre inconvénient est qu'il n'y a aucun moyen d'accéder au nom de la variable d'une énumération d'objet sealed , ou de la rechercher. Si vous avez besoin d'une sorte de nom associé à chaque valeur, celle-ci doit être définie manuellement:

  sealed trait WeekDay { val name: String }

  object WeekDay {
      case object Mon extends WeekDay { val name = "Monday" }
      case object Tue extends WeekDay { val name = "Tuesday" }
      (...)   
  }

Ou juste:

  sealed case class WeekDay(name: String)
    
  object WeekDay {
      object Mon extends WeekDay("Monday")
      object Tue extends WeekDay("Tuesday")
      (...)   
  }

Utilisation d'objets trait et de cas scellés et allValues-macro

Ceci est juste une extension sur la variante de trait scellée où une macro génère un ensemble avec toutes les instances au moment de la compilation. Cela omet bien l’inconvénient qu’un développeur peut ajouter une valeur à l’énumération mais oublier de l’ajouter à l’ensemble allElements.

Cette variante devient particulièrement pratique pour les grandes énumérations.

import EnumerationMacros._

sealed trait WeekDay
object WeekDay {
  case object Mon extends WeekDay
  case object Tue extends WeekDay
  case object Wed extends WeekDay
  case object Thu extends WeekDay
  case object Fri extends WeekDay
  case object Sun extends WeekDay
  case object Sat extends WeekDay
  val allWeekDays: Set[WeekDay] = sealedInstancesOf[WeekDay]
}

Pour que cela fonctionne, vous avez besoin de cette macro:

import scala.collection.immutable.TreeSet
import scala.language.experimental.macros
import scala.reflect.macros.blackbox

/**
A macro to produce a TreeSet of all instances of a sealed trait.
Based on Travis Brown's work:
http://stackoverflow.com/questions/13671734/iteration-over-a-sealed-trait-in-scala
CAREFUL: !!! MUST be used at END OF code block containing the instances !!!
*/
object EnumerationMacros {
  def sealedInstancesOf[A]: TreeSet[A] = macro sealedInstancesOf_impl[A]

  def sealedInstancesOf_impl[A: c.WeakTypeTag](c: blackbox.Context) = {
    import c.universe._

    val symbol = weakTypeOf[A].typeSymbol.asClass

    if  (!symbol.isClass || !symbol.isSealed)
      c.abort(c.enclosingPosition, "Can only enumerate values of a sealed trait or class.")
    else {

      val children = symbol.knownDirectSubclasses.toList

      if (!children.forall(_.isModuleClass)) c.abort(c.enclosingPosition, "All children must be objects.")
      else c.Expr[TreeSet[A]] {

        def sourceModuleRef(sym: Symbol) = Ident(sym.asInstanceOf[scala.reflect.internal.Symbols#Symbol
          ].sourceModule.asInstanceOf[Symbol]
        )

        Apply(
          Select(
            reify(TreeSet).tree,
            TermName("apply")
          ),
          children.map(sourceModuleRef(_))
        )
      }
    }
  }
}


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow