Buscar..


Observaciones

Se prefiere el enfoque con sealed trait y case objects porque la enumeración de Scala tiene algunos problemas:

  1. Las enumeraciones tienen el mismo tipo después del borrado.
  2. El compilador no se queja sobre "La coincidencia no es exhaustiva", si se pierde el caso, fallará en el tiempo de ejecución de 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)

Comparar con:

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 {
                                          ^

Una explicación más detallada se presenta en este artículo sobre Scala Enumeration .

Días de la semana usando Scala Enumeration

Las enumeraciones similares a Java se pueden crear extendiendo la enumeración .

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

También es posible agregar un nombre legible para los valores en una enumeración:

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

Tenga cuidado con el comportamiento no tan seguro de los tipos, en el que diferentes enumeraciones se pueden evaluar como el mismo tipo de instancia:

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

Usando rasgos sellados y objetos de caja

Una alternativa a la extensión de la Enumeration es usar objetos de casos 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
}

La palabra clave sealed garantiza que el rasgo WeekDay no se puede extender en otro archivo. Esto le permite al compilador hacer ciertas suposiciones, incluyendo que todos los valores posibles de WeekDay ya están enumerados.

Un inconveniente es que este método no le permite obtener una lista de todos los valores posibles. Para obtener dicha lista se debe proporcionar explícitamente:

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

Las clases de casos también pueden extender un rasgo sealed . Por lo tanto, los objetos y las clases de casos se pueden mezclar para crear jerarquías complejas:

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
}

Otro inconveniente es que no hay forma de acceder al nombre de variable de la enumeración de un objeto sealed , o buscar por él. Si necesita algún tipo de nombre asociado a cada valor, debe definirse manualmente:

  sealed trait WeekDay { val name: String }

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

O solo:

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

Uso de rasgos sellados y objetos de caja y allValues-macro

Esta es solo una extensión de la variante de rasgo sellado donde una macro genera un conjunto con todas las instancias en tiempo de compilación. Esto omite el inconveniente de que un desarrollador puede agregar un valor a la enumeración, pero se olvida de agregarlo al conjunto allElements.

Esta variante es especialmente útil para grandes enumeraciones.

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]
}

Para que esto funcione necesitas esta 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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow