Можно ли использовать отражение, чтобы найти фактический тип поля, объявленного подтипом перечисления Scala? - PullRequest
4 голосов
/ 04 декабря 2011

Я хочу автоматически преобразовывать / приводить строки в значения перечисления Scala, но не зная заранее, из какого подкласса перечисления (подобъекта?) Объявлено объявление.

Итак, учитывая:

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 ]

1 голос
/ 05 декабря 2011

Конкретный тип теряется во время выполнения, однако вы можете захватить его во время компиляции через имплики . Вы можете указать неявное в вашем перечислении следующее:

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"

Вы также можете сделать следующее, если система типов не может разрешить ваше перечисление для применения правильного неявного в ваших использованиях, вы можете по умолчанию использовать полиморфизм if и предоставить метод установки, подобный следующему.

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" )
0 голосов
/ 11 мая 2016

Проверьте мой EnumReflector , который расскажет вам все о типах полей перечисления, если они объявлены в области действия пакета (без перечислений внутреннего класса, перечислений области действия).

Вам необходимо указать тип поля enum типа 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()

, теперь вы можете получить поле enum объекта и интерпретировать его имя или идентификатор через отражатель:

 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)

В проекте есть модульный тест, демонстрирующий это.

...