サーチ…


備考

Scalaの列挙にはいくつかの問題があるため、 sealed trait case objectscase objectsを使用するアプローチが優先されます。

  1. 列挙型は消去後も同じ型です。
  2. コンパイラは「マッチは網羅的ではありません」と不平を言っていません。大文字とscala.MatchErrorないと実行時に失敗します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)

と比べて:

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

Scala Enumerationの詳細については、この記事で詳しく説明しています

Scala Enumerationを使用した曜日

Enumerationを拡張することによって、Javaのような列挙型を作成できます

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

列挙の値に人間が判読可能な名前を追加することもできます。

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

異なる列挙型が同じインスタンス型として評価されるような、型どおりではない動作に注意してください。

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

密封された特性オブジェクトとケースオブジェクトの使用

Enumerationを拡張する代わりに、 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
}

sealedキーワードは、特性WeekDayが別のファイルで拡張できないことを保証します。これにより、 WeekDayすべての可能な値が既に列挙されていることを含めて、コンパイラは特定の前提を設定できます。

1つの欠点は、このメソッドではすべての可能な値のリストを取得できないということです。このようなリストを取得するには、明示的に指定する必要があります。

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

ケースクラスはsealed特性を拡張することもできます。したがって、オブジェクトとケースクラスを混合して複雑な階層を作成することができます。

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
}

もう一つの欠点は、 sealedオブジェクトの列挙体の変数名にアクセスする方法、またはそれによって検索する方法がないことです。各値に関連付けられた名前が必要な場合は、手動で定義する必要があります。

  sealed trait WeekDay { val name: String }

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

あるいは単に:

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

密封された特性とケースオブジェクトとallValues-macroの使用

これは、マクロがコンパイル時にすべてのインスタンスを含むセットを生成する密封された特性変形の単なる拡張です。これは、開発者が列挙に値を追加できますが、それをallElementsセットに追加することを忘れてしまうという欠点を省いています。

この変形は、特に大きな列挙型の場合に便利です。

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

これを行うには、次のマクロが必要です。

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
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow