How to make java 8 Stream map continuously with null check

问题: I have this piece of code Coverage mainCoverage = illus.getLifes().stream() .filter(Life::isIsmain) .findFirst() .orElseThrow(() -> new ServiceInvalidAgurm...

问题:

I have this piece of code

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

which is totally work fine but I think It's a little bit messy and not cover all the null pointer exception possible (see the comment).

I try to refactor this code into

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

Seem after I map the life to coverage it is not a list of coverage anymore. So the question is how I can refactor the first section into null safe and maybe shorten it?


回答1:

Life::getCoverages returns a collection hence the filter Coverage::isMainplan will not work, instead you should flatMap the sequences returned after .map(Life::getCoverages) then apply the filter operation on the 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(...);

I've added a few things to your code:

  1. I've added .filter(Objects::nonNull) after .map(Life::getCoverages) which you can uncomment given the elements returned could potentially be null.
  2. I've added .flatMap(Collection::stream) which returns a stream consisting of the results of replacing each element of this stream with the contents of a mapped stream produced by applying the provided mapping function to each element.
  3. I've added another .filter(Objects::nonNull) which you can uncomment given the elements returned after flatMap could potentially be null.
  4. We're then at a stage in which we can apply .filter(Coverage::isMainplan) and finally, retrieve the first object meeting the criteria via findFirst and if none then provide a default value via orElse.

I'd suggest having a look at the following blogs to get familiar with the flatMap method:


回答2:

Add condition in filter if list is not null and i.isIsmain then only filter, you can use public static boolean isNull(Object obj) or 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:

In the first part of your code you can put in a filter(e -> e != null) to unsure if the List is null it wont throw an 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"))

The problem with your second code snippet is that I'm assuming Life::getCoverages returns a Collection, not individual Coverage objects, so you cannot call Coverage::isMainplan on it


回答4:

You can try to encapsulate the resulting Collection<Coverage> into an Optional<Collection<Coverage>> so that you can map over in a null safe manner.

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);
  • 发表于 2018-12-27 11:16
  • 阅读 ( 249 )
  • 分类:网络文章

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除