简体   繁体   中英

How do I set a parameterized type of a variable to the type of class the variable is in?

I am making a Discord bot with several 'command features'

public class MyBotCommandFeature extends MyBotFeature {
    @NotNull public final Set<MyBotCommand> commands;

    public MyBotCommandFeature(@NotNull MyBotCommand @NotNull ... commands) {
        if (commands.length == 0) {
            this.commands = new HashSet<>();
        } else {
            this.commands = stream(commands).collect(Collectors.toSet());
        }
    }

    @Nullable
    @CheckForNull
    @CheckReturnValue
    public Set<MyBotCommand> getExtraCommands(@NotNull Guild guild) {
        return null;
    }
}

Each feature has several commands (MyBotCommand). MyBotCommand has two abstract methods for handling text commands (runTextCommand) and slash commands (runSlashCommand).

Because some commands will want to refer to the MyBotFeature they're a part of, MyBotCommandFeature is currently one of the parameters of these abstract methods. However, to use any feature-specific features, commands currently have to cast MyBotCommandFeature to, say, ExampleCommandFeature .

public abstract class MyBotCommand {
    public abstract void runTextCommand(@NotNull MessageReceivedEvent event, @NotNull MyBotCommandFeature feature);
    public abstract void runSlashCommand(@NotNull SlashCommandInteractionEvent event, @NotNull MyBotCommandFeature feature);
}
public final class ExampleFeature extends MyBotCommandFeature {
    public ExampleFeature() {
        super(new MyBotCommand() {
            @Override
            public void runTextCommand(@NotNull MessageReceivedEvent event, @NotNull MyBotCommandFeature feature) {
                final ExampleFeature self = ((ExampleFeature) feature);
                // Use self to respond to the text command
            }

            @Override
            public void runSlashCommand(@NotNull SlashCommandInteractionEvent event, @NotNull MyBotCommandFeature feature) {
                final ExampleFeature self = ((ExampleFeature) feature);
                // Use self to respond to the slash command
            }
        });
    }

I was hoping I'd be able to make MyBotCommand be parameterized with the generic type <F extends MyBotCommandFeature> . Then instead of the abstract functions taking MyBotCommandFeature as parameters, they'd take F .

public abstract class MyBotCommand<F extends MyBotCommandFeature> {
    public abstract void runTextCommand(@NotNull MessageReceivedEvent event, @NotNull F feature);
    public abstract void runSlashCommand(@NotNull SlashCommandInteractionEvent event, @NotNull F feature);
}

However, I'm not sure I would then declare the type of the set of commands (defined in MyBotCommandFeature , not individually in each feature) to say that the generic type F is the same as the class the set of commands is being stored in. I would want to be able to do something like this.

public class MyBotCommandFeature extends MyBotFeature {
    @NotNull public final Set<MyBotCommand<this>> commands;

    public MyBotCommandFeature(@NotNull MyBotCommand<this> @NotNull ... commands) {
        if (commands.length == 0) {
            this.commands = new HashSet<>();
        } else {
            this.commands = stream(commands).collect(Collectors.toSet());
        }
    }

    @Nullable
    @CheckForNull
    @CheckReturnValue
    public Set<MyBotCommand<this>> getExtraCommands(@NotNull Guild guild) {
        return null;
    }
}
public final class ExampleFeature extends MyBotCommandFeature {
    public ExampleFeature() {
        super(new MyBotCommand<ExampleFeature>() {
            @Override
            public void runTextCommand(@NotNull MessageReceivedEvent event, @NotNull ExampleFeature feature) {
                // Use feature to respond to the text command
            }

            @Override
            public void runSlashCommand(@NotNull SlashCommandInteractionEvent event, @NotNull ExampleFeature feature) {
                // Use feature to respond to the text command
            }
        });
    }

I have tried using a wildcard '?' instead of 'this' but then I'm stuck on what to pass to the abstract methods after matching a JDA event to a MyBotCommand. For example, I have this for slash commands, but I am unable to pass MyBotCommandFeature to MyBotCommand::runSlashCommand even though the MyBotCommand instance is made in the MyBotCommandFeature I am passing.

    @Override
    public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) {
        for (MyBotCommandFeature feature : features) {
            for (MyBotCommand<?> command : feature.commands) {
                if (command.name.equals(event.getName())) {
                    command.runSlashCommand(event,feature);
                    return;
                }
            }
        }
    }

Is there some way I can do something like this? If not, is there an alternative way to avoid having to cast MyBotCommandFeature to an individual feature in most of my MyBotCommand implementations?

How about something like this? There is a type variable on MyBotCommandFeature indicating the type argument of the MyBotCommand that its commands are. Then the subclass ExampleFeature can extend MyBotCommandFeature with itself as the type argument. Of course, someone could write a class FeatureA extends MyBotCommandFeature<FeatureB> , but everything is still type-safe and should compile without any warnings or casts, at least with what you've shown so far.

public abstract class MyBotCommand<F> {
    public abstract void runTextCommand(@NotNull MessageReceivedEvent event, @NotNull F feature);
    public abstract void runSlashCommand(@NotNull SlashCommandInteractionEvent event, @NotNull F feature);
}

public class MyBotCommandFeature<F> extends MyBotFeature {
    @NotNull public final Set<MyBotCommand<F>> commands;

    public MyBotCommandFeature(@NotNull MyBotCommand<F> @NotNull ... commands) {
        if (commands.length == 0) {
            this.commands = new HashSet<>();
        } else {
            this.commands = stream(commands).collect(Collectors.toSet());
        }
    }

    @Nullable
    @CheckForNull
    @CheckReturnValue
    public Set<MyBotCommand<F>> getExtraCommands(@NotNull Guild guild) {
        return null;
    }
}

public final class ExampleFeature extends MyBotCommandFeature<ExampleFeature> {
    public ExampleFeature() {
        super(new MyBotCommand<ExampleFeature>() {
            // ...
        });
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM