Hello I am trying to create a new instance of a class via reflection:
In the example below the following applies:
- This is a method in a subclass of Arg1
- Data is an object which stores class references which are associated with each other
public <T extends Arg2, S extends Arg1> Foo
getFoo(@NotNull Data<T, S> data) {
Class<?>[] classes = new Class<?>[]{data.getArg1(), data.getArg2()};
T entity = getArg2(data);
try {
Class<? extends Foo> clazz = data.getFoo();
Constructor<? extends Foo> constructor = clazz.getDeclaredConstructor(classes);
constructor.setAccessible(true);
Object[] objects = new Object[]{this, entity};
return constructor.newInstance(objects);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException |
ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
This code works when the provided arguments are from the same class loader, yet the code fails when the arguments are from different class loaders. As such, multiple Class Loaders as arguments causes the failure of the method.
Is there any way I can get Java to accept my Arguments from multiple class loaders?
Edit: The reason I have multiple class loaders is due to the fact that I load external jar files which were compiled against this applications API into the application using a custom URLClassLoader.
As to the minimal reproducible example, I cannot at this time provide an example as this is private code which I do not own. The owner of the code would have to give me express permission to upload such an extensive chunk of the code (It's a bunch of classes that are essentially the cornerstone to the entire application). I can and will run any and all suggestions though and will forward this post to the owner for their approval.
Any help is much appreciated :)
Edit 2:
Here the code with debug messages:
public <T extends Arg2, S extends Arg1> Foo
getFoo(@NotNull Data<T, S> data) {
Class<?>[] classes = new Class<?>[]{data.getArg1(), data.getArg2()};
T entity = getArg2(data);
try {
Class<? extends Foo> clazz = data.getFoo();
System.out.println(clazz.getClassLoader());
Constructor<? extends Foo> constructor = clazz.getDeclaredConstructor(classes);
constructor.setAccessible(true);
System.out.println(this.getClass().getClassLoader());
System.out.println(entity.getClassLoader());
Object[] objects = new Object[]{this, entity};
return constructor.newInstance(objects);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException |
ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
Given that Arg1, Arg2 and Foo are Classes which are part of the base application, the output is as follows:
jdk.internal.loader.ClassLoaders$AppClassLoader@1d44bcfa
jdk.internal.loader.ClassLoaders$AppClassLoader@1d44bcfa
jdk.internal.loader.ClassLoaders$AppClassLoader@1d44bcfa
Given that Arg1 and Foo are Classes which are from the same external Jar File and Arg2 remains a class that is part of the base application:
Class Loader for a single jar file
Joined Class Loader for all jar files
jdk.internal.loader.ClassLoaders$AppClassLoader@1d44bcfa
java.lang.IllegalArgumentException: argument type mismatch
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[na:na]
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[na:na]
Note: These are the only two use cases
<T extends Arg2, S extends Arg1>but in Foo you have them in the correct order<S extends Arg1, T extends Arg2>? Also Foo is a parametrized class, so adding <?, ?> will increase the readability. However, your provided code literally does not help at all, because clearly the issue with initializing the objects in the first place, which you give less attention.