1

Currently, I have the following code that works:


    //in class Bar
        
        public Foo getFooIfItIsPresent(String param) {
                Optional<Foo> result = loadOptionalFoo(param);
                if (result.isPresent()) {
                    return result.get();
                } else {
                    return null;
                }
        
    // main driver code
    
    Foo foo = Bar.getFooIfItIsPresent(param);
                        
    if (foo != null) {
       // Currently just print, but might want to do stuff like pass foo to another object, etc.
       System.out.println(foo.getSomething() + foo.getSomethingElse());
    }

This is kind of ugly, because I am checking for null explicitly; in addition, I have a convoluted function getFooIfItIsPresent that exists for the sole purpose of the isPresent() dance. I would want to do something like:


    Bar.loadOptionalFoo(param).ifPresent((foo) -> {
    // Print, or whatever I wanna do!
    System.out.println(foo.getSomething() + foo.getSomethingElse());
    });

I know this doesn't compile. There's been a very similar question, and I've tried a bunch of stuff, but the compiler complains. Eg:


    Bar.loadOptionalFoo(week).map(foo -> {
    // Print, or whatever I wanna do!
                                System.out.println(foo.getSomething() + foo.getSomethingElse());
                            }).filter(Objects::nonNull);

Yes, the code above is nonsensical, but I can't seem to wrap my head around getting an elegant solution for this, so a bit of help would be much appreciated!

cloudy_eclispse
  • 313
  • 4
  • 16
  • Would the Null Object Pattern be an approach worth pursing? https://en.wikipedia.org/wiki/Null_object_pattern – Brent Worden Jul 02 '20 at 19:29
  • 1
    What is the compilation error here? You don't seem to have a parsing error, so I would assume that your lambda is incompatible with Consumer interface? – NoDataFound Jul 02 '20 at 19:37
  • Yeah, basically. Here's a snippet of the error: `reason: cannot infer type-variable(s) U (argument mismatch; bad return type in lambda expression missing return value)` – cloudy_eclispse Jul 02 '20 at 19:40
  • @BrentWorden Actually that might be suitable here. The reason my Foo is `Optional` is because it might just be empty (rather than absent) while loading it, which is what I understood from reading the wikipedia link? In that case, are you suggesting that the check for null is acceptable? – cloudy_eclispse Jul 02 '20 at 19:44
  • True: map() takes a Function and your lambda does not return, but I don't see the point in your case: your are not trying to handle the "if not there" part. – NoDataFound Jul 02 '20 at 19:44

4 Answers4

4

tl;dr

The code you showed saying “I know this doesn't compile” actually should compile. That code is your solution.

Using similar code, see this method:

public Optional < DayOfWeek > getFavoriteDayOfWeek ( )
{
    return Optional.ofNullable( DayOfWeek.WEDNESDAY );  // Silly implementation for demonstration.
}

…called like this:

this.getFavoriteDayOfWeek().ifPresent(
        ( DayOfWeek dow ) -> { … dow.get() …  }
);

…run live successfully at IdeOne.com.

Keep in mind that an Optional is its own object, wrapping some other object as its payload. So be careful about your type declarations: Optional<DayOfWeek> someVar versus DayOfWeek someVar.

table showing how a declaration of Optional creates a payload object wrapped in another object, an Optional object

Return the Optional

If a null is an acceptable value in your domain, then return the Optional. Returning a null unwrapped from within an Optional defeats the purpose of an Optional.

The purpose of an Optional is to signal to the calling method that a null is indeed a valid possibility. The type system of Java is being used to remind the calling programmer to code for the possibility of a null. An Optional object is like a big safety-orange traffic sign saying: “Beware: possible NULL ahead”.

Returning just null unwrapped from within an Optional adds no value, and makes for brittle code.

The code shown in the Question is jumping through extra hoops needlessly, just introducing an extra level of indirection with no benefit. The calling method should indeed receive an Optional if a payload of null is a valid result.

So this:

//in class Bar
    
    public Foo getFooIfItIsPresent(String param) {
            Optional<Foo> result = loadOptionalFoo(param);
            if (result.isPresent()) {
                return result.get();
            } else {
                return null;
            }
    
// main driver code

Foo foo = Bar.getFooIfItIsPresent(param);
                    
if (foo != null) {
   // Currently just print, but might want to do stuff like pass foo to another object, etc.
   System.out.println(foo.getSomething() + foo.getSomethingElse());
}

…should be:

//in class Bar
    
// ➥ Delete this method `getFooIfItIsPresent`. Adds no value.
//     public Foo getFooIfItIsPresent(String param) 

…and…

// main driver code

Optional<Foo> result = someBar.loadOptionalFoo( param );
                    
if ( result.isPresent() ) {
   // Currently just print, but might want to do stuff like pass foo to another object, etc.
   Foo foo = result.get() ;  // Calling `get` is safe because we checked for null in the `if … isPresent` line above.
   System.out.println( foo.getSomething() + foo.getSomethingElse() ) ;
}

Notice how we call Optional::get only after checking for null by calling Optional::ifPresent.

The Optional class offers various methods if you want to address the if else condition where the Optional is empty with no Foo object present. See methods such as orElse, orElseGet, and orElseThrow.

Your problematic code should indeed compile

You said this code does not compile:

Bar.loadOptionalFoo(param).ifPresent((foo) -> {
// Print, or whatever I wanna do!
System.out.println(foo.getSomething() + foo.getSomethingElse());
});

Actually, that code should compile. It is perfectly reasonable to do, another variation of what I showed as a solution above.

Here is a similar example. I make the method getFavoriteDayOfWeek which returns an Optional< DayOfWeek > using the DayOfWeek enum class built into Java. If a favorite day is registered, the method returns an Optional holding a DayOfWeek object. If no favorite has yet been determined, the method returns an empty Optional. Here is our dummy version of that method.

public Optional < DayOfWeek > getFavoriteDayOfWeek ( )
{
    return Optional.ofNullable( DayOfWeek.WEDNESDAY );
}

Calling that method using code similar to your code:

this.getFavoriteDayOfWeek().ifPresent(
        ( DayOfWeek dayOfWeek ) -> {
            // Print, or whatever I wanna do!
            System.out.println( dayOfWeek.getDisplayName( TextStyle.FULL , Locale.CANADA_FRENCH ) );
        }
);

When run:

mercredi

INFO - Done running demo.

You can change the WEDNESDAY object with null to experiment.

public Optional < DayOfWeek > getFavoriteDayOfWeek ( )
{
    return Optional.ofNullable( null  );
}

When run, we see that the System.out.println( dayOfWeek.getDisplayName(… code is never called because the conditional test for our Optional<DayOfWeek> actually containing a DayOfWeek object (ifPresent) was not met. We see output only from our second System.out.println, for "Done running".

INFO - Done running demo.

See this code run live at IdeOne.com.

Making an Optional

The body of your Question seems to be asking about extracting a value from an Optional. I showed that above.

The title of your Question seems to be about wrapping a value in an Optional.

  • If you know you want to return a null, return Optional.empty().
  • If returning something that may or may not be null, return Optional.ofNullable( x ).
  • If returning something that should definitely not be null in that situation, return Optional.of( x ). That method throws NullPointerException if the value is null. Use this method when the presence of a null means something went very wrong with your app.

A tip, by the way, after seeing your use of if (foo != null) {:

If you do need to test for a null, I suggest instead of using x != null or x == null that you instead use the Objects class methods, Objects.nonNull​( Object obj ) and Objects.isNull( Object obj ), respectively.

Even better, in a situation where the presence of a null in intolerable, meaning a major unexpected failure in your code, call Objects.requireNonNull​( T obj ). This method throws NullPointerException if receiving a null. Even better, this method returns the object passed. This makes it perfect for assigning passed arguments to other variables.

public void doSomething( final Fruit fruit , final DayOfWeek dayOfWeek ) 
{
    this.fruit = Objects.requireNonNull( fruit ) ;
    this.dayOfWeek = Objects.requireNonNull( dayOfWeek ) ;
}

Last tip: Java 14 brings more helpful NullPointerException objects, better pinpointing the place of failure. See JEP 358: Helpful NullPointerExceptions.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • Actually yeah, the "problematic" code does compile when I inspect it. Looking back through version control, it seems I mixed up and the lambda parameter as another Optional again, which caused a compile error and threw me off. I feel silly now :( That, being said, I think your answer gives the best explanation of the purpose and practices for using `Optional` correctly, so thanks for that! – cloudy_eclispse Jul 02 '20 at 21:31
2

I fails to understand your problem with ifPresent since you are not even handling the case where the value is empty: why not use a method reference?

Have some method take a Foo parameter and do whatever:

class X {

  private Optional<Foo> loadFoo(String name) {...}
  private void consumeFoo(Foo foo) {...}

  void doSomething(String name) {
    this.loadFoo(name).ifPresent(this::consumeFoo);
  }
}

Also you can do

return result.orElse(null);

Instead of:

        if (result.isPresent()) {
            return result.get();
        } else {
            return null;
        }
    
NoDataFound
  • 11,381
  • 33
  • 59
1

I agree with Basil, but here's another possibility :

Bar.loadOptionalFoo(week)
    .map(foo -> 
            String.format("%s %s",
            foo.getSomething(),
            foo.getSomethingElse()))
   .ifPresent(System.out::println);
daniu
  • 14,137
  • 4
  • 32
  • 53
0

You can directly return null more elegantly, then have that null check :). Or if you don't want to have null check, than you can return Optional and do isPresent check

public Foo getFoo(String param) {
            return Optional.ofNullable(loadOptionalFoo(param)).orElse(null);
}

Foo foo = Bar.getFoo(param);
                    
if (foo != null) {
   // Currently just print, but might want to do stuff like pass foo to another object, etc.
   System.out.println(foo.getSomething() + foo.getSomethingElse());
}
Marko Mackic
  • 2,293
  • 1
  • 10
  • 19