You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java如何将变量的参数化类型设置为其所属类的类型

解决方案

Java 语法本身不支持伪代码中MyBotCommand<this>形式的自类型声明,但可以通过两种成熟方案实现你要的效果,完全消除业务代码中重复的强转逻辑。


方案1:递归泛型边界实现自类型绑定(完全匹配你的设计预期)

这是Java生态实现自类型的标准惯用法,通过递归泛型参数让父类自动持有子类的类型信息,只需要在基类做一次安全的类型转换,所有业务代码无需再写强转。

第一步:改造基类MyBotCommandFeature

给基类加递归泛型参数,同时封装内部的类型安全调用逻辑,解决通配符场景下的编译报错:

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.CheckForNull;
import org.jetbrains.annotations.CheckReturnValue;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toSet;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;

// F 代表子类自身的类型,递归边界约束F必须是当前基类的子类
public abstract class MyBotCommandFeature<F extends MyBotCommandFeature<F>> extends MyBotFeature {
    @NotNull public final Set<MyBotCommand<F>> commands;

    @SafeVarargs
    public MyBotCommandFeature(@NotNull MyBotCommand<F> @NotNull ... commands) {
        // 可选:加一层校验,防止子类写错泛型参数
        Type superClass = this.getClass().getGenericSuperclass();
        if (superClass instanceof ParameterizedType pt) {
            Type typeArg = pt.getActualTypeArguments()[0];
            if (typeArg instanceof Class<?> c && !c.isInstance(this)) {
                throw new IllegalArgumentException("泛型参数必须传入当前Feature自身类型");
            }
        }

        if (commands.length == 0) {
            this.commands = new HashSet<>();
        } else {
            this.commands = Stream.of(commands).collect(toSet());
        }
    }

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

    // 封装斜杠命令执行逻辑,仅在此处做一次安全的强转
    @SuppressWarnings("unchecked")
    public final void execSlashCommand(@NotNull MyBotCommand<?> command, @NotNull SlashCommandInteractionEvent event) {
        // 强转100%安全:只有匹配当前F类型的命令才会被存入commands集合
        ((MyBotCommand<F>) command).runSlashCommand(event, (F) this);
    }

    // 封装文本命令执行逻辑,同理
    @SuppressWarnings("unchecked")
    public final void execTextCommand(@NotNull MyBotCommand<?> command, @NotNull MessageReceivedEvent event) {
        ((MyBotCommand<F>) command).runTextCommand(event, (F) this);
    }
}

第二步:子类实现

子类继承时把自身类型作为泛型参数传入即可,命令实现中直接拿到对应子类类型的feature参数,无需强转:

public final class ExampleFeature extends MyBotCommandFeature<ExampleFeature> {
    public ExampleFeature() {
        super(new MyBotCommand<ExampleFeature>() {
            @Override
            public void runTextCommand(@NotNull MessageReceivedEvent event, @NotNull ExampleFeature feature) {
                // 直接调用子类专属方法,无强转
                feature.customTextLogic();
            }

            @Override
            public void runSlashCommand(@NotNull SlashCommandInteractionEvent event, @NotNull ExampleFeature feature) {
                feature.customSlashLogic();
            }
        });
    }

    // 子类专属方法
    public void customTextLogic() {
        // 业务逻辑
    }

    public void customSlashLogic() {
        // 业务逻辑
    }
}

第三步:改造事件监听逻辑

直接调用基类封装好的执行方法即可,无需处理类型转换:

@Override
public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) {
    for (MyBotCommandFeature<?> feature : features) {
        // 遍历内置命令
        for (MyBotCommand<?> command : feature.commands) {
            if (command.name.equals(event.getName())) {
                feature.execSlashCommand(command, event);
                return;
            }
        }
        // 遍历额外命令
        Set<MyBotCommand<?>> extraCommands = feature.getExtraCommands(event.getGuild());
        if (extraCommands != null) {
            for (MyBotCommand<?> command : extraCommands) {
                if (command.name.equals(event.getName())) {
                    feature.execSlashCommand(command, event);
                    return;
                }
            }
        }
    }
}

方案2:构造期绑定Feature实例(实现更简单)

如果觉得递归泛型太绕,可以直接改MyBotCommand的设计,在命令实例初始化的时候就绑定所属的Feature实例,完全不需要在执行方法上传参:

public abstract class MyBotCommand<F extends MyBotCommandFeature<?>> {
    // 初始化时就绑定所属Feature,子类直接访问
    protected final F feature;

    protected MyBotCommand(F feature) {
        this.feature = feature;
    }

    // 执行方法不再需要传feature参数
    public abstract void runTextCommand(@NotNull MessageReceivedEvent event);
    public abstract void runSlashCommand(@NotNull SlashCommandInteractionEvent event);
}

子类实现时直接把this传给命令构造器:

public final class ExampleFeature extends MyBotCommandFeature {
    public ExampleFeature() {
        super(new MyBotCommand<ExampleFeature>(this) {
            @Override
            public void runTextCommand(@NotNull MessageReceivedEvent event) {
                // 直接访问成员变量feature,类型就是ExampleFeature,无强转
                feature.customTextLogic();
            }

            @Override
            public void runSlashCommand(@NotNull SlashCommandInteractionEvent event) {
                feature.customSlashLogic();
            }
        });
    }
    // 子类专属方法省略
}

事件监听逻辑直接调用无参的run方法即可,比泛型方案更简洁,唯一要求是构造命令时必须传入当前Feature的this实例。


内容的提问来源于stack exchange,提问作者hopperelec

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 01:42:23