【问题标题】:Should I throw exceptions in an if-else block?我应该在 if-else 块中抛出异常吗?
【发布时间】:2018-12-27 07:08:00
【问题描述】:

代码如下:

public Response getABC(Request request) throws Exception {
    Response res = new Response();
    try {
        if (request.someProperty == 1) {
            // business logic
        } else {
           throw new Exception("xxxx");
        }
    } catch (Exception e) {
        res.setMessage(e.getMessage); // I think this is weird
    }
    return res;
}

这个程序运行良好。 我认为它应该重新设计,但如何?

【问题讨论】:

  • 使用异常进行流控制通常被接受为anti pattern。有趣的是,不需要throws Excetpion(原文如此)声明。
  • 我个人不喜欢在业务流程中使用例外。
  • //business logic 会发生什么?该代码能否引发您需要在此方法中捕获的异常?
  • @Amadan python 在这方面几乎是独一无二的。不仅仅是 Java 和 C++。
  • @Amadan 1. 请停止在 cmets 中创建二次讨论;作为一个经验丰富的用户,您应该知道这违反了 SO 的规则。 2. 你提出的问题已经被讨论过令人作呕,例如由 StuartLC 提供的 SE 链接,在 Josh Bloch 的 EJ 2nd 中,以及在许多其他关于软件模式、反模式和通用设计架构的 SE 相关书籍中。 3. 当大部分人完全不同意时 ...好吧,大部分人认为全球变暖是骗局或欺诈,认为欧洲是一个国家,也称“地球圆”一个阴谋 - ad populum 等等。

标签: java if-statement exception throw


【解决方案1】:

在 try 块中抛出异常并立即捕获它是没有意义的,除非 catch 块抛出不同的异常。

这样你的代码会更有意义:

public Response getABC(Request request) {
    Response res = new Response();
    if (request.someProperty == 1) {
        // business logic
    } else {
        res.setMessage("xxxx");
    }
    return res;
}

如果您的业务逻辑(在条件为true 时执行)可能会抛出异常,您只需要 try-catch 块。

如果你没有捕捉到异常(这意味着调用者必须处理它),你可以不用else 子句:

public Response getABC(Request request) throws Exception {
    if (request.someProperty != 1) {
        throw new Exception("xxxx");
    }

    Response res = new Response();
    // business logic
    return res;
}

