Java 通过抛出异常进行流程控制

标签 java exception control-flow

关于首选的流量控制方法,我有一个问题困扰了我一段时间。

我经常遇到这样一种情况,我必须根据方法的返回值来决定要做什么,该返回值是 null 还是 not null

所以我有两个我知道的选项,如何处理它:

检查是否为空:

public class BasedOnNullFlowControl {

    public String process(String login) {
        String redirectUri = getRedirectUri(login);
        if (redirectUri != null) {
            return redirectUri;
        } else {
            return "Your login is taken";
        }
    }

    private String getRedirectUri(String login) {
        Optional<Login> loginFromDb = checkLoginExists(login);
        if (loginFromDb.isPresent()) {
            return null;
        } else {
            return "some-redirect-url";
        }
    }

    private Optional<Login> checkLoginExists(String login) {
        return Optional.empty(); //assume this value comes from some API
    }

    private class Login {}
}

或者异常中断流程:

public class ExceptionFlowControl {

    public String process(String login) {
        return getRedirectUri(login);
    }

    private String getRedirectUri(String login) {
        Optional<Login> loginFromDb = checkLoginExists(login);

        loginFromDb.ifPresent(l -> {
            throw new LoginExistsException();
        });
        return "some-redirect-url";
    }

    private Optional<Login> checkLoginExists(String login) {
        return Optional.empty();
    }

    private class LoginExistsException extends RuntimeException {
    }

    private class Login {}
}

我知道,异常应该只在特殊情况下使用,但我的情况并不特殊,第二个解决方案对我来说看起来更好,因为我可以添加一些异常处理程序(比如在 Spring 中)并将其转换为一些不错的 Http最后的状态码。 Controller 方法 process 没有被几十个空检查污染。

请各位聪明人指点应该使用哪一种解决方案?或者也许还有第三个?

最佳答案

异常方式可以减少代码各部分之间的依赖,所以第一种方式很差。有了它,您需要传递 nullnull == login taken 一直加载具有特殊含义的值 ( checkLoginExists() )至 process .

但这不是getRedirectUri() 的工作抛出异常,应该在checkLoginExists()中完成.我对拥有 getRedirectUri() 持怀疑态度负责检查登录是否存在。更不用说使用 nullOptional只给你一个成功/失败的二元概念。如果登录名存在但被锁定怎么办,因此您必须禁止登录并创建一个具有相同名称的新用户。您可能希望重定向到其他页面以指示登录已锁定。

不过,这是基于意见的,在很大程度上取决于具体情况,并且没有您可以遵循的明确规则。

编辑(现在所有这些喧嚣都结束了):一个广泛接受的观点是正常流量控制不应该有异常(exception)。然而,正常 的定义并不那么容易定义。你不会写这样的代码

int[] array = ...
int i = 0;
try {
    while(true) {
       array[i]++;
       i++;
    }
} catch(ArrayIndexOutOfBoundsException e) {
    // Loop finished
}

但是,如果您没有使用像上面那样简单得可笑的示例,它就会进入灰色区域。

另请注意,异常与应用程序错误不同。尝试通过验证所有内容来解决异常是不可能的(或者至少是不明智的)。如果我们考虑新用户尝试注册用户名的情况,并且我们希望防止重复的用户名。您可以检查重复项并显示错误消息,但仍然有可能两个并发请求尝试注册相同的用户名。此时其中一个会出现异常,因为数据库将不允许重复(如果您的系统无论如何都是合理的)。

验证很重要,但关于异常没有任何真正异常。你需要优雅地处理它们,所以如果你最终不得不处理异常,为什么还要费心去验证呢。如果我们以我在评论中写的东西为例,你需要防止重复和诅咒词作为用户名,你可能会想出这样的东西(假设这是在 getRedirectUri() 中:

if(checkForCurseWords(username))
    return "login not allowed";

try {  // We can also omit the try-catch and let the exception bubble upwards
    createLogin(username);
} catch(DuplicateException e) {
    return "login exists";
} catch(Exception e) {
    return "problem with login";
}
return "main page";

关于Java 通过抛出异常进行流程控制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46703427/

相关文章:

java - 为什么我没有收到 NullPointerException

c++ - 在 C++ 中编译类的代码时,控制流的顺序是什么?

c++ - 使用10为底的数字转换程序的控制流

javascript - Promise 对象内的 setTimeout

JavaMail,如果接收者邮件错误,不会抛出异常

java - Android ListView - 当数组是本地文件时如何刷新ListView?

c++ - 未捕获 WM_PAINT 中的访问冲突

Delphi如何在错误时退出到顶级例程

java - 如何在 Java Swing 中创建圆角 BevelBorder?

java - 字典数据结构+快速复杂度方法