自定义未经检查的异常不会抛出和调用方法 return

Custom unchecked exception doesn't make throwing and caller methods return

我遇到了无法在全新项目中重现的问题;它发生在针对 Spigot 1.8.8-R0.1 构建的 Bukkit 插件上。

我正在开发命令框架。 有两种类型的命令:那些只能由玩家 运行...

private Map<Command, PlayerCommand> playerCommands = new HashMap<>();
public interface PlayerCommand {
    void run(Player sender, String[] args);
}

...以及控制台也可能 运行 的那些。

private Map<Command, GlobalCommand> globalCommands = new HashMap<>();
public interface GlobalCommand {
    void run(CommandSender sender, String[] args);
}

为了注册命令,我实现了3种方法:

public void registerCommand(String name, PlayerCommand executor) {
    Command command = registerCommand(name);
    // 2
    playerCommands.put(command, executor);
}
public void registerCommand(String name, GlobalCommand executor) {
    Command command = registerCommand(name);
    // 2'
    globalCommands.put(command, executor);
}
private Command registerCommand(String name) {
    PluginCommand command = plugin.getCommand(name);
    if (command.getExecutor() == this) {
        throw new CommandAlreadyRegisteredException("The '" + name + "' command has been already registered");
    }
    // 1
    command.setExecutor(this);
    return command;
}

您可能已经注意到,客户端不能多次注册相同的命令。

为了实现这一点,我知道我可以要求地图已经包含命令,但那将是两个操作,同时使用 == 运算符检查执行程序(它必须完全这个实例,不仅仅是 equals()) 只是一个。

所以,对于错误的情况,我决定抛出一个未经检查的异常:

public class CommandAlreadyRegisteredException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    protected CommandAlreadyRegisteredException(String message) {
        super(message);
    }
}

重点是:我原以为抛出异常会 return 在抛出它的方法中,以及在上层调用者方法中。也就是说,在这种情况下,第 1、2 和 2' 点永远不应该执行。 但他们确实被处决了! 这怎么可能?你能重现吗?为什么会这样?

我知道我可以在私有方法中 returned null,但是这样客户端就不会知道命令已经注册了。

您需要从 Exception 而不是 RuntimeException 扩展您的自定义异常 class,因为那样会被检查,之后您可以使用代码 throw new YourException()在任何需要抛出异常的地方。

第一次尝试注册命令:

  • 您从插件中获取命令。
  • 该命令没有执行者,因此检查失败。
  • 然后你设置执行器。

第二次尝试:

  • 您从插件中获取命令。 和你第一次得到的命令是同一个对象
  • 它仍然将this视为其执行者。检查通过,抛出异常。

如果你只想阻止它被同一个执行者注册两次,那么绝对不需要例外:

private Command registerCommand(String name) {
    PluginCommand command = plugin.getCommand(name);
    if (command.getExecutor() != this) {
        command.setExecutor(this);
    }
    return command;
}

没有错误。

为了调试,我在抛出异常后添加了一些System.out.println()。 我只打印了一次这些断点。 没关系,因为那是允许的第一个注册,但不知为什么,我认为它们是由第二个引起的...我不知道为什么。