55

In Scala, what's the best way to dynamically instantiate an object and invoke a method using reflection?

I would like to do Scala-equivalent of the following Java code:

Class class = Class.forName("Foo");
Object foo = class.newInstance();
Method method = class.getMethod("hello", null);
method.invoke(foo, null);

In the above code, both the class name and the method name are passed in dynamically. The above Java mechanism could probably be used for Foo and hello(), but the Scala types don't match one-to-one with that of Java. For example, a class may be declared implicitly for a singleton object. Also Scala method allows all sorts of symbols to be its name. Both are resolved by name mangling. See Interop Between Java and Scala.

Another issue seems to be the matching of parameters by resolving overloads and autoboxing, described in Reflection from Scala - Heaven and Hell.

Eugene Yokota
  • 94,654
  • 45
  • 215
  • 319
  • 2
    Given that the experimental feature in my answer did not make 2.8.0, it would be better if another answer was marked as accepted. – Daniel C. Sobral Jun 14 '10 at 17:07
  • if i have class with parameters for a class like class MailServerice(emailIds : string) is it possible to invoke dynamically at runtime? – ashK Jun 14 '17 at 08:57

5 Answers5

73

There is an easier way to invoke method reflectively without resorting to calling Java reflection methods: use Structural Typing.

Just cast the object reference to a Structural Type which has the necessary method signature then call the method: no reflection necessary (of course, Scala is doing reflection underneath but we don't need to do it).

class Foo {
  def hello(name: String): String = "Hello there, %s".format(name)
}

object FooMain {

  def main(args: Array[String]) {
    val foo  = Class.forName("Foo").newInstance.asInstanceOf[{ def hello(name: String): String }]
    println(foo.hello("Walter")) // prints "Hello there, Walter"
  }
}
Walter Chang
  • 11,547
  • 2
  • 47
  • 36
  • 15
    Structural Type won't help me if I don't know the method name at the compile time. – Eugene Yokota Sep 24 '09 at 12:26
  • 7
    using a type alias to give the structural type a name often improves the readability of this trick. – Seth Tisue Apr 21 '10 at 17:39
  • 2
    This only answers part of the question. Is there a Scala centric way of performing Method method = class.getMethod("hello", null);? – James McMahon Jul 15 '12 at 19:41
  • 2
    In this example Foo takes no constructor parameters, but if there were, how would you go about passing them in on `newInstance`? – Luciano Feb 11 '14 at 15:43
  • What if we don't know the method names? Just that the class is actually an `object`. Why? Its a long story. In a nutshell, I need the user to input a object/class and then I want to find all methods in that object/class. – Jus12 Oct 08 '14 at 22:04
  • 10
    Executing this in scala repl I get `java.lang.NoClassDefFoundError: Foo (wrong name: Foo)` – marcin_koss Feb 12 '16 at 15:31
  • Say your type is Array[Int], it means that you must use `Class.forName("[I")`, how do you get `"[I"` from `"Array[Int]"` that you can get from a TypeTag? – Profiterole Feb 23 '20 at 17:47
  • What if we have another function bye() inside class foo and i want to call that too. how do i add that in instance of? – ss301 May 26 '20 at 13:36
12

The answers by VonC and Walter Chang are quite good, so I'll just complement with one Scala 2.8 Experimental feature. In fact, I won't even bother to dress it up, I'll just copy the scaladoc.

object Invocation
  extends AnyRef

A more convenient syntax for reflective invocation. Example usage:

class Obj { private def foo(x: Int, y: String): Long = x + y.length }

You can call it reflectively one of two ways:

import scala.reflect.Invocation._
(new Obj) o 'foo(5, "abc")                 // the 'o' method returns Any
val x: Long = (new Obj) oo 'foo(5, "abc")  // the 'oo' method casts to expected type.

If you call the oo method and do not give the type inferencer enough help, it will most likely infer Nothing, which will result in a ClassCastException.

Author Paul Phillips

Community
  • 1
  • 1
Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
7

The instanciation part could use the Manifest: see this SO answer

experimental feature in Scala called manifests which are a way to get around a Java constraint regarding type erasure

 class Test[T](implicit m : Manifest[T]) {
   val testVal = m.erasure.newInstance().asInstanceOf[T]
 }

With this version you still write

class Foo
val t = new Test[Foo]

However, if there's no no-arg constructor available you get a runtime exception instead of a static type error

scala> new Test[Set[String]] 
java.lang.InstantiationException: scala.collection.immutable.Set
at java.lang.Class.newInstance0(Class.java:340)

So the true type safe solution would be using a Factory.


Note: as stated in this thread, Manifest is here to stay, but is for now "only use is to give access to the erasure of the type as a Class instance."

The only thing manifests give you now is the erasure of the static type of a parameter at the call site (contrary to getClass which give you the erasure of the dynamic type).


You can then get a method through reflection:

classOf[ClassName].getMethod("main", classOf[Array[String]]) 

and invoke it

scala> class A {
     | def foo_=(foo: Boolean) = "bar"
     | }
defined class A

scala>val a = new A
a: A = A@1f854bd

scala>a.getClass.getMethod(decode("foo_="),
classOf[Boolean]).invoke(a, java.lang.Boolean.TRUE)
res15: java.lang.Object = bar 
Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
7

In case you need to invoke a method of a Scala 2.10 object (not class) and you have the names of the method and object as Strings, you can do it like this:

package com.example.mytest

import scala.reflect.runtime.universe

class MyTest

object MyTest {

  def target(i: Int) = println(i)

  def invoker(objectName: String, methodName: String, arg: Any) = {
    val runtimeMirror = universe.runtimeMirror(getClass.getClassLoader)
    val moduleSymbol = runtimeMirror.moduleSymbol(
      Class.forName(objectName))

    val targetMethod = moduleSymbol.typeSignature
      .members
      .filter(x => x.isMethod && x.name.toString == methodName)
      .head
      .asMethod

    runtimeMirror.reflect(runtimeMirror.reflectModule(moduleSymbol).instance)
      .reflectMethod(targetMethod)(arg)
  }

  def main(args: Array[String]): Unit = {
    invoker("com.example.mytest.MyTest$", "target", 5)
  }
}

This prints 5 to standard output. Further details in Scala Documentation.

nedim
  • 1,767
  • 1
  • 18
  • 20
5

Working up from @nedim's answer, here is a basis for a full answer, main difference being here below we instantiate naive classes. This code does not handle the case of multiple constructors, and is by no means a full answer.

import scala.reflect.runtime.universe

case class Case(foo: Int) {
  println("Case Case Instantiated")
}

class Class {
  println("Class Instantiated")
}

object Inst {

  def apply(className: String, arg: Any) = {
    val runtimeMirror: universe.Mirror = universe.runtimeMirror(getClass.getClassLoader)

    val classSymbol: universe.ClassSymbol = runtimeMirror.classSymbol(Class.forName(className))

    val classMirror: universe.ClassMirror = runtimeMirror.reflectClass(classSymbol)

    if (classSymbol.companion.toString() == "<none>") // TODO: use nicer method "hiding" in the api?
    {
      println(s"Info: $className has no companion object")
      val constructors = classSymbol.typeSignature.members.filter(_.isConstructor).toList
      if (constructors.length > 1) { 
        println(s"Info: $className has several constructors")
      } 
      else {
        val constructorMirror = classMirror.reflectConstructor(constructors.head.asMethod) // we can reuse it
        constructorMirror()
      }

    }
    else
    {
      val companionSymbol = classSymbol.companion
      println(s"Info: $className has companion object $companionSymbol")
      // TBD
    }

  }
}

object app extends App {
  val c = Inst("Class", "")
  val cc = Inst("Case", "")
}

Here is a build.sbt that would compile it:

lazy val reflection = (project in file("."))
  .settings(
    scalaVersion := "2.11.7",
    libraryDependencies ++= Seq(
      "org.scala-lang" % "scala-compiler" % scalaVersion.value % "provided",
      "org.scala-lang" % "scala-library" % scalaVersion.value % "provided"
    )
  )
matanster
  • 15,072
  • 19
  • 88
  • 167
  • 1
    related followup: http://stackoverflow.com/questions/34227984/perfectly-handling-scala-constructors-in-dynamic-instantiation – matanster Dec 11 '15 at 16:22