Search code examples
scalareflectionscala-reflect

Scala reflection: How to construct object and run its methods given 'String-type' class name and method name?


Given the name of a class, method and parameters from a third party library, how to create an object and invoke its method using scala reflection?

For example, the class name is "org.apache.spark.mllib.clustering.LDA", the method is "setK" and the parameter is 3, how to use scala reflection to construct a LDA object and invoke method? The result should be equivalent to new LDA().setK(3).

In scala reflection document, I found the following code to construct a Person object

val m = ru.runtimeMirror(getClass.getClassLoader)
val classPerson = ru.typeOf[Person].typeSymbol.asClass
val ctor = ru.typeOf[Person].decl(ru.termNames.CONSTRUCTOR).asMethod
val ctorm = cm.reflectConstructor(ctor)
val p = ctorm("Mike")

but what if I have the "Person" instead of the Person class?


Solution

  • I have to agree with Luis in the comments that you should strongly consider other approaches, but in case this does turn out to be what you need:

    // for the example
    val className = "org.apache.spark.mllib.clustering.LDA"
    val methodName = "setK"
    val constructorParams = Array()
    val params = Array(3)
    
    // symbols
    val m = ru.runtimeMirror(getClass.getClassLoader)
    val classSymbol = m.staticClass(className)
    val ctor = classSymbol.primaryConstructor.asMethod
    // assumes the method exists and isn't overloaded
    val method = classSymbol.toType.decl(ru.TermName(methodName)).asMethod
    
    val cm = m.reflectClass(classSymbol)
    val ctorm = cm.reflectConstructor(ctor)
    
    val instance = ctorm(constructorParams: _*)
    val instancem = m.reflect(instance)
    val methodm = instancem.reflectMethod(method)
    
    methodm(params: _*)
    

    Or for this particular task you can just find that using Java reflection is simpler and Scala reflection provides no real advantage:

    val clazz = Class.forName(className)
    val ctor = clazz.getConstructors()(0)
    val instance = ctor.newInstance(constructorParams: _*)
    // again, assumes the method exists and isn't overloaded
    val method = clazz.getMethods().find(_.getName == methodName).get
    method.invoke(instance, params: _*)