How to use scala reflection API to get all contained classes

reflection, scala

Solution

Based on what soc wrote, I got this:

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

Let me explain the pattern match. The type of a `val` or an `object` can be compared directly, but functions have a slightly different type. Here I'm matching against methods with no parameter lists, and methods with a zero-arity parameter list.

There are some differences here compared to soc's answer. First, I use `members` instead of `declarations`. That returns inherited members as well as those that are declared on `MyClass` itself.

Second, I check that it is a value member, as opposed to a type member. You can only invoke methods on values, so it looked a reasonable restriction, though maybe unnecessary. upd. The `isValue` method is no longer available in 2.10.0-RC1, so I removed the check.

Finally, I use `<:<` instead of checking each parent for equality.

Now, to the invocation. I'm going to change the code above since invocation depends on what kind of member you have, so we'd best do filtering and invocation at the same time. I'm going to change from `members` to `nonPrivateMembers` as well, assuming that's what is wanted. upd. `nonPrivateMembers` is no longer available in 2.10.0-RC1, use `filter(!_.isPrivate)` if necessary.

And I'll also avoid using `typeOf`, which won't work with mirrors on the REPL. upd. In 2.10.0-RC1 `typeOf` is working finely, but I'll keep the skeleton of the implementation unchanged.

All of the above is basically concerned with the structure of things: what the members of a type are, what kind of members they are, and so on. When you want to use this stuff, in you need mirrors.

Whenever you have a symbol or a type for something -- a class, method, obj, etc -- you act on that thing through a mirror. To act (reflectively) on an instance of an object, you need an instance mirror. To act on a method, you need a method mirror, and so on.

So let's try to build a functon to do what's requested:

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
}

Note that nested modules cannot be recovered with Scala 2.10.0-M4 -- that should be possible with M5 or RC1. To test this code with M4, replace the module code with `null`.

Here's a sample:

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)

Problem

I got this kind of class: ``` trait ThirdParty { def invoke = println("right") } trait WeatherIcon { def invoke = println("wrong") } class MyClass { object objA extends ThirdParty object objB extends WeatherIcon } ``` How can I use the Scala reflection API to iterate through the contained objects and invoke a method if it is an instance of a `ThirdParty` class?

Original source

Related problems