50

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?

Lii
  • 11,553
  • 8
  • 64
  • 88
  • 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. – Radiodef Jun 27 '16 at 21:35
  • @Lii 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. – user140547 Jun 28 '16 at 07:23
  • 1
    this all seams like examples where the use of Optional itself is not the best solution... – user85421 Sep 30 '17 at 06:46
  • 1
    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. – Vince Sep 30 '17 at 07:04
  • @VinceEmigh: That is a misunderstanding. – Lii Sep 30 '17 at 07:36
  • @Lii 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? – Vince Sep 30 '17 at 07:43
  • 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.) – Lii Sep 30 '17 at 13:43
  • It is truly weird how some questions unexpectedly end up so popular! :) – Lii Aug 24 '22 at 07:00

5 Answers5

52

You could use orElse(null):

String o = getOptional().orElse(null);
if (o == null) {
    return;
}
dnault
  • 8,340
  • 1
  • 34
  • 53
  • 4
    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. – Lii Jun 27 '16 at 20:37
  • Ah, now I've tried it in some real code and I like it more and more! Already half a dozen pesky `get`s are gone from my code. Thank you! – Lii Jun 27 '16 at 21:14
  • really? and why not add one more level: `boolean noData = (o == null); if (noData) { return; }` – user85421 Sep 30 '17 at 06:48
  • 1
    @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. – Lii Sep 30 '17 at 13:48
  • 1
    @Lii 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. – user85421 Sep 30 '17 at 14:48
  • 3
    `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. – Druckles Jun 05 '18 at 14:51
  • 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()`. – Druckles Jun 05 '18 at 14:53
11

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.

Sleiman Jneidi
  • 22,907
  • 14
  • 56
  • 77
  • 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. – Lii Mar 14 '17 at 09:17
  • 1
    @Lii you can move/split long blocks in new methods, right? – user85421 Sep 30 '17 at 06:53
  • @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. – Lii Sep 30 '17 at 07:04
  • @Lii 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 comment – user85421 Sep 30 '17 at 08:00
10

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 expect, 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!

Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
  • Often the block of code to execute in the lambda is pretty large so this is not an option most of the time. – Lii Jun 27 '16 at 20:39
  • 1
    @Lii 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. – Joshua Taylor Jun 27 '16 at 20:43
  • 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. – Lii Jun 27 '16 at 20:54
  • 1
    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. – Lii Jun 27 '16 at 20:59
  • 1
    @Lii 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. – Joshua Taylor Jun 27 '16 at 21:12
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 :)

mwdev
  • 469
  • 3
  • 5
  • 1
    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. – Lii Mar 29 '19 at 07:08
  • 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](https://stackoverflow.com/a/30787638/452775). Very interesting! But I prefer to avoid then most of the time. – Lii Mar 29 '19 at 07:10
  • 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: https://www.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: https://www.youtube.com/watch?v=vuFCTdywMtE&t=37m25s – mwdev Mar 29 '19 at 09:12
  • 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. – mwdev Mar 29 '19 at 09:33
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.

Luka Jacobowitz
  • 22,795
  • 5
  • 39
  • 57
  • 1
    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. – Lii Jun 27 '16 at 20:43
  • 1
    @Lii 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. – Joshua Taylor Jun 27 '16 at 21:14
  • 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);`. – Joshua Taylor Jun 27 '16 at 21:16