【问题标题】:Break out of for loop from a method called within loop从循环内调用的方法中跳出 for 循环
【发布时间】:2017-04-12 05:46:00
【问题描述】:

我有一个 Listjobs 需要 x 步(比如 5 步)。每个步骤都必须成功才能继续,如果任何步骤失败,则必须提出票证并且必须没收当前job的执行并继续下一个job

这是我目前拥有的(它就像一个魅力)。

for(Job aJob: jobs){
    // Step 1
    try{
        Obj objRet1 = method1();
        // use objRet1;
    }
    catch(Exception e){
        raiseTicket(errMsg1,e);
        break;
    }

    // Step 2
    try{
    Obj objRet2 =   method2();
    // use objRet2;
    }
    catch(Exception e){
        raiseTicket(errMsg2,e);
        break;
    }

    // Step 3
    try{
    Obj objRet3 = method3();
    // use objRet3;
    }
    catch(Exception e){
        raiseTicket(errMsg3,e);
        break;
    }

    ...

    // Step 5
}

IMO,这不是很优雅且易于阅读。我想把它浓缩成下面的样子。

for(Job aJob: jobs){
    step1();
    step2();
    ..
    step5();
}

step1(){
    try{
        ...
    }
    catch(Exception e){
        raiseTicket(errMsg1,e);
        break;
    }
}

step2(){
}
...

有人可以说明如何改进这个程序吗?请注意,返回一个值或将其存储在方法参数中也可能不起作用,因为这里试图实现的是避免break 执行所需的样板代码,并将它们整齐地打包在一个可读的方法中。

【问题讨论】:

    标签: java loops for-loop iteration break


    【解决方案1】:

    如果发生错误,您可以在每个步骤中抛出一个已检查的异常,然后在 for 循环中捕获已检查的异常。像这样的:

    class StepException extends Exception {...}
    
    for (Job aJob: jobs) {
      try {
         step1();
         step2();
         ...
         step5();
      } catch (StepException se) {
         // do something, e.g. print error to console
         // the for loop will keep going when this occurs
      }
    }
    
    step1() throws StepException {
       try {
          ...
       } catch (Exception e) {
          raiseTicket(...);
          throw new StepException(e);
       }
    }
    
    // similar to step2(), ..., step5()
    

    【讨论】:

    • 我喜欢这个解决方案。但由于 Seelenvirtuose 首先提供了相同的答案,因此我接受了他的答案。
    【解决方案2】:

    一种可能的解决方案是:

    for (Job job : jobs) {
        AtomicInteger step = new AtomicInteger();
        try {
            Obj result = executeJob(step, () -> method1());
            // do something with result
            result = executeJob(step, () -> method2());
            // do something with result
            result = executeJob(step, () -> method3());
            // do something with result
        } catch (Exception e) {
            raiseTicket(errorMessages.get(step.get()), e);
        }
    }
    
    private Obj executeJob(AtomicInteger step, Supplier<Obj> supplier) {
        step.incrementAndGet();
        return supplier.get();
    }
    

    errorMessagesMap&lt;Integer, String&gt;

    【讨论】:

      【解决方案3】:

      首先,break 不会中断当前的迭代,而是整个循环。您需要使用continue 来实现这一点。但是由于抛出的异常会跳过剩下的步骤,在这种情况下你不需要任何额外的语句。

      为什么不使用这样的东西?

      // create list with errorMessages in the following order: errMesFor1, errMesFor2,..., errMesFor5
      List<String> messageList = new ArrayList<>();
      messageList.add(errMesFor1);
      messageList.add(errMesFor2);
      messageList.add(errMesFor3);
      messageList.add(errMesFor4);
      messageList.add(errMesFor5);
      
      for(Job aJob: jobs){
          // create list that holds successfully created objects 
          List<Obj> objList = new ArrayList<>();
          try {
                  Obj objRet1 = method1();
                  // use objRet1;
                  list.add(objRet1);
                  Obj objRet2 = method2();
                  // use objRet2;
                  list.add(objRet2);
                  Obj objRet3 = method3();
                  // use objRet3;
                  list.add(objRet3);
                  Obj objRet4 = method4();
                  // use objRet4;
                  list.add(objRet4);
                  Obj objRet5 = method5();
                  // use objRet5;
                  list.add(objRet5);
              }
              catch(Exception e){
                  // retrieve message for the first element that was not successfully added to the object list, hence the one that threw error
                  raiseTicket(messageList.get(objList.size()),e);
              }
      

      这样,您只需编写一次try-catch 块。

      list 中组织消息也很不错(或者您甚至可以在列表上编写自定义wrapper class)。

      唯一需要的额外内容是对象列表,以便您可以轻松找到引发异常的对象。

      【讨论】:

      • 感谢您的指出。我想说continue,但我输入错误break
      【解决方案4】:

      让步骤抛出一个特定的异常,让循环代码处理它!

      有以下异常类

      class StepExecutionException extends RuntimeException {
          StepExecutionException(String message, Throwable cause) {
              super(message, cause);
          }
      
          void raiseTicket() {
              // Code that raises the ticket ...
              // The message can be retrieved with getMessage().
              // The cause can be retrieved with getCause().
          }
      }
      

      您可以使用该代码轻松实现这些步骤:

      void step1() {
          try {
              ...
          } catch(Exception e) {
              throw new StepExecutionException("Step 1", e);
          }
      }
      

      现在您的循环代码如下所示:

      for (Job aJob : jobs) {
          try {
              step1();
              step2();
              ...
          } catch (StepExecutionException see) {
              see.raiseTicket();
          }
      }
      

      【讨论】:

      • 我喜欢这个解决方案。我看到 Dat Nguyen 也提出了相同的答案。所以看起来这是一个常见问题,并且有一种设计模式可以解决它。
      • 请问,为什么使用 RuntimeException 而不是检查异常?
      • StepExecutionException 是一个检查异常 :) 有关更多信息,请参阅此 SO 线程:stackoverflow.com/questions/6115896/…
      • 您可以选中或取消选中 - 无论您喜欢什么。 Joshua Bloch 的书“Effective Java”现在有些陈旧了。是否应该使用已检查的异常存在一个长期的讨论。我个人更喜欢只使用未经检查的异常。主要原因是不要弄乱方法签名。
      【解决方案5】:

      根据 JAVA 基础知识,没有人可以使用 break outside loop 或 switch

      由于方法代表堆栈内存条目,因此如果循环在另一个堆栈条目中而方法在另一个堆栈条目中,则您不能从堆栈内存中中断循环。

      【讨论】:

        猜你喜欢
        • 2018-09-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多