Search code examples
scalaclassloader

Dynamically classloading Scala types


I have the following Scala class hierarchy:

abstract class BaseModule(val appConf : AppConfig) {
  // ...
}

class SimpleModule(appConf : AppConfig) extends BaseModule(appConf) {
  // ...
}

class FairlyComplexModule(appConf : AppConfig) extends BaseModule(appConf) {
  // ...
}

// dozens of other BaseModule subclasses...

At runtime, my app will accept a String input argument for the fully-qualified class name of a BaseModule subclass to instantiate, but the code won't know which concrete subclass it will be. So I have:

val moduleFQCN = loadFromInputArgs()  // ex: "com.example.myapp.SimpleModule"
val moduleClass = Class.forName(moduleFQCN)
println(s"Found ${moduleFQCN} on the runtime classpath.")
val module = Class.forName(moduleFQCN).getConstructor(classOf[AppConfig]).newInstance(appConf).asInstanceOf[BaseModule]

So this way, the input specifies which BaseModule subclass to look for on the classpath, and then subsequently, to instantiate. The first three lines above execute just fine, and I see the println fire. However the last line above throws an exception:

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    <rest of stacktrace omitted for brevity>

So clearly I'm doing something wrong when trying to create an instance of the SimpleModule subclass, just can't figure out what it is. Any ideas?


Solution

  • You're probably failing because you call newInstance() without any arguments, but no default constructor is found therefore the instantiation fails.

    try this:

    Class.forName(moduleFQCN).getConstructor(classOf[AppConfig])
    .newInstance(appConf).asInstanceOf[BaseModule]
    

    Where appConf is an instance of AppConfig and is the parameter to instantiate BaseModule with.