【问题标题】:How to handle Exception in Java 8 Stream?如何处理 Java 8 Stream 中的异常?
【发布时间】:2017-09-09 01:22:10
【问题描述】:

我有一个遍历列表并创建列表的方法。这样做时,我调用了一个方法(createResult)来给出一个结果,它也会抛出我包装为 ResultClassException 的 CustomException。但是我一直收到一个错误提示 Unhandled Exception。

我的代码:

 private  List<Result> getResultList(List<String> results) throws ResultClassException {
    List<Result> resultList = new ArrayList<>();
        results.forEach(
                (resultName) -> {
                    if (!resultRepository.contains(resultName)) {
                       try {
                           final Result result = createResult(resultName);
                           resultList.add(result);
                       } catch (CustomException e) {
                           throw new ResultClassException("Error",e);
                       }

                    } else {
                        resultList.add(resultRepository.get(resultName));
                        log.info("Result {} already exists.", resultName);
                    }
                }
        );
        return  Collections.unmodifiableList(resultList);
    }

谁能告诉我我做错了什么?

【问题讨论】:

  • ResultClassException 不是RuntimeException 的子类或resultRepository.get(resultName) 都会引发检查异常。您不能从 forEach 的 lambda 中抛出已检查的异常,因为此 lambda 不会对应于 Consumer 功能接口。
  • 我在您的代码中看不到任何流,只有 forEach。为什么不使用常规的 for 循环?

标签: java exception java-8 java-stream throws


【解决方案1】:

您的方法中可能有太多责任。您应该考虑将其拆分为一个仅映射的方法和另一个收集它们的方法。

private List<Result> getResultList(List<String> names) throws ResultClassException {
  try {
    return names.stream()
        .map(this::getOrCreateResult)
        .collect(collectingAndThen(toList(), Collections::unmodifiableList));
  } catch (RuntimeException e) {
    if (e.getCause() instanceof CustomException) {
      throw new ResultClassException("Error", e.getCause());
    }
    throw e;
    // Or use Guava's propagate
  }
}

private Result getOrCreateResult(String name) {
  if (!resultRepository.contains(name)) {
    try {
      return createResult(name);
    } catch (CustomException e) {
      throw new RuntimeException(e);
    }
  } else {
    log.info("Result {} already exists.", name);
    return resultRepository.get(name);
  }
}

【讨论】:

    【解决方案2】:

    我不建议使用 RuntimeException,因为这会使您陷入糟糕的编码习惯。尝试在 getResultList(...) 的调用方法中处理 ResultClassException。

    【讨论】:

      【解决方案3】:

      You can't handle a checked exception from inside of Streams

      一种解决方法是从createResult 中抛出一个RuntimeException,或者编写一个包装createResult 的方法,该方法将捕获并处理已检查的异常。

      【讨论】:

        【解决方案4】:

        使用 Java 8 中的 lambda 表达式,您可以表示内部类。所以异常会在你的匿名内部类中抛出。 尝试在添加throw new ResultClassException("Error",e)的位置添加@

        Thread.getAllStackTraces()
          .keySet()
          .stream()
          .map(Thread::getStackTrace)
          .map(Arrays::asList)
          .forEach(list -> System.out.println(list.stream()
                  .map(i -> i.toString())
                  .collect(Collectors.joining("\n\t"))));
        

        并查看调用它的线程。您将看到您的异常超出了您对 lambda 的预期范围。您将看到流正在创建许多线程,并且您的异常不是您想要的线程的一部分。 你可以这样包装你的方法: Java 8: How do I work with exception throwing methods in streams?

        【讨论】:

          【解决方案5】:

          如果部分处理的流是可以接受的(没有错),这是一种优雅处理的好方法:

                 List.of(1,2,3)
                 .stream().map( entry-> {
                      try {
                        return Optional.of(new Object());
                      } catch (Exception e) {
                        log.error("error [{}] while transforming entry [{}]", e.getMessage(), entry, e);
                        return Optional.empty();
                      }
                    }).filter(Optional::isPresent)
                 .map(Optional::get)
                 .collect(Collectors.toList());
          

          【讨论】:

            猜你喜欢
            • 2018-04-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-03-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多