Java 8 optional add return result only if optional.isPresent

22,270

Solution 1

I see where you're going with this - when a projectile (may be a better class name than Bullet) goes through BallisticGelPuddy, it either becomes stuck or it doesn't. If it gets stuck, it accumulates in BallisticGelPuddy.

Let's rewrite the code if we were using null checks instead:

for(Gun gun: guns) {
    final Bullet bullet = gun.shoot();
    if(bullet != null) {
        bullets.add(bullet);
    }
}

Pretty straightforward, right? If it exists we want to add it in.

Let's add the optional style back in:

for(Gun gun: guns) {
    gun.shoot().ifPresent(bullets::add);
}

Effectively these two things accomplish the same thing, although the Optional approach is terser.

In this scenario, there's really no difference between the two approaches since you're always going to be checking for existence. Optional is meant to guard against mistakes when handling null and allows you to express a more fluid call chain, but consider the practicality of using Optional in this scenario. It doesn't seem entirely necessary for this case.

Solution 2

I think you want:

gun.shoot().ifPresent(bullets::add);

Or you can dispense with the (coded) loop too:

guns.stream()
  .map(Gun::shoot)
  .filter(Optional::isPresent)
  .map(Optional::get)
  .forEach(bullets::add);

But it's uglier.

Solution 3

With the stream API, you can do:

    List<Bullet> bullets = Arrays.stream(guns)
            .map(Gun::shoot)
            .flatMap(this::streamopt) // make Stream from Optional!
            .collect(Collectors.toList());

Unfortunately, in Java 8, there is no method which converts Optionals to Stream, so you need to write it yourself. See Using Java 8's Optional with Stream::flatMap

Share:
22,270
Vale
Author by

Vale

Updated on June 16, 2020

Comments

  • Vale
    Vale about 4 years

    I have a piece of code where an interface has an Optional return method and some of the classes that implement it to return something, other don't.

    In an effort to embrace this brilliant "null killer", here is what I have tried:

    public interface Gun {
        public Optional<Bullet> shoot();
    }
    
    public class Pistol implements Gun{
        @Override
        public Optional<Bullet> shoot(){
            return Optional.of(this.magazine.remove(0)); 
        }//never mind the check of magazine content
    }
    
    public class Bow implements Gun{
        @Override
        public Optional<Bullet> shoot(){
            quill--;
            return Optional.empty();
        }
    }
    
    public class BallisticGelPuddy{
        private Gun[] guns = new Gun[]{new Pistol(),new Bow()};
        private List<Bullet> bullets = new ArrayList<>();
        public void collectBullets(){
            //here is the problem
            for(Gun gun : guns)
                gun.shoot.ifPresent(bullets.add( <the return I got with the method>)
    }}
    

    I apologise for how silly this example is.
    How can I check the return I just got and add it only if present, using optional?

    P.S. is there any real usefulness to Optional which is if(X != null) couldn't do?