Reputation: 16182
I'm trying to create an instance of a class, based on the class and the parameters. The method used to create this class looks like so:
@SuppressWarnings("unchecked")
public Plugin instantiatePlugin(Class clazz, Object... params) {
try {
Class plugin = pluginClasses.get(clazz);
if (params.length > 0) {
Class[] parameterTypes = new Class[params.length];
for (int i = 0; i < parameterTypes.length; i++) {
parameterTypes[i] = params[i].getClass();
}
return (Plugin)plugin.getDeclaredConstructor(parameterTypes).newInstance(params);
} else {
return (Plugin)plugin.newInstance();
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
The error that I'm getting is the following:
java.lang.NoSuchMethodException: plugin.movement.WalkRunPlugin.(com.rr.server.entity.mob.player.Player)
When trying to call the declared constructor. Here's what I'm passing to the method: instantiatePlugin(MovementPlugin.class, this);
Where this
is the instance of the Player
class, which extends Mob
.
Here's the only
constructor in my WalkRunPlugin
example:
public WalkRunPlugin(Mob mob) {
this.mob = mob;
}
Considering Mob is abstract, and Player extends mob, Player is a Mob, So I see no reason why this shouldn't work.
This seems to be because the reflection method is expecting the type of Player and not the type of Mob, however a Player is not the only Mob. So I need to find a way to accept subclasses of a super-class in this method. Tips?
Compilable example:
public class Example {
public static void main(String[] args) {
new Example();
}
private Example() {
try {
Class clazz = Plugin.class;
clazz.getDeclaredConstructor(Player.class).newInstance(new Player());
} catch (Exception e) {
e.printStackTrace();
}
}
public static abstract class Mob {
}
public static class Player extends Mob {
}
public static class Plugin {
Mob m;
Plugin(Mob m) {
this.m = m;
}
}
}
Upvotes: 0
Views: 1419
Reputation: 178263
The getConstructor
method will not find the constructor because it will only find an exact match of parameter types.
The constructor to reflect is the public constructor of the class represented by this Class object whose formal parameter types match those specified by parameterTypes.
It won't work like the compiler, which will consider a possible widening reference conversion of Player
to Mob
to match the constructor.
You will need to find the proper Constructor
. Here is the best way I can think of:
Loop through all Constructor
s returned by getConstructors
. Determine if all parameter types either match the types of arguments you have, or if they are superclasses of all the types of arguments you have. You can access the parameter types of a Constructor
with the getParameterTypes()
method, which will access the Class
objects associated with the parameter types. Then you can use isInstance(params[i])
(or isAssignableFrom(params[i].getClass())
). If all parameters match, then use that Constructor
.
This can obviously get messy and very complicated, because you are doing a job the compiler normally does -- determining the correct constructor to call -- but for this exact purpose, one of the above solutions should be sufficient. You should be able to find the Constructor
that takes a Mob
, even if you are given a Player
instance, in the above way.
Upvotes: 1