问题 如何使用scala反射API来获取所有包含的类


我有这样的课:

trait ThirdParty { def invoke = println("right") }

trait WeatherIcon { def invoke = println("wrong") }

class MyClass {

    object objA extends ThirdParty

    object objB extends WeatherIcon

}

如何使用Scala反射API迭代包含的对象并调用方法(如果它是a的实例) ThirdParty 类?


12816
2018-06-15 15:19


起源



答案:


根据soc写的,我得到了这个:

import scala.reflect.runtime.universe._
val members = typeOf[MyClass].members.filter(_.typeSignature match {
  case tpe if tpe <:< typeOf[ThirdParty] => true
  case NullaryMethodType(tpe) if tpe <:< typeOf[ThirdParty] => true
  case MethodType(Nil, tpe) if tpe <:< typeOf[ThirdParty] => true
  case _ => false
})

让我解释模式匹配。的类型 val 或者 object 可以直接比较,但功能类型略有不同。这里我匹配没有参数列表的方法,以及带有零参数列表的方法。

与soc的答案相比,这里存在一些差异。首先,我用 members 代替 declarations。这将返回继承的成员以及声明的成员 MyClass 本身。

其次,我检查它是一个值成员,而不是类型成员。您只能在值上调用方法,因此它看起来是合理的限制,尽管可能没有必要。 UPD。该 isValue 方法在2.10.0-RC1中不再可用,所以我删除了检查。

最后,我用 <:< 而不是检查每个父母的平等。

现在,来调用。我将更改上面的代码,因为调用取决于您拥有的成员类型,因此我们最好同时进行过滤和调用。我要改变了 members 至 nonPrivateMembers 同样,假设这是想要的。 UPD。 nonPrivateMembers 在2.10.0-RC1中不再可用,请使用 filter(!_.isPrivate) 如有必要。

我也会避免使用 typeOf,这不适用于REPL上的镜像。 UPD。在2.10.0-RC1中 typeOf 工作正常,但我会保持实现的骨架不变。

以上所有内容基本上都与事物的结构有关:一种类型的成员是什么,他们是什么样的成员,等等。什么时候想 使用 这个东西,你需要镜子。

每当你有一个符号或某种类型的东西 - 一个类,一个方法,一个obj等 - 你通过一个东西对那个东西采取行动 镜子。要(反射地)对象的实例进行操作,您需要一个实例镜像。要对方法执行操作,您需要一个方法镜像,依此类推。

因此,让我们尝试构建一个功能来执行所要求的操作:

import scala.reflect.runtime.universe._
def invoke[Target : TypeTag](obj: Any): Seq[Target] = {
  val mirror = runtimeMirror(obj.getClass.getClassLoader)
  val insMirror = mirror reflect obj
  val originType = insMirror.symbol.typeSignature
  val targetType = typeTag[Target].tpe

  val members = originType.members

  val result = members collect (member => member.typeSignature match {
    case tpe if tpe <:< typeOf[ThirdParty] =>
      if (member.isModule)
        (insMirror reflectModule member.asModule).instance
      else
        (insMirror reflectField member.asTerm).get
    case NullaryMethodType(tpe) if tpe <:< typeOf[ThirdParty] =>
      (insMirror reflectMethod member.asMethod).apply()
    case MethodType(Nil, tpe) if tpe <:< typeOf[ThirdParty] =>
      (insMirror reflectMethod member.asMethod).apply()
  })

  result.map(_.asInstanceOf[Target]).toSeq
}

请注意,使用Scala 2.10.0-M4无法恢复嵌套模块 - 应该可以使用M5或RC1。要使用M4测试此代码,请将模块代码替换为 null

这是一个示例:

scala> class MyClass {
    object objA extends ThirdParty
    object objB extends WeatherIcon
    val aVal = new ThirdParty {}
    val bVal = new WeatherIcon {}
    def aDef = new ThirdParty {}
    def bDef = new WeatherIcon {}
    def anotherDef() = new ThirdParty {}
    def yetAnotherDef() = new WeatherIcon {}
  }
defined class MyClass


scala> invoke[ThirdParty](new MyClass)
res88: Seq[ThirdParty] = List(MyClass$$anon$5@c250cba, MyClass$$anon$3@54668d90, MyClass$$anon$1@18d8143a, null)

14
2018-06-15 18:39



比我的例子好多了!谢谢! - soc
我认为typeSignature返回NullaryMethodTypes,它们不是ThirdParty的子类型?你能打印吗? _.typeSignature.kind? - Eugene Burmako
调用部分可以使用镜像API完成。更多信息: stackoverflow.com/questions/11020746/...。简而言之,您需要这样做 runtimeMirror(getClass.getClassLoader).reflect(<your instance>).reflectMethod(<method symbol>)(<args>) - Eugene Burmako
@EugeneBurmako我在哪里可以找到那些提取器?我看到类型有它们,但我似乎无法使用提取器找到类型的实例。 - Daniel C. Sobral
宣言: github.com/scala/scala/blob/...,定义: github.com/scalamacros/kepler/blob/... (即“对象NullaryMethodType扩展NullaryMethodTypeExtractor”部分) - Eugene Burmako


答案:


根据soc写的,我得到了这个:

