微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在任意Scala对象中调用私有方法

如何解决在任意Scala对象中调用私有方法

假设我有一个Scala对象:

object SomeObject {
  private def someMethod(msg: String): Unit = println(msg)
}

我可以使用以下代码调用someMethod

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

但是在上面,我已经将SomeObject硬编码到函数中。我真正想要的是传递一个任意的对象名称/类标记/任何内容,这样我就可以在ANY对象中通用地调用私有函数

这些尝试无效:

// 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")

我有点不了解,所以我尝试使用第三个选项可能甚至没有意义。

谢谢!

解决方法

如果对象名称为String,请尝试以下操作

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

或者如果您有对象ClassTag

def invokeObjectPrivateMethod[R](classTag: ClassTag[_],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],"it works") //it works

或者如果您有对象TypeTag

def invokeObjectPrivateMethod[R](typeTag: TypeTag[_],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],"it works") //it works

或者如果您有对象Class

def invokeObjectPrivateMethod[R](clazz: Class[_],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,"it works") //it works

或者如果您有对象Type

def invokeObjectPrivateMethod[R](typ: Type,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],"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?

我认为该方法没有重载版本,否则您应该使用typ.decl(...).alternatives.find(...).get.asMethodtyp.decl(...).alternatives.head.asMethod

使用Java反射

def invokeObjectPrivateMethod[R](packageName: String,objectName: 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","it works") //it works

使用Java方法句柄

def invokeObjectPrivateMethod[R](packageName: String,'$')}$$"
  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","it works") //it works

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。