0

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?

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
  • I would start by first making sure if you really need reflection. Do you really require the ability to invoke any method o any object? Or maybe just a few ones. Also, do those strings come from runtime values, le are there any way to have them at compile time? - Anyways, answering your question, you can ask the class loader to find a `Class` by its **Name**. – Luis Miguel Mejía Suárez Jul 18 '19 at 14:30
  • Cause the method name is input by users, and there are many types of classes need to be constructed and invoke their methods. – Sherry Pan Jul 18 '19 at 15:06
  • 1
    Well, it sounds like you user will be mainly programming then. What about just writing a DSL to simplify their lives and / or using some kind of notebook environment _(like Jupyter or Zeppelin)_, or even just reuse the RELP. Also, take a look to **ScalaMeta**. – Luis Miguel Mejía Suárez Jul 18 '19 at 15:28

2 Answers2

1

Try

import org.apache.spark.mllib.clustering.LDA
import scala.reflect.runtime.{universe => ru}

val m = ru.runtimeMirror(getClass.getClassLoader)
val classLDA = ru.typeOf[LDA].typeSymbol.asClass
val ctor = ru.typeOf[LDA].decl(ru.termNames.CONSTRUCTOR).asMethod
val cm = m.reflectClass(classLDA)
val ctorm = cm.reflectConstructor(ctor)
val p = ctorm.asInstanceOf[LDA]
p.setK(3)
Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66
1

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: _*)
Alexey Romanov
  • 167,066
  • 35
  • 309
  • 487