12

我上了这样的课:

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

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

class MyClass {

    object objA extends ThirdParty

    object objB extends WeatherIcon

}

如果它是类的实例,我如何使用 Scala 反射 API 遍历包含的对象并调用方法ThirdParty

4

2 回答 2

15

根据 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
})

让我解释一下模式匹配。aval或 an的类型object可以直接比较,但函数的类型略有不同。在这里,我匹配没有参数列表的方法和具有零参数列表的方法。

与 soc 的答案相比,这里有一些差异。首先,我使用members而不是declarations. 这将返回继承的成员以及在MyClass其自身上声明的成员。

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

最后,我使用<:<而不是检查每个父母是否相等。

现在,到调用。我将更改上面的代码,因为调用取决于您拥有什么样的成员,所以我们最好同时进行过滤和调用。假设这就是我们想要的,我也会从 to 改变membersnonPrivateMembers更新。nonPrivateMembers在 2.10.0-RC1 中不再可用,filter(!_.isPrivate)如有必要,请使用。

而且我还将避免使用typeOf,它不适用于 REPL 上的镜像。更新。在 2.10.0-RC1typeOf中运行良好,但我将保持实现的框架不变。

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

每当你有某事物的符号或类型——类、方法、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)
于 2012-06-15T18:39:14.223 回答
2

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

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!

我想还有比这更直接的方法。

于 2012-06-15T16:07:01.613 回答