I have a TypeTag
for one of several objects and I know that they extend a base trait with a known method signature. I also have access to the method's MethodSymbol
if I need it. What I want to do is:
def invokeMyMethod[T: TypeTag](myMethodSymbol: MethodSymbol): String = {
// I know T has structural type { def myMethod: String }
// so I want the result of calling T.myMethod but I don't have
// access to the actual object T, only its type.
}
Since I know the type represents an object, I know that the method is static and so I just need access to the singleton instance to be able to invoke the method. Unfortunately I can't find a way to get from the type to the instance.
I know I can obtain a RuntimeClass
instance from a runtimeMirror
, but I'm not sure what to do with that class once I have it. It seems to essentially have type AnyRef
, so I tried casting it with no luck:
def invokeMyMethod[T: TypeTag]: Any = {
val runtimeT = runtimeMirror(getClass.getClassLoader).runtimeClass(T)
runtimeT.asInstanceOf[{ def myMethod: String }].myMethod
// Error invoking method 'myMethod'
}
I also know I can get a ClassMirror
from my ClassSymbol
but that only seems to give access to the constructor MethodMirror
which doesn't help if my item is an object and not a class:
def invokeMyMethod[T: TypeTag](myMethodSymbol: MethodSymbol): Any = {
val mirror = runtimeMirror(getClass.getClassLoader)
val runtimeT = mirror.runtimeClass(T)
val mirrorT = mirror.reflect(runtimeT)
mirrorT.reflectMethod(myMethodSymbol)()
// Expected a member of class Class, you provided value T.myMethod
}
And I know if I had the actual runtime instance of T
it would be easy with an InstanceMirror
but I can't figure out how to get the InstanceMirror
of my object type.
Try
import scala.reflect.runtime.universe._
import scala.reflect.runtime
trait BaseTrait {
def myMethod: String
}
object MyObject extends BaseTrait {
override def myMethod: String = "MyObject.myMethod"
}
def invokeMyMethod[T: TypeTag]: String = {
val typ = typeOf[T]
val moduleSymbol = typ.termSymbol.asModule
val methodSymbol = typ.decl(TermName("myMethod")).asMethod
val runtimeMirror = runtime.currentMirror
val moduleMirror = runtimeMirror.reflectModule(moduleSymbol)
val instance = moduleMirror.instance
val instanceMirror = runtimeMirror.reflect(instance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror().asInstanceOf[String]
}
invokeMyMethod[MyObject.type] // MyObject.myMethod
If the object is nested into a class try
class Outer {
object `_` extends BaseTrait {
override def myMethod: String = "_.myMethod"
}
}
def invokeMyMethod[T: TypeTag]: String = {
val typ = typeOf[T]
val runtimeMirror = runtime.currentMirror
val moduleSymbol = typ.termSymbol.asModule
val outerClassSymbol = moduleSymbol.owner.asClass
val outerClassType = outerClassSymbol.typeSignature
val outerConstructorSymbol = outerClassType.decl(termNames.CONSTRUCTOR).asMethod
val outerClassMirror = runtimeMirror.reflectClass(outerClassSymbol)
val outerConstructorMirror = outerClassMirror.reflectConstructor(outerConstructorSymbol)
val outerInstance = outerConstructorMirror() // if class Outer has no-arg constructor
val outerInstanceMirror = runtimeMirror.reflect(outerInstance)
val moduleMirror = outerInstanceMirror.reflectModule(moduleSymbol)
val methodSymbol = typ.decl(TermName("myMethod")).asMethod
val instance = moduleMirror.instance
val instanceMirror = runtimeMirror.reflect(instance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror().asInstanceOf[String]
}
val outer = new Outer
invokeMyMethod[outer.`_`.type] // _.myMethod
If Outer
is a trait (abstract class) rather than class you can use Toolbox
trait Outer {
object `_` extends BaseTrait {
override def myMethod: String = "_.myMethod"
}
}
def invokeMyMethod[T: TypeTag]: String = {
val typ = typeOf[T]
val runtimeMirror = runtime.currentMirror
val toolbox = runtimeMirror.mkToolBox()
val outerClassSymbol = toolbox.define(
q"class OuterImpl extends com.example.Outer".asInstanceOf[ClassDef]
).asClass
toolbox.eval(q"(new $outerClassSymbol).`_`.myMethod").asInstanceOf[String]
}
val outer = new Outer {}
invokeMyMethod[outer.`_`.type] // _.myMethod
or
def invokeMyMethod[T: TypeTag]: String = {
val typ = typeOf[T]
val runtimeMirror = runtime.currentMirror
val toolbox = runtimeMirror.mkToolBox()
val toolboxMirror = toolbox.mirror
val moduleSymbol = typ.termSymbol.asModule
val outerClassSymbol = toolbox.define(
q"class OuterImpl extends com.example.Outer".asInstanceOf[ClassDef]
).asClass
val outerClassType = outerClassSymbol.typeSignature
val outerConstructorSymbol = outerClassType.decl(termNames.CONSTRUCTOR).asMethod
val outerClassMirror = toolboxMirror.reflectClass(outerClassSymbol)
val outerConstructorMirror = outerClassMirror.reflectConstructor(outerConstructorSymbol)
val outerInstance = outerConstructorMirror()
val outerInstanceMirror = runtimeMirror.reflect(outerInstance)
val moduleMirror = outerInstanceMirror.reflectModule(moduleSymbol)
val methodSymbol = typ.decl(TermName("myMethod")).asMethod
val instance = moduleMirror.instance
val instanceMirror = toolboxMirror.reflect(instance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror().asInstanceOf[String]
}
val outer = new Outer {}
invokeMyMethod[outer.`_`.type] // _.myMethod
Or if you can use existing instance of the outer class/trait try
val outer = new Outer
def invokeMyMethod[T: TypeTag]: String = {
val typ = typeOf[T]
val runtimeMirror = runtime.currentMirror
val moduleSymbol = typ.termSymbol.asModule
val outerInstanceMirror = runtimeMirror.reflect(outer)
val moduleMirror = outerInstanceMirror.reflectModule(moduleSymbol)
val methodSymbol = typ.decl(TermName("myMethod")).asMethod
val instance = moduleMirror.instance
val instanceMirror = runtimeMirror.reflect(instance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror().asInstanceOf[String]
}
invokeMyMethod[outer.`_`.type] // _.myMethod
Actually you can use outer
deconstructing the input type
def invokeMyMethod[T: TypeTag]: String = {
val typ = typeOf[T]
val outerSymbol = typ match {
case SingleType(pre, _) => pre.termSymbol
}
val runtimeMirror = runtime.currentMirror
val toolbox = runtimeMirror.mkToolBox()
toolbox.eval(q"$outerSymbol.`_`.myMethod").asInstanceOf[String]
}
val outer = new Outer
invokeMyMethod[outer.`_`.type] // _.myMethod