Reputation: 1323
I'm trying to call 2 different versions of the same dependency library (jars
) within a top level Main class
. So I created an interface with 2 implementation classes, both classes have a run method which makes use of common apis one will use somejar-1.0.0-SNAPSHOT.jar
and the other will use somejar-2.0.0-SNAPSHOT.jar
by explicitly calling ClassLoader.
public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
ClassLoader loader1 = new URLClassLoader( new URL[] { new File("/Users/haddad/.m2/repository/com/company/somejar-1.0.0-SNAPSHOT.jar").toURL() });
ClassLoader loader2 = new URLClassLoader( new URL[] { new File("/Users/haddad/.m2/repository/com/company/somejar-2.0.0-SNAPSHOT.jar").toURL() });
Class<?> c1 = loader1.loadClass("com.engine.na.EngineV1");
Class<?> c2 = loader2.loadClass("com.engine.na.EngineV2");
IEngine app1 = (IEngine) c1.newInstance();
IEngine app2 = (IEngine) c2.newInstance();
Integer s1 = app1.run();
Integer s2 = app2.run();
Assert.equals(s1,s2,"Outputs from somejar-1.0 and somejar-2.0 did not match, perhaps somejar-2.0 has regressed?");
}
Here are EngineV1 and V2 with Interface:
public Interface IEngine {
Integer run();
}
public class EngineV1 implements IEngine {
private File content;
private File en;
public EngineV1(args) {
this.content = new File("/some/path");
this.en = new File("/some/path");
}
public static void main(String[] args) {
new EngineV1(args).run();
}
public Integer run() {
// some logic...
somejar.evaluateSpeed();
}
}
public class EngineV2 implements IEngine {
private File content;
private File en;
public EngineV2(args) {
this.content = new File("/some/path");
this.en = new File("/some/path");
}
public static void main(String[] args) {
new EngineV2(args).run();
}
public Integer run() {
// some logic...
somejar.evaluateSpeed();
}
}
When I go to run the main class I get:
Exception in thread "main" java.lang.InstantiationException: com.engine.na.EngineV1
at java.lang.Class.newInstance(Class.java:427)
at com.engine.na.MainClass.main(MainClass.java:23)
Caused by: java.lang.NoSuchMethodException: com.engine.na.EngineV1.<init>()
at java.lang.Class.getConstructor0(Class.java:3082)
at java.lang.Class.newInstance(Class.java:412)
... 1 more
Why do I get this error? How to solve for this?
Upvotes: 1
Views: 18968
Reputation: 131324
When you declare a constructor in a class, the default constructor with no argument is not generated at compile time.
You have two ways :
adding explicitly a constructor with no argument.
invoking by reflection the constructor with an argument.
The first way is the easy trick and is a very generic way but it not necessarily the best way if it matters to value the state or one of part of the instance when it is created.
For this first case, it is self explanatory.
For the second case, the idea is you have to retrieve the constructor with argument from the class and specify the argument when you use it.
For example with this constructor :
public EngineV1(String value) {
...
}
You can invoke it in this way :
Class<EngineV1> c1 = (Class<EngineV1>)loader1.loadClass("com.engine.na.EngineV1");
Constructor<EngineV1> constructor = c1.getConstructor(String.class);
EngineV1 instance = ctor.newInstance("myString");
Upvotes: 5
Reputation: 2181
If there are no constructor defined in class then only compiler will create a no-arg constructor for your class.
But as in your case you are defining a parameterized constructor and in that case you need to provide a no-arg constructor for Reflection to create instances by calling it.
Or you can check available constructors for your class by calling getConstructor() method and then call newInstance() on that.
Follow below link for more information : http://docs.oracle.com/javase/tutorial/reflect/member/ctorInstance.html
Upvotes: 1