import scala.reflect.runtime.universe._
val members = typeOf[MyClass].members.filter(_.typeSignature match {
  case tpe if tpe <:< typeOf[ThirdParty] => true
  case NullaryMethodType(tpe) if tpe <:< typeOf[ThirdParty] => true
  case MethodType(Nil, tpe) if tpe <:< typeOf[ThirdParty] => true
  case _ => false
})

让我解释模式匹配。的类型 val 或者 object 可以直接比较,但功能类型略有不同。这里我匹配没有参数列表的方法,以及带有零参数列表的方法。

与soc的答案相比,这里存在一些差异。首先,我用 members 代替 declarations。这将返回继承的成员以及声明的成员 MyClass 本身。

其次,我检查它是一个值成员,而不是类型成员。您只能在值上调用方法,因此它看起来是合理的限制,尽管可能没有必要。 UPD。该 isValue 方法在2.10.0-RC1中不再可用,所以我删除了检查。

最后,我用 <:< 而不是检查每个父母的平等。

现在,来调用。我将更改上面的代码,因为调用取决于您拥有的成员类型,因此我们最好同时进行过滤和调用。我要改变了 members 至 nonPrivateMembers 同样,假设这是想要的。 UPD。 nonPrivateMembers 在2.10.0-RC1中不再可用,请使用 filter(!_.isPrivate) 如有必要。

我也会避免使用 typeOf,这不适用于REPL上的镜像。 UPD。在2.10.0-RC1中 typeOf 工作正常,但我会保持实现的骨架不变。

以上所有内容基本上都与事物的结构有关:一种类型的成员是什么,他们是什么样的成员,等等。什么时候想 使用 这个东西,你需要镜子。

每当你有一个符号或某种类型的东西 - 一个类,一个方法,一个obj等 - 你通过一个东西对那个东西采取行动 镜子。要(反射地)对象的实例进行操作,您需要一个实例镜像。要对方法执行操作,您需要一个方法镜像,依此类推。

因此,让我们尝试构建一个功能来执行所要求的操作:

import scala.reflect.runtime.universe._
def invoke[Target : TypeTag](obj: Any): Seq[Target] = {
  val mirror = runtimeMirror(obj.getClass.getClassLoader)
  val insMirror = mirror reflect obj
  val originType = insMirror.symbol.typeSignature
  val targetType = typeTag[Target].tpe

  val members = originType.members

  val result = members collect (member => member.typeSignature match {
    case tpe if tpe <:< typeOf[ThirdParty] =>
      if (member.isModule)
        (insMirror reflectModule member.asModule).instance
      else
        (insMirror reflectField member.asTerm).get
    case NullaryMethodType(tpe) if tpe <:< typeOf[ThirdParty] =>
      (insMirror reflectMethod member.asMethod).apply()
    case MethodType(Nil, tpe) if tpe <:< typeOf[ThirdParty] =>
      (insMirror reflectMethod member.asMethod).apply()
  })

  result.map(_.asInstanceOf[Target]).toSeq
}

请注意,使用Scala 2.10.0-M4无法恢复嵌套模块 - 应该可以使用M5或RC1。要使用M4测试此代码,请将模块代码替换为 null

这是一个示例:

scala> class MyClass {
    object objA extends ThirdParty
    object objB extends WeatherIcon
    val aVal = new ThirdParty {}
    val bVal = new WeatherIcon {}
    def aDef = new ThirdParty {}
    def bDef = new WeatherIcon {}
    def anotherDef() = new ThirdParty {}
    def yetAnotherDef() = new WeatherIcon {}
  }
defined class MyClass


scala> invoke[ThirdParty](new MyClass)
res88: Seq[ThirdParty] = List(MyClass$$anon$5@c250cba, MyClass$$anon$3@54668d90, MyClass$$anon$1@18d8143a, null)

14
2018-06-15 18:39



比我的例子好多了!谢谢! - soc
我认为typeSignature返回NullaryMethodTypes,它们不是ThirdParty的子类型?你能打印吗? _.typeSignature.kind? - Eugene Burmako
调用部分可以使用镜像API完成。更多信息: stackoverflow.com/questions/11020746/...。简而言之,您需要这样做 runtimeMirror(getClass.getClassLoader).reflect(<your instance>).reflectMethod(<method symbol>)(<args>) - Eugene Burmako
@EugeneBurmako我在哪里可以找到那些提取器?我看到类型有它们,但我似乎无法使用提取器找到类型的实例。 - Daniel C. Sobral
宣言: github.com/scala/scala/blob/...,定义: github.com/scalamacros/kepler/blob/... (即“对象NullaryMethodType扩展NullaryMethodTypeExtractor”部分) - Eugene Burmako


我无法提供完整的解决方案,但也许这是一个开始:

import reflect.runtime.universe._

val myClassType = typeOf[MyClass]    // Get the type of McClass
val thirdPartyType = typeOf[ThirdParty] // Get the type of ThirdParty
val methodToInvoke = newTermName("invoke")

val declsOfMyClass = myClassType.declarations // Get the declarations of MyClass

val subtypesOfThirdParty = 
  declsOfMyClass.filter(_.typeSignature.parents.contains(thirdPartyType))

val methodsToInvoke =             // Now we have the methods.
  subtypesOfThirdParty.map(tps => tps.typeSignature.member(methodToInvoke))

// TODO: Invoke!

我想有一种比这更简单的方式。


2
2018-06-15 16:07