如何获得Scala 2.10反射引用的实际对象?

时间:2012-08-26 08:45:58

标签: scala reflection scala-2.10

请考虑以下代码:

object ResponseType extends Enumeration {
  val Listing, Album = Value
}

我可以像Symbol那样引用这个对象:

import reflect.runtime.universe._
val s = typeOf[ResponseType.Value].asInstanceOf[TypeRef].pre.typeSymbol

现在,有了这个符号,我怎样才能获得实际的ResponseType对象?

1 个答案:

答案 0 :(得分:13)

scala> val moduleClass = typeOf[ResponseType.Value].asInstanceOf[TypeRef].pre.typeSymbol
moduleClass: reflect.runtime.universe.Symbol = object ResponseType

scala> val module = moduleClass.owner.typeSignature.member(moduleClass.name.toTermName)
module: reflect.runtime.universe.Symbol = object ResponseType

scala> reflect.runtime.currentMirror.reflectModule(module.asModule).instance
res9: Any = ResponseType

现在,一些解释是有序的,因为这是一个非常模糊的实现细节,我们(还是!)无法在公共API中进行抽象。

对于每个object Scala创建一个表示其签名的基础类,内部称为模块类。例如,如果编译object C,编译器将生成C$.class。这正是模块类。

请注意,模块类与伴随类不同。比方说,对于case class C,编译器将生成三个符号:type Cterm C和(另一个)type C,其中第一个type C表示C类(其中包含自动生成的副本,productPrefix,productArity等),第二个type C表示对象C的签名(包含自动生成的工厂,提取器等)。不会有任何名称冲突,因为模块类没有直接添加到符号表中,只能通过<module>.moduleClass使用。


所以你从typeOf[ResponseType.Value].asInstanceOf[TypeRef].pre.typeSymbol咒语中得到的是一个代表模块类的符号。 API中没有任何功能可以让您从模块类中获取模块符号。在编译器内部肯定有一个,但我们决定不公开这个实现细节,因为它可能很快就会改变。

要访问源模块,您需要转到owner,查看其成员列表,并查找与模块类具有相同名称的对象。这正是moduleClass.owner.typeSignature.member(moduleClass.name.toTermName)的作用。一个小问题是,如果在同一范围内您有一个具有相同名称的方法,那么member将返回一个重载符号,您需要执行.member(...).suchThat(_.isModule)之类的操作。

之后,这是一块蛋糕。


编辑。实际上我们正在考虑引入ClassSymbol.module,否则将返回模块类的源模块符号,否则返回NoSymbol。很可能这最终会在RC1中结束。按照发行说明进行操作。