简体   繁体   中英

instantiating a Scala class using reflection Java's `newInstance`

For some special use-case I have a small utility to load Java classes from jars using a dynamic class loader DynamicClassLoader . This works fine for Java classes contained in jars. Loading Scala classes from a jar also works without problems. However, instantiating the loaded Scala class leads to the following exception. It looks like the Scala class has private default constructor? Note the compiled Scala class name ending with $

java.lang.IllegalAccessException: Class XXX can not access a member of class ScalaClassYYY$ with modifiers "private"

The snippet below illustrates the idea of what I'm trying to achieve and gives a bit more context. The exception happens at the annotated line:

// deploy and register the new code
byte[] jarBytes = (byte[]) ((Object) message.getAttachment("jar"));
String registerClassName = message.getAttachment("register");
logger.debug("the register is '" + registerClassName + "'");

DynamicClassLoader loader = new DynamicClassLoader(jarBytes);
Class<?> registerClass = loader.lookUp(registerClassName);
// ===> this is where the java.lang.IllegalAccessException happens 
IRegisterExecutor registerExecutor = (IRegisterExecutor) registerClass.newInstance();
registerExecutor.register();

Any ideas how to fix?

Obviously, you need to make the default constructor public (it won't work for Java classes without a public default constructor either). Eg

class ScalaClassYYY() {
  ...
}

or if you want primary constructor to take some arguments,

class ScalaClassYYY(arg1: Int) {
  def this() = this(0)
}

But from

Note the compiled Scala class name ending with $

it seems like you are actually trying to instantiate a Scala object :

object ScalaClassYYY { ... }

In this case, you shouldn't create a new instance and instead use the existing one :

(IRegisterExecutor) registerClass.getField("MODULE$").get(null);

EDIT:

I don't see in your answer how you add a default public constructor to a Scala class that does NOT require any parameters.

A class ( not an object ) that doesn't require any parameters has a default public constructor already (my first example).

Actually in Java all classes by default offer a public default constructor

No. Only those classes which have no constructors which take arguments.

remove the "(it won't work for Java classes without a public default constructor either)" because it is wrong

The documentation for Class.newInstance() says

IllegalAccessException - if the class or its nullary constructor is not accessible.

So I am pretty sure it's right. If it does work for Java classes without a public default constructor, this seems to be a major bug in the class loader you use. You can test it with a Java class which looks like this:

public class TestClass implements IRegisterExecutor {
    public TestClass(int dummy) {}

    // some implementation for IRegisterExecutor methods to get it to compile
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM