如何使用null检查连续制作java 8流图

时间:2018-12-25 01:35:47

标签: java java-8 java-stream

我有这段代码

Coverage mainCoverage = illus.getLifes().stream()
    .filter(Life::isIsmain)
    .findFirst()
    .orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")))
    .getCoverages()  
    .stream() // <==may cause null here if list coverage is null
    .filter(Coverage::isMainplan)
    .findFirst()
    .orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")));

这完全可以正常工作,但我认为这有点混乱,无法涵盖所有​​的null pointer exception(请参阅注释)。

我尝试将此代码重构为

Coverage mainCoverage1 = illus.getLifes().stream()
    .filter(Life::isIsmain)
    .map(Life::getCoverages)
    .filter(Coverage::isMainplan) //<== cannot filter from list coverage to one main coverage
    ...

在将生命映射到覆盖范围之后,似乎不再是覆盖范围列表。 所以问题是我如何将第一部分重构为null安全并可能将其缩短?

5 个答案:

答案 0 :(得分:5)

Life::getCoverages返回一个集合,因此过滤器Coverage::isMainplan不起作用,相反,您应该flatMap .map(Life::getCoverages)之后返回的序列,然后对filter进行操作Coverage

Coverage mainCoverage = 
          illus.getLifes()
               .stream()
               .filter(Life::isIsmain)               
               .map(Life::getCoverages)
               //.filter(Objects::nonNull) uncomment if there can be null lists
               .flatMap(Collection::stream) // <--- collapse the nested sequences
               //.filter(Objects::nonNull) // uncomment if there can be null Coverage
               .filter(Coverage::isMainplan)
               .findFirst().orElse(...);

我已经在您的代码中添加了一些内容:

  1. 我在.filter(Objects::nonNull)之后添加了.map(Life::getCoverages),鉴于返回的元素可能为空,您可以取消注释。
  2. 我添加了.flatMap(Collection::stream),它返回一个流,该流包括将流中的每个元素替换为通过将提供的映射函数应用于每个元素而生成的映射流的内容而得到的结果。
  3. 我添加了另一个.filter(Objects::nonNull),鉴于在flatMap之后返回的元素可能为空,您可以取消注释。
  4. 然后我们处于可以应用.filter(Coverage::isMainplan)的阶段,最后,通过findFirst检索符合条件的第一个对象,如果没有对象,则通过orElse提供默认值。

我建议您看一下以下博客以熟悉flatMap方法:

答案 1 :(得分:3)

filter中添加条件,如果列表不为空并且i.isIsmain仅进行过滤,则可以使用public static boolean isNull(Object obj)public static boolean nonNull(Object obj)

Coverage mainCoverage = illus.getLifes().stream()
.filter(i->i.isIsmain && Objects.nonNull(i.getCoverages()))
.findFirst()
.orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")))
.getCoverages()  
.stream() // <==may cause null here if list coverage is null
.filter(Coverage::isMainplan)
.findFirst()
.orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")));

答案 2 :(得分:2)

在代码的第一部分中,您可以放入filter(e -> e != null)来确定List是否为空,否则不会抛出NPE:

Coverage mainCoverage = illus.getLifes().stream()
         .filter(Life::isIsmain)
         .findFirst()
         .orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")))
         .getCoverages()  
         .filter(e -> e != null) //<=== Filter out all null values
         .stream()
         .filter(Coverage::isMainplan)
         .findFirst()
         .orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002"))

第二个代码段的问题是,我假设Life::getCoverages返回一个Collection,而不是单个Coverage对象,因此您不能在其上调用Coverage::isMainplan < / p>

答案 3 :(得分:1)

您提供的示例和此处给出的答案破坏了一些简洁的功能样式编码的原理。

首先,您不应该在相同的代码链/管道中混合使用终端操作和再次创建流。就像你的例子 stream()。findFirst()。orElseThrow(..)。stream()。otherActions。 这确实不是一个好习惯,而且容易出错。理想情况下,一系列Java Stream API调用应与单个Stream一起使用。这样,您就可以更轻松地跟踪代码并对其进行推理。

其次,您提到此位可能会击中Null Pointer例外:

.getCoverages()  
.stream() // <==may cause null here if list coverage is null

如果getCoverages()应该返回collection,则它永远不应返回null,而应始终返回空collection。

将代码分成单独的逻辑部分,并为其赋予一些有意义的名称:

Coverage mainCoverage = illus.getLifes().stream()
    .filter(Life::isIsmain)
    .findFirst()
    .orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")));

Coverage mainplan = mainCoverage.getCoverages().stream()
    .filter(Coverage::isMainplan)
    .findFirst()
    .orElseThrow(() -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002")));

它看起来比您的起点要好得多。我希望这会有所帮助。

答案 4 :(得分:0)

您可以尝试将生成的Collection<Coverage>封装到Optional<Collection<Coverage>>中,以便可以以 null safe 的方式进行映射。

final Supplier<ServiceInvalidAgurmentGeneraliException> customExceptionThrower = () -> new ServiceInvalidAgurmentGeneraliException(env.getProperty("MSG_002"));

final Collection<Coverage> firstMainLifeCoverages = illus.getLifes().stream()
    .filter(Life::isIsmain)
    .findFirst()
    .orElseThrow(customExceptionThrower)
    .getCoverages();

Optional.ofNullable(firstMainLifeCoverages)
    .map(Collection::stream)
    .orElseThrow(customExceptionThrower)
    .filter(Coverage::isMainplan)
    .findFirst()
    .orElseThrow(customExceptionThrower);