How to return when an optional is empty?
Asked Answered
D

6

54

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?

Decimalize answered 27/6, 2016 at 20:28 Comment(8)
There is really nothing wrong with if (optional.isPresent()) {...}. Optional adds more semantic value than null, so mission accomplished. Readability and brevity are often a fine balance. "Ugly" and "verbose" are such exaggerations here.Nibelungenlied
@Decimalize if you are interested in a more general case (different types of optionals and operations), please add it to your question. It is a different use case if you have the same types and same operations.Steffie
this all seams like examples where the use of Optional itself is not the best solution...Nereen
If an optional is empty, yet a value is expected, that's an error. Sounds like you should be throwing an exception. Using null with Optional is definitely a code smell, seeing how the purpose of Optional is to avoid the need to declare/check for null.Briticism
@VinceEmigh: That is a misunderstanding.Decimalize
@Decimalize How is it a misunderstanding? Optional exists for values that may not be present. orElse exists for specifying a default value in such a case. Specifying null as the default value destroys the purpose of using Optional, as it would still require the null check (ifPresent) or blow up (get, which Brain himself mentioned should be avoided). If you require a value, how does using Optional make sense? Apparently the value isn't required, and the design is flawed... You should NEVER return null from an Optional. Mind elaborating on my misunderstanding?Briticism
Sorry for my much-too-brief response. I think there is clearly a value of using Optional here. For getOptional the absence of a value is a valid result. For m1 this fact is expected and valid, and the action to be taken in that case is to return. There is no reason to throw an exception whatsoever, and the optional in the return type of getOptional makes the potential absence of a produced value clearly visible. (And getOptional might not be a method I can rewrite anyway.)Decimalize
It is truly weird how some questions unexpectedly end up so popular! :)Decimalize
C
54

You could use orElse(null):

String o = getOptional().orElse(null);
if (o == null) {
    return;
}
Ciborium answered 27/6, 2016 at 20:31 Comment(7)
My first thought was: "Meh, then I'm back to just using nullables." But on a second thought maybe this is not so bad! Because the potential absence of a value is still visible in the type of getOptional, and the nullable string is used in a small scope where it's very clear what's going on.Decimalize
Ah, now I've tried it in some real code and I like it more and more! Already half a dozen pesky gets are gone from my code. Thank you!Decimalize
really? and why not add one more level: boolean noData = (o == null); if (noData) { return; }Nereen
@CarlosHeuberger: Your comment sounds to me line some sort or irony, but I don't understand your intent. This is a good solution for my problem because: 1) I don't have to use Optional.get to get the value of o in the rest of the method. 2) The potential absence of a value is visible in the return type of the getOptional method. 3) There is no extra levels of nested blocks in the rest of the method resulting from the use of Optinal.isPresent. 4) Even though a nullable string is used it is only used in a very limited scope where the risk of mistakes is minimal.Decimalize
@Decimalize Irony: Optional was introduced to avoid null; this is just going back (IMHO)... obviously I do not know what the Optional is, where it comes from and what is intended to be done with it; my comments are only based on what was asked.Nereen
Optional was introduced to avoid problems with null; it allows you to make it clear in your method signature that you might return something nully. You can't stop people doing stupid things but you can try and help people stop making stupid mistakes.Cholinesterase
An example to clarify: using an optional is safer than, String o = getMysteriousThing(); o.length(), and this solution is clearer than, String o = getOptional().orElse(null); o.length().Cholinesterase
M
12

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.

Morvin answered 27/6, 2016 at 20:38 Comment(4)
See method m5 in the example, it is meant to demonstrate why I don't want to do this. I think using ifPresent is okay when you do one short thing. But if you do a lot of things and/or have several optionals then the level of nesting quickly makes it ugly and hard to read.Decimalize
@Decimalize you can move/split long blocks in new methods, right?Nereen
@CarlosHeuberger: Yeah, but often I don't want to decide what methods to structure my program into based on only when I have to deal with an optional value. And often a method first tests a few preconditions and terminate if they don't hold. Sometimes those preconditions are that an optional value is not empty.Decimalize
@Decimalize but you wrote "...makes it ugly and hard to read" - that should be a reason to decide how to structure your program and a way to do that is what I meant with my commentNereen
H
11

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!

Hollerman answered 27/6, 2016 at 20:32 Comment(5)
Often the block of code to execute in the lambda is pretty large so this is not an option most of the time.Decimalize
@Decimalize If it's a big block of code, then it's quite possible that it should be extracted into its own method, which could then easily be passed as a method reference.Hollerman
Heh, you suggest that I use both nesting and get calls when what I want is to avoid both! I think it's much clearer to start a method with a few checks of preconditions and then return if they fail. That makes it immediate clear to readers what happens if the checks fail, and it can save several levels of nesting for the whole remainder of the method.Decimalize
Your write: "except that it forces you to consider the case where the value is missing" But that thing alone is really very valuable, and maybe the main point of using optionals.Decimalize
@Decimalize Yes, forcing the programmer to handle the possibility of the value not being present is important. But that's usually more like String value = getOptional().orElse("default value"); Afterward, we always have a string: either the one in the optional, or "default value". The point is that we need some String value that may or may not be present. You're not doing any string processing when the optional doesn't have a value. That's the difference.Hollerman
N
6

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

Natural answered 28/3, 2019 at 19:34 Comment(4)
I agree than this is an interesting discussion! I think the functional approach that you suggesting is interesting and have its merits. But even after working quite a lot in functional languages I prefer the more imperative approach where you return early. It feels wrong to me that you would have to rely on library functions such as flatMap for simple control flow.Decimalize
If you want to go further down the road of the functional approach then there are libraries for this, have a look at this answer. Very interesting! But I prefer to avoid then most of the time.Decimalize
Thanks for the link - I heard Vavr is as close as it gets to Scala. There are some interesting talks, especially around exception handling in java streams: youtube.com/watch?v=zko8R_alQgw&t=3s so you have things like Either, which you will know very well in principle. If someone doesn't want to pull the whole library just to use one tool, it's fairly easy to implement Either yourself, there is an example code in this talk: youtube.com/watch?v=vuFCTdywMtE&t=37m25sNatural
Some things are often a matter of personal preference on the style of coding you're comfortable with. I suppose, what matters is to understand the concepts and be consistent throughout the project.Natural
B
1
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.

Barytone answered 22/12, 2023 at 8:55 Comment(1)
this form was what was needed in my caseLawanda
M
0

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.

Maladjusted answered 27/6, 2016 at 20:34 Comment(3)
Hm. But then I have to pass all arguments to m4 along to doThing also. And most of the time I don't wish to divide my solution into methods based on just that I'm working with an optional.Decimalize
@Decimalize No, you don't have to pass all of them; you only have to pass the ones that doThings needs. One of your concerns in the question was that some objects had a much larger scope than necessary. By refactoring out to another method, and passing values to it, you can avoid the wider scopes.Hollerman
When it's just one value (the string), though; the if (!o.isPresent()) return; seems superfluous. The whole m4 method body could just be: getOptional().ifPresent(this::doThings);.Hollerman

© 2022 - 2025 — McMap. All rights reserved.