scalareflectionscala-reflect

Invoke all methods on a Scala object using reflection


Given the following object

object functions {

  type MyOutput = String

  def f1: MyOutput = "res1"

  def f2: MyOutput = "res2"

  def f3(foo: String): Boolean = foo.equals("bar")

  def f4(in:String): MyOutput = in+"res4"
}

Is it possible to call all methods on this object, which the returned type is of MyOutput type?

Currenly I tried to use reflection for this, but had no luck with it.

import scala.reflect.runtime.universe._

val m = runtimeMirror(getClass.getClassLoader)
val instanceMirror = m.reflect(typeOf[functions.type])
val classSymbol = m.classSymbol(functions.getClass)
val methods = classSymbol.info.members.filter(_.isMethod)
methods.foreach({
  method =>
    if (method.info.resultType.toString.contains("MyOutput")) {
      println(method.name.toString)
      val methodm = instanceMirror.reflectMethod(method.asMethod)
      methodm()
    }
})

This is the error scala.ScalaReflectionException: expected a member of class UniqueSingleType, you provided method


Solution

  • val instanceMirror = m.reflect(functions)
    

    should be instead of

    val instanceMirror = m.reflect(typeOf[functions.type])
    

    This was the mistake because it's functions that is an instance, not typeOf[functions.type] (which is UniqueSingleType as written in the error).

    Also there can be some improvements.

    val classSymbol = m.classSymbol(functions.getClass)
    val methods = classSymbol.info.members.filter(_.isMethod)
    

    can be now replaced with

    val methods = typeOf[functions.type].decls.toList.filter(_.isMethod)
    

    since you know the object functions at compile time (so you don't need .classSymbol) and you're interested in the object methods rather than methods of its parents (so this is .decls rather than .members).

    It's better to replace

    method.info.resultType.toString.contains("MyOutput")
    

    with

    method.info.resultType =:= typeOf[String]
    

    (aka method.info.resultType =:= typeOf[functions.MyOutput]) in order not to rely on a work with raw strings.

    In methodm() you should provide arguments for the method:

    methodm(...here...)