Getting a constructor with "parameters that extends the original" with reflection (Kinda confusing)

Discussion in 'Spigot Plugin Development' started by DDevil_, Jul 2, 2016.

  1. Let's say I have a framework with these classes
    Code (Java):
    public class CustomPlugin<P extends CustomPlugin, M extends MessageManager, C extends ConfigurationManager> extends JavaPlugin {
    }
    Code (Java):

    public abstract class CustomCommand<P extends CustomPlugin> extends Command {
        public CustomCommand(P plugin, CommandInfo info) {
        }

    }
     
    Let's say I have a plugin that uses that framework and has these classes:
    Code (Java):

    public class MineMe extends CustomPlugin<MineMe, MineMeMessageManager, MineMeConfigurationManager> {
    }
     
    Code (Java):

    public class MineMeCommand extends CustomCommand<MineMe> {
        public MineMeCommand(MineMe plugin, CommandInfo info) {
        }
    }
    Now, what I need to do is to get MineMeCommand's constructor with reflection, I have tried the following:
    Code (Java):
    MineMeCommand.class.getConstructor(
            new Class[]{
                    CustomPlugin.class,
                    CommandInfo.class
            }
    );
     
    This doesn't work, since the constructor uses MineMe and CommandInfo as parameters, not CustomPlugin, but the following code works
    Code (Java):
    MineMeCommand.class.getConstructor(
            new Class[]{
                    MineMe.class,
                    CommandInfo.class
            }
    );
     
    Note that I need to do this inside the framework, so I can't just use MineMeCommand.class or the seconds attempt I've done, basically what I need to do is something among the lines of
    Code (Java):
    MineMeCommand.class.getConstructor(
            new Class[]{
                    Class<T extends CustomPlugin>,
                    CommandInfo.class
            }
    );
     
    Of course the code above doesn't compile, but do you guys have any idea of how to achieve what I need? Any help is appreciated :D
     
  2. Loop through the constructors yourself and check if the first argument is assignable to CustomPlugin. Please note that I did not convert the parameter types you are checking against (this should be done), as well as supporting Superclass lookups (which also should be done).
    Code (Java):
    Constructor con;
    for (Constructor constructor : MineMeCommand.class.getDeclaredConstructors()) {
        Class<?>[] params = constructor.getParameterTypes();
        // Should convert the parameter types to primitives,
        // incase you plan on using short, int, byte, etc.
        if (params.length == 2) {
            // Change it from == 2 to whatever check you want (like > 0 or > 1),
            // so long as it ensures there will be nothing for this next part to error with.
            if (CustomPlugin.class.isAssignableFrom(params[0])) {
                // This parameter is a class that extends CustomPlugin
                constructor.setAccessible(true);
                // Make sure it's accessible (incase it's not public)
                con = constructor;
                break;
            }
        }
    }

    if (con != null) {
        // The constructor was found, gogogo
    }
    PS: You use too many parameterized types. You should be storing the abstract class version. This way other classes don't have to know what plugin/api/service/manager is extending whatever plugin/class/api.