Skip to content

Instantly share code, notes, and snippets.

@jorgeortiz85
Created April 7, 2011 15:41
Show Gist options
  • Star 36 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save jorgeortiz85/908035 to your computer and use it in GitHub Desktop.
Save jorgeortiz85/908035 to your computer and use it in GitHub Desktop.
Calling private methods in Scala
// Usage:
// p(instance)('privateMethod)(arg1, arg2, arg3)
class PrivateMethodCaller(x: AnyRef, methodName: String) {
def apply(_args: Any*): Any = {
val args = _args.map(_.asInstanceOf[AnyRef])
def _parents: Stream[Class[_]] = Stream(x.getClass) #::: _parents.map(_.getSuperclass)
val parents = _parents.takeWhile(_ != null).toList
val methods = parents.flatMap(_.getDeclaredMethods)
val method = methods.find(_.getName == methodName).getOrElse(throw new IllegalArgumentException("Method " + methodName + " not found"))
method.setAccessible(true)
method.invoke(x, args : _*)
}
}
class PrivateMethodExposer(x: AnyRef) {
def apply(method: scala.Symbol): PrivateMethodCaller = new PrivateMethodCaller(x, method.name)
}
def p(x: AnyRef): PrivateMethodExposer = new PrivateMethodExposer(x)
@wcauchois
Copy link

thank you jorge! fyi, i have this gist bookmarked :P

@nafg
Copy link

nafg commented Dec 4, 2016

Any way to make this work for a static java method?

@nafg
Copy link

nafg commented Dec 4, 2016

This works:

    class PrivateMethodCaller[T: ClassTag](x: T, methodName: String) {
      def apply[R](_args: Any*): R = {
        val args = _args.map(_.asInstanceOf[AnyRef])

        def _parents: Stream[Class[_]] = Stream(scala.reflect.classTag[T].runtimeClass) #::: _parents.map(_.getSuperclass)

        val parents = _parents.takeWhile(_ != null).toList
        val methods = parents.flatMap(_.getDeclaredMethods)
        val method = methods.find(_.getName == methodName).getOrElse(throw new IllegalArgumentException("Method " + methodName + " not found"))
        method.setAccessible(true)
        method.invoke(x, args: _*).asInstanceOf[R]
      }
    }
    class p[T: ClassTag](x: T) {
      def call(method: scala.Symbol) = new PrivateMethodCaller[T](x, method.name)
    }
    def p[T: ClassTag](x: T): p[T] = new p[T](x)
    def ps[T <: AnyRef : ClassTag]: p[T] = new p[T](null.asInstanceOf[T])

@leobenkel
Copy link

Do you have an example on how to use that ?

@Kylin1027
Copy link

Kylin1027 commented Aug 8, 2019

@leobenkel demo like this?

class PrivateMethodCallerDemo {
  private def saySomething(name: String, content: String): String = {
   s"${name} says ${content} to you"
  }
}

object DriverMain {
  def p(x: AnyRef): PrivateMethodExposer = new PrivateMethodExposer(x)
  def main(args: Array[String]): Unit = {
    val demoInstance = new PrivateMethodCallerDemo

    val name = "Aimer"
    val content = "Hello"
    val expectedRet = "Aimer says Hello to you"

    // Usage: p(instance)('privateMethod)(arg1, arg2, arg3)
    val retValue = p(demoInstance)('saySomething)(name, content)
    assert(retValue == expectedRet)
  }
}

@zbstof
Copy link

zbstof commented Dec 31, 2019

some method names are of the form: org$apache$spark$sql$execution$command$ShowCreateTableCommand$$escapeSingleQuotedString

p(instance)('escapeSingleQuotedString)("aaa")
java.lang.IllegalArgumentException: Method escapeSingleQuotedString not found

Workaround like so:

val method = methods.find(_.getName.split("\\$\\$") contains methodName).getOrElse(
  throw new IllegalArgumentException("Method " + methodName + s" not found, available: ${methods.map(_.getName).mkString(", ")}"))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment