Lii
Lii

Reputation: 12112

How to return when an optional is empty?

I love that optionals are in the Java standard library now. But there is one basic problem that I keep running into that I haven't figured out how to solve in the best way (easiest to read and understand, prettiest, shortest):

How to return from a method when an optional is empty?

I am looking for a general solution that works for different combinations of numbers of optionals and sizes of code blocks.

In the following examples I'll try to show what I mean:

void m1() {
    // When I get an optional:
    Optional<String> o = getOptional();

    // And want to return if it's empty
    if (!o.isPresent()) return;
    
    // In the whole rest of the method I have to call Optional.get 
    // every time I want the value:
    System.out.println(o.get());
    
    // Which is pretty ugly and verbose!
}


void m2() {
    // If I instead return null if a value is absent:
    String s = getNullabe();
    if (s == null) return;
    
    // Then I can use the value directly:
    System.out.println(s);
}

This question is about how to get the good aspect of both the examples above: The type safely of the optional and the brevity of nullable types.

The rest of the examples illustrates this more.

void m3() {
    // If I on the other hand want to throw on empty that's pretty and compact:
    String s = getOptional()
        .orElseThrow(IllegalStateException::new);
    
    System.out.println(s);
}

void m4() {
    Optional<String> o = getOptional();
    if (!o.isPresent()) return;
    
    // I can of course declare a new variable for the un-optionalised string:
    String s = o.get();

    System.out.println(s);
    
    // But the old variable still remains in scope for the whole method 
    // which is ugly and annoying.
    System.out.println(o.get());
}


void m5() {
    // This is compact and maybe pretty in some ways:
    getOptional().ifPresent(s -> {
        System.out.println(s);

        // But the extra level of nesting is annoying and it feels 
        // wrong to write all the code in a big lambda.
        
        getOtherOptional().ifPresent(i -> {
            // Also, more optional values makes it really weird and 
            // pretty hard to read,  while with nullables I would 
            // get no extra nesting, it would looks good and be 
            // easy to read.
            System.out.println("i: " + i);
            
            // It doesn't work in all cases either way.
        });
    });
}


Optional<String> getOptional() {
    throw new UnsupportedOperationException();
}

Optional<Integer> getOtherOptional() {
    throw new UnsupportedOperationException();
}

String getNullabe() {
    throw new UnsupportedOperationException();
}

How can I return from a method if an optional is empty, without having to use get in the rest of the method, without declaring an extra variable and without extra levels of block nesting?

Or if it's not possible to get all that, what is the best way to handle this situation?

Upvotes: 55

Views: 209760

Answers (6)

degr
degr

Reputation: 1565

public Boolean myMethod(String input) {
  return Optional.ofNullable(input)
   .map(v -> v.equals("A"))
   .orElse(Boolean.FALSE)
}


public Boolean myMethod(String input) {
  return Optional.ofNullable(input)
   .map(v -> v.equals("A"))
   .orElseGet(() -> Boolean.FALSE)
}

In case if your optional is null, "orElse" or "orElseGet" construction will work. Otherwise "map" function is used.

Upvotes: 1

Joshua Taylor
Joshua Taylor

Reputation: 85863

The ifPresent that you're using doesn't require you to create a new lambda, you can just use a method reference:

getOptional().ifPresent(System.out::println);

This doesn't really solve the case where you want to conditionalize on the presence of two optionals, though. But as an alternative to

// And want to return if it's empty
if (!o.isPresent()) return;

why not just reverse the condition, which works nicely in the nested case, too? There's no need to make the return explicit:

if (o.isPresent()) {
  System.out.println(o.get());
  if (oo.isPresent()) {
    System.out.println(oo.get());
  }
}

However, this kind of use case suggests that you're not really benefiting from Optional as opposed to a nullable value. In general, if you're using isPresent and get, then Optional might not really be getting you all that much (except that it forces you to consider the case where the value is missing). Using ifPresent, map, filter, and other "more functional" methods might be more typical uses for an Optional value.


But in any case, please don't return null when you're promising an Optional. Though it's perfectly legal to return null when an object is expected, the point of Optional is precisely to avoid having to check for null. So don't do:

Optional<String> getOptional() {
    return null;
}

but instead do:

Optional<String> getOptional() { 
  return Optional.empty();
}

Otherwise you end up having to do:

Optional<String> o = getOptional();
if (o != null && o.isPresent()) {
  // ...
}

which is really just doing the same kind of thing twice. Use an Optional, or use a nullable value, but don't do both!

Upvotes: 12

mwdev
mwdev

Reputation: 479

That's a great topic, we all love functional style of programming!

Often when start an implementation of a method you are given an optional right at the top. At this point you start wondering, what is the best you can do to handle an empty optional, it only makes sense to exit and stop processing if that's the case.

STEP 1 - Explore and anylyze

public void processMedia(String mediaClassName, String mediaName) {

    // THAT MIGHT BE YOUR FIRST IDEA
    MediaClass mediaClass = mediaClassFinder.find(mediaClassName).orElse(null); 

    // RETURNING ON NULL CONDITION LIKE THE BELOW CAN BE ALRIGHT,
    // BUT POSSIBLY YOU CAN DO BETTER
    if (mediaClass == null) {
        return;
    }
    Optional<Media> media = mediaFinder.find(mediaClass.getId(), mediaName);

    // do processing

    // render the processed object
}

STEP 2 The best approach might be to extract various pieces of the implementation to a separate methods and chain them together in a functional style. As a side effect of this exercise you will probably end up with much improved interface and structure of your application. That's how refactoring works. Look below, there is no explicit null assignments and no extra return points anywhere. And the coding becomes fun.

public void processMedia(String mediaClassName, String mediaName) {
    mediaClassFinder.find(mediaClassName)
        .flatMap(mediaClass -> mediaFinder.find(mediaClass.getId(), mediaName))
        .map(this::compress)
        .ifPresent(this::render);
}
private Media compress(Media media) {
    // compress media implementation
    return media;
}
private void render(Media media) {
    // render media implementation
}

I hope you liked my example :)

Upvotes: 6

Sleiman Jneidi
Sleiman Jneidi

Reputation: 23329

You can use ifPresent and map methods instead, if the function is void and you need to do side-effects you can use ifPresent,

optional.ifPresent(System.out::println); 

If another method return relies on the Optional than that method might need to return an Optional as well and use the map method

Optional<Integer> getLength(){
    Optional<String> hi = Optional.of("hi");
    return hi.map(String::length)
}

Most of the time when you call isPresent and get, you are misusing Optional.

Upvotes: 12

Luka Jacobowitz
Luka Jacobowitz

Reputation: 23512

I don't think what you're asking is actually possible, but I would like to suggest just taking all of your code that works directly on your String and wrap it in a function. So your function becomes something like this:

void m4() {
    Optional<String> o = getOptional();
    if (!o.isPresent()) return;

    doThings(o.get());
}

void doThings(String s){
    System.out.println(s);
    //do whatever else with the string.
}

This way you only have the String in scope and you don't have to call .get() everytime you want to access it.

Upvotes: 0

dnault
dnault

Reputation: 8899

You could use orElse(null):

String o = getOptional().orElse(null);
if (o == null) {
    return;
}

Upvotes: 56

Related Questions