Let's say I have a Scala object:
object SomeObject {
private def someMethod(msg: String): Unit = println(msg)
}
I can invoke someMethod
with the following code:
import scala.reflect.runtime.{universe => ru}
import scala.reflect.ClassTag
def invokeObjectPrivateMethod[R](methodName: String, args: AnyRef*): R = {
val rm = ru.runtimeMirror(getClass.getClassLoader)
val instanceMirror = rm.reflect(SomeObject)
val methodSymbol = ru.typeOf[SomeObject.type].decl(ru.TermName(methodName)).asMethod
val method = instanceMirror.reflectMethod(methodSymbol)
method(args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod("someMethod", "it works")
But above I've hardcoded SomeObject
into the function. What I'd really like is to pass an arbitrary object name/classtag/whatever so I can invoke a private function generically in ANY object.
These attempts have NOT worked:
// With explicit ClassTag parameter
def invokeObjectPrivateMethod2[R](classTag: ClassTag[_], methodName: String, args: AnyRef*): R = {
val rm = ru.runtimeMirror(getClass.getClassLoader)
val instanceMirror = rm.reflect(classTag)
val methodSymbol = ru.typeOf[classTag.type].decl(ru.TermName(methodName)).asMethod
val method = instanceMirror.reflectMethod(methodSymbol)
method(args: _*).asInstanceOf[R]
}
// This fails at runtime with: `scala.ScalaReflectionException: <none> is not a method`
invokeObjectPrivateMethod2[Unit](ClassTag(SomeObject.getClass), "someMethod", "it doesn't work")
// With implicit ClassTag/TypeTag
def invokeObjectPrivateMethod3[T: ClassTag, S: ru.TypeTag, R](methodName: String, args: AnyRef*)(implicit ct: ClassTag[T]): R = {
val rm = ru.runtimeMirror(getClass.getClassLoader)
val instanceMirror = rm.reflect(ct)
val methodSymbol = ru.typeOf[S].decl(ru.TermName(methodName)).asMethod
val method = instanceMirror.reflectMethod(methodSymbol)
method(args: _*).asInstanceOf[R]
}
// This fails at compile time: `not found: type SomeObject`
invokeObjectPrivateMethod3[SomeObject, SomeObject, Unit]("someMethod", "it also doesn't work")
I'm somewhat out of my depth, so what I'm trying with the 3rd option might not even make sense.
Thanks!
Try the following if you have String
of object name
def invokeObjectPrivateMethod[R](objectName: String, methodName: String, args: AnyRef*): R = {
val rm = scala.reflect.runtime.currentMirror
val moduleSymbol = rm.staticModule(objectName)
val classSymbol = moduleSymbol.moduleClass.asClass
val moduleMirror = rm.reflectModule(moduleSymbol)
val objectInstance = moduleMirror.instance
val objectType = classSymbol.toType
val methodSymbol = objectType.decl(ru.TermName(methodName)).asMethod
val instanceMirror = rm.reflect(objectInstance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror(args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod("com.example.App.SomeObject", "someMethod", "it works") //it works
or if you have object ClassTag
def invokeObjectPrivateMethod[R](classTag: ClassTag[_], methodName: String, args: AnyRef*): R = {
val rm = scala.reflect.runtime.currentMirror
val clazz = classTag.runtimeClass
val classSymbol = rm.classSymbol(clazz)
val moduleSymbol = classSymbol.owner.info.decl(classSymbol.name.toTermName).asModule //see (*)
val moduleMirror = rm.reflectModule(moduleSymbol)
val objectInstance = moduleMirror.instance
val objectType = classSymbol.toType
val methodSymbol = objectType.decl(ru.TermName(methodName)).asMethod
val instanceMirror = rm.reflect(objectInstance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror(args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod(classTag[SomeObject.type], "someMethod", "it works") //it works
or if you have object TypeTag
def invokeObjectPrivateMethod[R](typeTag: TypeTag[_], methodName: String, args: AnyRef*): R = {
val rm = scala.reflect.runtime.currentMirror
val objectType = typeTag.tpe
val clazz = rm.runtimeClass(objectType) // see (**)
val classSymbol = rm.classSymbol(clazz)
val moduleSymbol = classSymbol.owner.info.decl(classSymbol.name.toTermName).asModule // see (*)
val moduleMirror = rm.reflectModule(moduleSymbol)
val objectInstance = moduleMirror.instance
val methodSymbol = objectType.decl(ru.TermName(methodName)).asMethod
val instanceMirror = rm.reflect(objectInstance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror(args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod(typeTag[SomeObject.type], "someMethod", "it works") //it works
or if you have object Class
def invokeObjectPrivateMethod[R](clazz: Class[_], methodName: String, args: AnyRef*): R = {
val rm = scala.reflect.runtime.currentMirror
val classSymbol = rm.classSymbol(clazz)
val moduleSymbol = classSymbol.owner.info.decl(classSymbol.name.toTermName).asModule //see (*)
val moduleMirror = rm.reflectModule(moduleSymbol)
val objectInstance = moduleMirror.instance
val objectType = classSymbol.toType
val methodSymbol = objectType.decl(ru.TermName(methodName)).asMethod
val instanceMirror = rm.reflect(objectInstance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror(args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod(SomeObject.getClass, "someMethod", "it works") //it works
or if you have object Type
def invokeObjectPrivateMethod[R](typ: Type, methodName: String, args: AnyRef*): R = {
val rm = scala.reflect.runtime.currentMirror
val classSymbol = typ.typeSymbol.asClass
val moduleSymbol = classSymbol.owner.info.decl(classSymbol.name.toTermName).asModule //see (*)
val moduleMirror = rm.reflectModule(moduleSymbol)
val objectInstance = moduleMirror.instance
val methodSymbol = typ.decl(ru.TermName(methodName)).asMethod
val instanceMirror = rm.reflect(objectInstance)
val methodMirror = instanceMirror.reflectMethod(methodSymbol)
methodMirror(args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod(typeOf[SomeObject.type], "someMethod", "it works") //it works
(*) Get the module symbol, given I have the module class, scala macro
(**) How to get ClassTag form TypeTag, or both at same time?
I assume that the method didn't have overloaded versions, otherwise you should work with typ.decl(...).alternatives.find(...).get.asMethod
or typ.decl(...).alternatives.head.asMethod
.
Using Java reflection
def invokeObjectPrivateMethod[R](packageName: String, objectName: String, methodName: String, args: AnyRef*): R = {
val javaClassName = s"$packageName.${objectName.replace('.', '$')}$$"
val clazz = Class.forName(javaClassName)
val method = clazz.getDeclaredMethods.find(_.getName == methodName).get
method.setAccessible(true)
val field = clazz.getField("MODULE$")
val instance = field.get(null) // null because field is static
method.invoke(instance, args: _*).asInstanceOf[R]
}
invokeObjectPrivateMethod("com.example", "App.SomeObject", "someMethod", "it works") //it works
Using Java method handles
def invokeObjectPrivateMethod[R](packageName: String, objectName: String, methodName: String, args: AnyRef*): R = {
val javaClassName = s"$packageName.${objectName.replace('.', '$')}$$"
val clazz = Class.forName(javaClassName)
val lookup = MethodHandles.lookup
val field = clazz.getField("MODULE$")
val fieldMethodHandle = lookup.unreflectGetter(field)
val instance = fieldMethodHandle.invokeWithArguments()
val method = clazz.getDeclaredMethods.find(_.getName == methodName).get
method.setAccessible(true)
val methodHandle = lookup.unreflect(method)
methodHandle.invokeWithArguments(instance +: args : _*).asInstanceOf[R]
}
invokeObjectPrivateMethod("com.example", "App.SomeObject", "someMethod", "it works") //it works
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.