Hobbyist
Hobbyist

Reputation: 16182

NoSuchMethodException thrown when method exists

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

Answers (1)

rgettman
rgettman

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 Constructors 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

Related Questions