如何在Java 8 Stream中处理异常?

4

我有一个方法,它遍历一个List并创建一个List。在这个过程中,我调用了一个方法(createResult)来获取一个结果,同时也会抛出CustomException异常,我将其包装成ResultClassException。但是我一直收到未处理的异常错误。

我的代码:

 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);
    }

有人能告诉我我做错了什么吗?


4
要么ResultClassException不是RuntimeException的子类,要么resultRepository.get(resultName)会抛出一个已检查的异常。在forEach的lambda内部,您不能抛出已检查的异常,因为此lambda将无法对应于Consumer函数接口。 - fps
2
我在你的代码中没有看到任何流,只有一个 forEach。为什么不使用常规的 for 循环呢? - shmosel
这个回答解决了你的问题吗?Java 8:Lambda-Streams,使用异常过滤方法 - Vadzim
5个回答

7
你的方法可能有太多职责了。你应该考虑将其拆分为一个仅进行映射的方法和另一个收集它们的方法。
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);
  }
}

1

如果部分处理的流是可以接受的(不算错误),优雅地处理它的好方法:

       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());

1

我不建议使用RuntimeException,因为这样会导致糟糕的编程实践。尝试在调用getResultList(...)的方法中处理ResultClassException。


编译器不允许在forEach块中抛出异常,但这正是它需要被抛出的地方。 - undefined

0

0
使用 Java 8 中的 Lambda 表达式表示内部类。因此,异常将在匿名内部类中抛出。 尝试将throw new ResultClassException("Error",e)添加到您添加的位置。
(Note: Chinese text is in simplified Chinese)
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: 如何在流中处理抛出异常的方法?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接