Zoeken…


Opmerkingen

Aanpak met sealed trait en case objects heeft de voorkeur omdat Scala-opsomming een paar problemen heeft:

  1. Tellingen hebben hetzelfde type na het wissen.
  2. Compiler klaagt niet over "Match is niet uitputtend", als geval wordt gemist zal het mislukken 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)

Vergelijken met:

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

Meer gedetailleerde uitleg wordt gepresenteerd in dit artikel over Scala-telling .

Dagen van de week met Scala-telling

Java-achtige opsommingen kunnen worden gemaakt door Enumeratie uit te breiden.

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

Het is ook mogelijk om een voor mensen leesbare naam toe te voegen voor waarden in een opsomming:

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

Pas op voor het niet-zo-typerende veilige gedrag, waarbij verschillende opsommingen als hetzelfde instantietype kunnen evalueren:

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

Gebruik van verzegelde eigenschap- en kastobjecten

Een alternatief voor het verlengen van de Enumeration is het gebruik van sealed case-objecten:

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
}

Het sealed trefwoord garandeert dat de eigenschap WeekDay niet in een ander bestand kan worden verlengd. Hierdoor kan de compiler bepaalde veronderstellingen maken, waaronder dat alle mogelijke waarden van WeekDay al zijn opgesomd.

Een nadeel is dat u met deze methode geen lijst met alle mogelijke waarden kunt verkrijgen. Om een dergelijke lijst te krijgen, moet deze expliciet worden verstrekt:

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

Gevalklassen kunnen ook een sealed eigenschap uitbreiden. Objecten en casusklassen kunnen dus worden gemengd om complexe hiërarchieën te maken:

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
}

Een ander nadeel is dat er geen manier is om toegang te krijgen tot de variabelenaam van de opsomming van een sealed object, of er naar te zoeken. Als u een soort naam nodig hebt die aan elke waarde is gekoppeld, moet deze handmatig worden gedefinieerd:

  sealed trait WeekDay { val name: String }

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

Of gewoon:

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

Gebruik van verzegelde eigenschap- en caseobjecten en allValues-macro

Dit is slechts een uitbreiding op de verzegelde eigenschapsvariant waarbij een macro een set genereert met alle instanties tijdens het compileren. Hiermee wordt het nadeel dat een ontwikkelaar een waarde aan de opsomming kan toevoegen, netjes weggelaten, maar vergeet deze aan de set allElements toe te voegen.

Deze variant wordt vooral handig voor grote enums.

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

Om dit te laten werken heb je deze macro nodig:

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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow