mercredi 7 octobre 2020

Invoke private method in arbitrary scala object

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!





Aucun commentaire:

Enregistrer un commentaire