是否可以使用反射来查找声明为Scala Enumeration子类型的字段的实际类型?

时间:2011-12-03 20:59:14

标签: scala reflection enumeration

我想自动将字符串转换/强制转换为Scala Enumeration Values,但事先不知道声明来自的Enumeration子类(子对象?)。

所以,给定:

object MyEnumeration extends Enumeration {
  type MyEnumeration = Value
  val FirstValue, SecondValue = Value
}

class MyThing {
  import MyEnumeration._
  var whichOne: MyEnumeration = FirstValue
}

我将如何实施以下内容?

val myThing = new MyThing()
setByReflection(myThing, "whichOne", "SecondValue")

我发现当我获得MyThing.whichOne(通过java.lang.Field)的类时,返回的是'scala.Enumeration $ Value',这对我来说并不具体,无法枚举所有可能值的名称。

2 个答案:

答案 0 :(得分:1)

特定类型在运行时丢失,但您可以通过implicits 在编译时捕获它。您可以在枚举中提供隐含的内容,如下所示。

object MyEnumeration extends Enumeration {
  type MyEnumeration = Value
  val FirstValue, SecondValue = Value
  implicit def convertMyEnumeration( value: String ) =
    MyEnumeration.withName( value )
}

class MyThing {
  import MyEnumeration._
  var whichOne: MyEnumeration = FirstValue
}

val myThing = new MyThing()
myThing.whichOne = "SecondValue"

如果类型系统无法解析您的枚举以在您的默认使用多态的用法中应用正确的隐含,并且提供如下的setter,您也可以执行以下操作:

class MySuperThing {
  def setWhichOne( value: String }
}

class MyThing extends MySuperThing {
  import MyEnumeration._
  var whichOne: MyEnumeration = FirstValue
  def setWhichOne( value: String ) = MyEnumeration.withName( value )
}

val myThing: MySuperThing = new MyThing()
myThing.setWhichOne( "SecondValue" )

答案 1 :(得分:0)

查看我的EnumReflector,它会告诉你关于枚举字段类型的所有内容,只要它们在包范围内声明(没有内部类枚举,函数作用域枚举)。

你需要为它提供枚举字段的scala类型(可以通过scala的反射界面找到)(请参阅下面有关如何从类[_]获取此内容)

val enumObjectType:Type = ... object's scala.Enumeration.Value field

val isEnum = EnumReflector.isEnumeration(enumObjectType)

val reflector = EnumReflector(typ)
val eid = reflector.toID(enumObject)
val enum = reflector.fromID(eid)
assertTrue(eid eq enum)

如何获取enumObjectType?

val typ = typeOf[EC]
// OR
val typ = getTypeForClass(classOf[EC]) // or from your package scanner

val enumGetters = classAccessors( typ ).filter(EnumReflector.isEnumeration(_))

// Let's looks at the 1st enum field
val reflector = EnumReflector(enumGetters.head.returnType)
val fieldName = enumGetters.head.name.toString

...现在你需要使用Java反射为这个类找到相同的方法。 Scala具有实例反射,但要求您在运行时对实例进行反射(这很慢)。更好地掌握Java反射调用方法:

 val enumGetter = toJavaClass(typ).getMethods.filter(_.getName==fieldName).head

从您的

开始
 val obj = new MyThing()

您现在可以获取对象的枚举字段并通过反射器解释其名称或ID:

 val enumObj = enumGetter.invoke(obj)
 val name = reflector.toName(enumObj)
 val enumObj2 reflector.fromName(name)
 assertTrue( enumObj2 eq enumObj )

支持功能:

import scala.reflect.runtime.universe._
val mirror = runtimeMirror(this.getClass.getClassLoader)

def toJavaClass(tpe:Type) = mirror.runtimeClass(tpe.typeSymbol.asClass)
def getTypeForClass(clazz: Class[_]): Type = mirror.classSymbol(clazz).toType

def classGetters(typ:Type) = typ.members.collect{case m:MethodSymbol=> m}.filter(_.isGetter)
def classAccessors(typ:Type) = typ.members.collect{case m:MethodSymbol=> m}.filter(_.isAccessor)

项目中有一个单元测试来演示它。