在自定义枚举中使用反射来获取可能的值

时间:2016-06-10 20:30:00

标签: scala reflection types

我正在处理一些使用自定义枚举的代码,以确保完整性检查,并且需要使用反射来获取可能的值列表。像下面这样的一些代码已经存在于代码库中(所有不相关的代码都被删除和/或重命名)我将它们组合成一个独立的可运行格式。

import scala.reflect.runtime.universe.TypeTag

object ReflectionUtil {

  def modules[A : TypeTag](parent: Any) = {
    import scala.reflect.runtime.{currentMirror, universe}
    val members = currentMirror.classSymbol(parent.getClass).toType.members
      .filter{ symbol =>
        println(s"$symbol: ${symbol.typeSignature} <:< ${universe.typeOf[A]} is ${symbol.typeSignature <:< universe.typeOf[A]}")
        symbol.typeSignature <:< universe.typeOf[A]
      }
  }
}

trait Enumeration {
  type Value <: V

  protected[this] trait V

  lazy val values = ReflectionUtil.modules[V](this)
}

object Switch extends Enumeration {
  sealed trait Value extends V
  case object On extends Value
  case object Off extends Value
}

object Main {
  def main(args: Array[String]) = println(Switch.values)
}

这里的问题是行symbol.typeSignature <:< universe.typeOf[A],它会为出现的所有内容返回false,您可以在输出中看到:

object Off: Switch.Off.type <:< Enumeration.this.V is false
object On: Switch.On.type <:< Enumeration.this.V is false
constructor Switch: ()Switch.type <:< Enumeration.this.V is false
value values: => scala.Unit <:< Enumeration.this.V is false
trait V: scala.AnyRef {

} <:< Enumeration.this.V is false
method $init$: ()scala.Unit <:< Enumeration.this.V is false
method $asInstanceOf: [T0]()T0 <:< Enumeration.this.V is false
method $isInstanceOf: [T0]()Boolean <:< Enumeration.this.V is false
method synchronized: [T0](x$1: T0)T0 <:< Enumeration.this.V is false
method ##: ()Int <:< Enumeration.this.V is false
method !=: (x$1: Any)Boolean <:< Enumeration.this.V is false
method ==: (x$1: Any)Boolean <:< Enumeration.this.V is false
method ne: (x$1: AnyRef)Boolean <:< Enumeration.this.V is false
method eq: (x$1: AnyRef)Boolean <:< Enumeration.this.V is false
method notifyAll: ()Unit <:< Enumeration.this.V is false
method notify: ()Unit <:< Enumeration.this.V is false
method clone: ()java.lang.Object <:< Enumeration.this.V is false
method getClass: ()java.lang.Class[_] <:< Enumeration.this.V is false
method hashCode: ()Int <:< Enumeration.this.V is false
method toString: ()java.lang.String <:< Enumeration.this.V is false
method equals: (x$1: Any)Boolean <:< Enumeration.this.V is false
method wait: ()Unit <:< Enumeration.this.V is false
method wait: (x$1: Long)Unit <:< Enumeration.this.V is false
method wait: (x$1: Long, x$2: Int)Unit <:< Enumeration.this.V is false
method finalize: ()Unit <:< Enumeration.this.V is false
method asInstanceOf: [T0]=> T0 <:< Enumeration.this.V is false
method isInstanceOf: [T0]=> Boolean <:< Enumeration.this.V is false
()

注意输出的前两行。 为什么即使对于看起来应该符合的类型(我真正感兴趣的枚举类型)以及我该如何解决这个问题,它总是会变回错误?

重申:我希望从lazy val values得到的是On中相关的枚举案例对象OffSwitch,这需要自动化以便它可以使用许多其他类似的枚举类型。

1 个答案:

答案 0 :(得分:0)

为了后代,我会回答我自己的问题。

这是按预期工作的代码:

import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror

object ReflectionUtil {

  def modules[A : TypeTag](parent: Any): Iterable[A] = {
    val baseType = typeOf[A]
    for {
      symbol <- currentMirror.classSymbol(parent.getClass).toType.members
      if symbol.isModule && symbol.info.widen <:< baseType
    } yield currentMirror.reflectModule(symbol.asModule).instance.asInstanceOf[A]
  }
}

trait EnumeratedValue

trait Enumeration {
  type Value <: EnumeratedValue

  lazy val values = ReflectionUtil.modules[EnumeratedValue](this)
}

object Switch extends Enumeration {
  sealed trait Value extends EnumeratedValue
  case object On extends Value
  case object Off extends Value
}

object Main {
  def main(args: Array[String]) = println(Switch.values)
}

事实证明,由于traits嵌入到生成的类字节码中,因此所有使用外部特征的类的内部特征都不相同。因此,它检查是否符合与函数定义中实际传递的特性不同的特征。通过拉出相关特征超出Enumeration特征,该函数现在可以正确地解析特征,因此它可以按预期工作。