【讨论】:

    【解决方案2】:

    如果你从方法中抛出异常,那么为什么还要去捕捉它呢?要么你返回一个带有“xxxx”消息的响应,要么抛出一个异常让这个方法的调用者来处理它。

    public Response getABC(Request requst) {
        Response res = new Response();
            if(request.someProperty == 1){
                //business logic
            else{
               res.setMessage("xxxx");
            }
        }
        return res;
    }
    

    或

    public Response getABC(Request requst) throw Excetpions {
        Response res = new Response();
            if(request.someProperty == 1){
                //business logic
            else{
               throw new Exception("xxxx");
            }
        return res;
    }
    
    
    public void someMethod(Request request) {
        try {
            Response r = getABC(request);
        } catch (Exception e) {
            //LOG exception or return response with error message
            Response response = new Response();
            response.setMessage("xxxx");
            retunr response;
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      故意抛出异常然后直接捕获它似乎不对, 它可以像这样重新设计,
      可以将throw new Exception("xxxx"); 更改为res.setMessage("xxxx");,
      然后可以保留捕获异常部分,以便捕获业务逻辑内部可能发生的异常。

      public Response getABC(Request requst) {
        Response res = new Response();
        try{
            if(request.someProperty == 1){
                //business logic
            else{
               res.setMessage("xxxx");
            }
        }catch(Exception e){
            res.setMessage(e.getMessage);
        }
        return res;
      }
      

      【讨论】:

        【解决方案4】:

        我认为您可能错过了尝试/捕获的要点。该代码使用异常系统将任何异常消息冒泡给调用者。这可能位于嵌套调用堆栈的深处,而不仅仅是您正在查看的那个“抛出”。

        换句话说,您的示例代码中的“抛出”声明正在利用这种机制向客户端传递消息,但它几乎肯定不是 try/catch 的主要预期用户。 (此外,它是一种草率、有点廉价的方式来传递此信息——它可能会导致混乱)

        这个返回值无论如何都不是一个好主意,因为异常通常没有消息并且可以重新包装......但总比没有好。异常消息并不是解决此问题的最佳工具,但像这样在高级别处理异常仍然是个好主意。

        我的意思是,如果您重构此代码,请务必查找可能在代码库中的任何位置(至少在消息处理期间调用的任何位置)中抛出的运行时异常——即使这样,您也应该保留捕获/返回消息作为一个包罗万象的信息,以防出现您没想到的运行时异常。您不必将错误“消息”作为您的响应消息返回——这可能是一些俏皮的“我们此时无法处理您的请求”,但请务必将堆栈跟踪转储到日志中.你现在正在扔掉它。

        【讨论】:

          【解决方案5】:

          首先,在重构工作方法时要更加小心——尤其是在执行手动重构时。也就是说,引入一个变量来保存message 可能是改变设计的一种方式:

          public Response getABC(Request requst) throw Excetpions {
              String message = "";
              try{
                  if(request.someProperty == 1){
                      //business logic
                  else{
                     message = "xxxx";
                  }
              }catch(Exception e){
                  message = e.getMessage();
              }
              Response res = new Response();
              res.setMessage(message);
              return res;
          }
          

          假设business logic成功时会自己返回。

          【讨论】:

            【解决方案6】:

            当你已经抛出 Checked Exception 时,为什么还要使用 try/catch 语句?

            Checked exception 通常用于某些语言,如 C++ 或 Java,但不用于 Kotlin 等新语言。我个人限制使用它。

            例如,我有一个这样的类:

            class ApiService{
                Response getSomething() throw Exception(); 
            } 
            

            感觉干净易读,但破坏了异常处理机制的实用性。实际上,getSomething() 不会抛出已检查异常,但仍需要表现得像它一样吗?当 ApiService 的上游有人知道如何处理像这样的 unpredictable 或 unpreventable 错误时,这很有效。如果你真的知道如何处理它,那就继续使用下面的例子,否则,Unchecked Exception就足够了。

            public Response getSomething(Request req) throws Exception{
                if (req.someProperty == 1) {
                    Response res = new Response();
                    // logic 
                } else {
                    thows Exception("Some messages go here")
                }
            }
            

            我会鼓励这样做:

            public Response getSomething(Request req){
            if (req.someProperty == 1) {
                    Response res = new Response();
                    // logic 
                    return res;
                } else {
                    return ErrorResponse("error message"); // or throw RuntimeException here if you want to
                }
            }
            

            如需更多见解,我之前提到的Kotlin 不支持Checked exception,原因有很多。

            以下是StringBuilder类实现的JDK的示例接口:

            Appendable append(CharSequence csq) throws IOException;
            

            这个签名说明了什么?它说每次我将字符串附加到某些东西(StringBuilder、某种日志、控制台等)时,我都必须抓住那些IOExceptions。为什么?因为它可能在执行IO(Writer 也实现了Appendable)......所以它导致这种代码到处都是:

            try {
                log.append(message)
            }
            catch (IOException e) {
                // Must be safe
            }
            

            这不好,请参阅Effective Java,第 3 版,第 77 条:不要忽略异常。

            看看这些链接:

            【讨论】:

              【解决方案7】:

              如果你想获取JVM在失败时返回的特定异常消息,那么你可以在catch块中使用带有getMessage()或printStackTrace()方法的try-catch。所以在这里你可以修改你的代码:

              public Response getABC(Request request) throws Exception {
                  Response res = new Response();
                  try {
                      if (request.someProperty == 1) {
                          // business logic
                      } 
                  } catch (Exception e) {
                      res.setMessage(e.getMessage); 
                  }
                  return res;
              }
              

              【讨论】:

                【解决方案8】:

                异常机制有三个目的:

                1. 立即禁用正常的程序流程并返回调用堆栈,直到找到合适的捕获块。
                2. 以异常类型、消息和可选的附加字段的形式提供上下文,catch 块代码可以使用这些字段来确定操作过程。
                3. 供程序员查看以进行取证分析的堆栈跟踪。 (这在过去非常昂贵)。

                对于机制来说,这是很多功能。为了让程序尽可能简单——对于未来的维护者——我们应该只在我们真的必须使用这个机制时才使用。

                在您的示例代码中,我希望任何throw 语句都是非常严重的事情,表明出现了问题,并且代码有望在某处处理这种紧急情况。在继续阅读程序的其余部分之前,我需要了解出了什么问题以及它有多严重。这只是一个字符串的花哨返回,我会挠头想知道“为什么这是必要的?”并且本可以更好地花费额外的努力。

                所以这段代码没有它可以做的那么好,但如果你也有时间做一个完整的测试,我只会改变它。更改程序流程可能会引入细微的错误,如果您需要修复任何问题,您需要将这些更改牢记在心。

                【讨论】:

                  猜你喜欢
                  • 2013-05-20
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-05-02
                  • 1970-01-01
                  • 1970-01-01
                  • 2019-01-27
                  • 1970-01-01
                  • 2021-10-02
                  相关资源
                  最近更新 更多