Suche…


Bemerkungen

Ein Ansatz mit sealed trait und case objects wird bevorzugt, da die Aufzählung von Scala einige Probleme aufweist:

  1. Aufzählungen haben nach dem Löschen denselben Typ.
  2. Der Compiler beschwert sich nicht über "Match ist nicht erschöpfend". Wenn die Groß- / Kleinschreibung übersehen wird, scala.MatchError in Laufzeit 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)

Vergleichen mit:

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

Eine ausführlichere Erklärung zu Scala Enumeration finden Sie in diesem Artikel .

Wochentage mit Scala Enumeration

Java-ähnliche Aufzählungen können durch Erweitern der Aufzählung erstellt werden .

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

Es ist auch möglich, einen von Menschen lesbaren Namen für Werte in einer Aufzählung hinzuzufügen:

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

Beachten Sie das nicht so typsichere Verhalten, bei dem verschiedene Enumerationen als derselbe Instanztyp ausgewertet werden können:

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

Verwendung versiegelter Eigenschaften und Fallobjekte

Eine Alternative zur Erweiterung der Enumeration besteht in der Verwendung sealed Fallobjekte:

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
}

Das sealed Schlüsselwort garantiert, dass das Merkmal WeekDay nicht in einer anderen Datei erweitert werden kann. Dadurch kann der Compiler bestimmte Annahmen treffen, einschließlich, dass alle möglichen Werte von WeekDay bereits aufgelistet sind.

Ein Nachteil ist, dass Sie mit dieser Methode keine Liste aller möglichen Werte abrufen können. Um eine solche Liste zu erhalten, muss diese explizit angegeben werden:

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

Fallklassen können auch eine sealed Eigenschaft erweitern. Daher können Objekte und Fallklassen gemischt werden, um komplexe Hierarchien zu erstellen:

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
}

Ein weiterer Nachteil besteht darin, dass es nicht möglich ist, auf den Variablennamen der Aufzählung eines sealed Objekts zuzugreifen oder danach zu suchen. Wenn Sie einen Namen für jeden Wert benötigen, muss dieser manuell definiert werden:

  sealed trait WeekDay { val name: String }

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

Oder nur:

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

Verwendung von versiegelten Merkmals- und Fallobjekten und allValues-Makro

Dies ist nur eine Erweiterung der versiegelten Merkmalsvariante, bei der ein Makro zur Kompilierzeit einen Satz mit allen Instanzen generiert. Dadurch wird der Nachteil vermieden, dass ein Entwickler der Aufzählung einen Wert hinzufügen kann, aber vergessen, ihn dem Satz allElements hinzuzufügen.

Diese Variante eignet sich besonders für große Aufzählungen.

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

Damit dies funktioniert, benötigen Sie dieses Makro:

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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow