代码之家  ›  专栏  ›  技术社区  ›  Dang Nguyen

如何使用空检查连续生成Java8流映射

  •  8
  • Dang Nguyen  · 技术社区  · 7 年前

    我有这段代码

    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
        ...
    

    所以问题是我如何将第一部分重构为空安全部分,并可能缩短它?

    4 回复  |  直到 7 年前
        1
  •  11
  •   Ousmane D.    7 年前

    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) 如果返回的元素可能为null,则可以取消注释。
    2. .flatMap(Collection::stream) 它返回一个流,该流由将提供的映射函数应用于每个元素而生成的映射流的内容替换该流的每个元素的结果组成。
    3. 我又加了一个 .filter(对象::非空) 如果返回的元素在 flatMap 可能为空。
    4. 那时我们正处于可以申请的阶段 .filter(Coverage::isMainplan) 最后,通过检索满足条件的第一个对象 findFirst 如果没有,则通过提供默认值 orElse .

    我建议大家看看下面的博客,熟悉一下 平面图

        2
  •  3
  •   Ryuzaki L    7 年前

    添加条件 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")));
    
        3
  •  2
  •   GBlodgett    7 年前

    在代码的第一部分,您可以输入 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 在上面

        4
  •  1
  •   mwdev    7 年前

    您给出的示例和这里给出的答案打破了干净函数式编码的一些原则。

    stream().findFirst().orelsetrow(..).stream().otherActions。 这真的不是一个好的实践,而且容易出错。理想情况下,Java流API调用链应该与单个流一起工作。这样就更容易理解和推理您的代码。

    其次,您提到此位可能会命中Null指针exc:

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

    如果getCoverage()应该返回集合,则它永远不会返回null,而是始终返回空集合。

    将代码分解为不同的逻辑部分,并为其指定一些有意义的名称:

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

    它看起来比你的出发点好多了。我希望这有帮助。

        5
  •  0
  •   HPH    7 年前

    您可以尝试封装结果 Collection<Coverage> 变成 Optional<Collection<Coverage>> 这样你就可以在一个 零安全 方式

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