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?
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)
I can't offer a complete solution, but maybe this is a start:
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!
I guess there is a much more straight-forward way than this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With