使用密封的 trait 和 case 物件以及 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(_))
)
}
}
}
}