Ricerca…


Osservazioni

L'approccio con sealed trait e case objects è preferito perché l'enumerazione di Scala ha alcuni problemi:

  1. Le enumerazioni hanno lo stesso tipo dopo la cancellazione.
  2. Il compilatore non si lamenta del fatto che "Match non è esaustivo", se il caso è mancato fallirà in runtime 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)

Confrontare 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 spiegazione più dettagliata è presentata in questo articolo su Enumerazione di Scala .

Giorni della settimana usando Enumerazione Scala

Le enumerazioni Java-like possono essere create estendendo Enumeration .

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

È anche possibile aggiungere un nome leggibile dall'uomo per i valori in un'enumerazione:

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

Fai attenzione al comportamento non tipicamente errato, in cui diverse enumerazioni possono valutare come lo stesso tipo di istanza:

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

Utilizzo di tratti sigillati e oggetti del caso

Un'alternativa all'estensione Enumeration sta utilizzando oggetti caso 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 parola chiave sealed garantisce che il tratto WeekDay non possa essere esteso in un altro file. Ciò consente al compilatore di fare alcune ipotesi, incluso che tutti i valori possibili di WeekDay sono già elencati.

Uno svantaggio è che questo metodo non consente di ottenere un elenco di tutti i valori possibili. Per ottenere una tale lista deve essere fornita esplicitamente:

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

Le classi di casi possono anche estendere un tratto sealed . Pertanto, oggetti e case case possono essere mescolati per creare gerarchie complesse:

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 altro svantaggio è che non c'è modo di accedere a un nome di variabile dell'enumerazione di un oggetto sealed , o cercare da esso. Se è necessario un tipo di nome associato a ciascun valore, deve essere definito 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 semplicemente:

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

Utilizzo di tratti tratti e casi e di tutti i valori-macro

Questa è solo un'estensione della variante del tratto sigillato in cui una macro genera un set con tutte le istanze in fase di compilazione. Ciò omette lo svantaggio che uno sviluppatore può aggiungere un valore all'enumerazione ma dimentica di aggiungerlo al set allElements.

Questa variante diventa particolarmente utile per grandi enumerazioni.

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

Per far funzionare questo è necessario questa 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
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow