自定义Scala枚举,最优雅的版本搜索
对于我的一个项目,我已经实施了一个基于的枚举
trait Enum[A] { trait Value { self: A => _values :+= this } private var _values = List.empty[A] def values = _values } sealed trait Currency extends Currency.Value object Currency extends Enum[Currency] { case object EUR extends Currency case object GBP extends Currency } 从Case objects vs Enumerations in Scala.我工作不错,直到遇到以下问题.案例对象似乎很懒惰,如果我使用Currency.value,我可能会获得一个空的列表.在启动时可能会对所有枚举值进行调用,以使得值列表将被填充,但这将是一种打败点. 所以我冒险进入黑暗和未知的Scala反射的地方,并提出了这个解决方案,基于以下SO答案. Can I get a compile-time list of all of the case objects which derive from a sealed parent in Scala? import scala.reflect.runtime.universe._ abstract class Enum[A: TypeTag] { trait Value private def sealedDescendants: Option[Set[Symbol]] = { val symbol = typeOf[A].typeSymbol val internal = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol] if (internal.isSealed) Some(internal.sealedDescendants.map(_.asInstanceOf[Symbol]) - symbol) else None } def values = (sealedDescendants getOrElse Set.empty).map( symbol => symbol.owner.typeSignature.member(symbol.name.toTermName)).map( module => reflect.runtime.currentMirror.reflectModule(module.asModule).instance).map( obj => obj.asInstanceOf[A] ) } 令人惊奇的部分是它实际上是有效的,但是如果可以使这个更简单和更优雅,并且摆脱asInstanceOf调用,那么这个地狱是丑陋的,我会感兴趣的. 解决方法
这是一个简单的基于宏的实现:
import scala.language.experimental.macros import scala.reflect.macros.blackbox abstract class Enum[E] { def values: Seq[E] = macro Enum.caSEObjectsSeqImpl[E] } object Enum { def caSEObjectsSeqImpl[A: c.WeakTypeTag](c: blackbox.Context) = { import c.universe._ val typeSymbol = weakTypeOf[A].typeSymbol.asClass require(typeSymbol.isSealed) val subclasses = typeSymbol.knownDirectSubclasses .filter(_.asClass.isCaseClass) .map(s => Ident(s.companion)) .toList val seqTSymbol = weakTypeOf[Seq[A]].typeSymbol.companion c.Expr(Apply(Ident(seqTSymbol),subclasses)) } } 这样你可以写: sealed trait Currency object Currency extends Enum[Currency] { case object USD extends Currency case object EUR extends Currency } 那么那么 Currency.values == Seq(Currency.USD,Currency.EUR) 由于它是一个宏,Seq(Currency.USD,Currency.EUR)是在编译时生成的,而不是运行时.请注意,由于它是一个宏,所以类枚举类的定义必须与使用它的单独的项目(即枚举的具体子类像货币)在一起.这是一个相对简单的实现;你可以做更复杂的事情,例如遍历多层次的层次结构,以更复杂的代价找到更多的案例对象,但希望这将让你开始. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |