加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

自定义Scala枚举,最优雅的版本搜索

发布时间:2020-12-16 19:02:57 所属栏目:安全 来源:网络整理
导读:对于我的一个项目,我已经实施了一个基于的枚举 trait Enum[A] { trait Value { self: A = _values :+= this } private var _values = List.empty[A] def values = _values}sealed trait Currency extends Currency.Valueobject Currency extends Enum[Curren
对于我的一个项目,我已经实施了一个基于的枚举

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?
和How can I get the actual object referred to by Scala 2.10 reflection?

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)是在编译时生成的,而不是运行时.请注意,由于它是一个宏,所以类枚举类的定义必须与使用它的单独的项目(即枚举的具体子类像货币)在一起.这是一个相对简单的实现;你可以做更复杂的事情,例如遍历多层次的层次结构,以更复杂的代价找到更多的案例对象,但希望这将让你开始.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读