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