按名称检索Scala枚举常量

时间:2016-07-25 15:01:08

标签: scala enums introspection

我想按名称检索scala枚举常量。

Dmitriy Yefremov提出了Scala 2.10的解决方案(@see http://yefremov.net/blog/scala-enum-by-name/

代码崩溃

private def factoryMethodSymbol(enumType: Type): MethodSymbol = {
  enumType.member(newTermName("withName")).asMethod // scala.ScalaReflectionException: <none> is not a method
}

我想更新此代码以使用Scala 2.11。 有什么想法吗?

3 个答案:

答案 0 :(得分:2)

您已经可以使用现有API执行此操作,您不需要解决方法:

def constantByName[T <: Enumeration](enum: T, key: String): Option[T#Value] = {
  enum.values.find(_.toString == key)
}

它有效,因为.values会为您提供List[Enum#Value],您可以查看匹配的内容。

答案 1 :(得分:1)

已知反射存在符号init问题,也缺乏线程安全性。也许这就是你引出症状的方法。

显示原始代码有效:

$ scala
Welcome to Scala 2.11.8 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_92).
Type in expressions for evaluation. Or try :help.

scala> object FunninessLevel extends Enumeration {
     |   type FunninessLevel = Value
     |   val LOL, ROFL, LMAO = Value
     | }
defined object FunninessLevel

scala> 

scala> :pa
// Entering paste mode (ctrl-D to finish)

import scala.reflect.runtime.universe._

/**
 * Scala [[Enumeration]] helpers implementing Scala versions of
 * Java's [[java.lang.Enum.valueOf(Class[Enum], String)]].
 * @author Dmitriy Yefremov
 */
object EnumReflector {

  private val mirror: Mirror = runtimeMirror(getClass.getClassLoader)

  /**
   * Returns a value of the specified enumeration with the given name.
   * @param name value name
   * @tparam T enumeration type
   * @return enumeration value, see [[scala.Enumeration.withName(String)]]
   */
  def withName[T <: Enumeration#Value: TypeTag](name: String): T = {
    typeOf[T] match {
      case valueType @ TypeRef(enumType, _, _) =>
        val methodSymbol = factoryMethodSymbol(enumType)
        val moduleSymbol = enumType.termSymbol.asModule
        reflect(moduleSymbol, methodSymbol)(name).asInstanceOf[T]
    }
  }

  /**
   * Returns a value of the specified enumeration with the given name.
   * @param clazz enumeration class
   * @param name value name
   * @return enumeration value, see [[scala.Enumeration#withName(String)]]
   */
  def withName(clazz: Class[_], name: String): Enumeration#Value = {
    val classSymbol = mirror.classSymbol(clazz)
    val methodSymbol = factoryMethodSymbol(classSymbol.toType)
    val moduleSymbol = classSymbol.companionSymbol.asModule
    reflect(moduleSymbol, methodSymbol)(name).asInstanceOf[Enumeration#Value]
  }

  private def factoryMethodSymbol(enumType: Type): MethodSymbol = {
    enumType.member(newTermName("withName")).asMethod
  }

  private def reflect(module: ModuleSymbol, method: MethodSymbol)(args: Any*): Any = {
    val moduleMirror = mirror.reflectModule(module)
    val instanceMirror = mirror.reflect(moduleMirror.instance)
    instanceMirror.reflectMethod(method)(args:_*)
  }

}


// Exiting paste mode, now interpreting.

warning: there were two deprecation warnings; re-run with -deprecation for details
import scala.reflect.runtime.universe._
defined object EnumReflector

scala> val level = EnumReflector.withName(FunninessLevel.getClass, "ROFL")
level: Enumeration#Value = ROFL

有时REPL强制意外初始化。显示命令行:

$ scalac reflect-enum.scala && scala reflect_enum.Test
reflect-enum.scala:45: warning: method companionSymbol in trait SymbolApi is deprecated: Use `companion` instead, but beware of possible changes in behavior
    val moduleSymbol = classSymbol.companionSymbol.asModule
                                   ^
reflect-enum.scala:50: warning: method newTermName in trait Names is deprecated: Use TermName instead
    enumType.member(newTermName("withName")).asMethod
                    ^
two warnings found
ROFL

答案 2 :(得分:0)

解决方案是:

def constantByName(clazz: Class[_],value:String):Enumeration#Value = 
{
  val module=mirror.reflectModule(
    mirror.classSymbol(clazz)
   .toType.typeSymbol.companion.asModule)
   .instance.asInstanceOf[Enumeration]
  module.values.find(_.toString == value).get
}