Optional .... what else?
Tags: java, functional, codenewbie
Published: 2019-08-19 09:27:35


Optional is in some ways, the Java implementation of the Maybe monad. Don’t get scared by the ‘M’ word. It is simply an encapsulation to handle a specific case of a type — in this case, the possibility of a null value. Just consider it a wrapper to force the user to check if the value is present or not.

When using an Optional in a declarative way, we are able to execute a map function on it. The lambda supplied with the map will only be executed if the Optional is filled with a value.

```java
public void execute() {
   Optional<String> maybeString = Optional.of("foo");
maybeString.map(this::runIfExist);
}

private String runIfExist(String str) {
   System.out.println("only run if optional is filled ");
   return str;
}
```
 
When running the `execute()` method, the console output will obviously be: 
```
only run if optional is filled
```

If we change the code of maybeString to Optional.empty() and execute again, as expected, nothing happens.

### Alternative Flow
That's all nice, but when we want an alternative flow for when the Optional is empty, we can choose from `else()`, `elseGet()`, and  `elseThrow()`. With the last one, we can throw an exception, but what if we want to do something different?

Intuitively, the following code looks quite obvious.

```java
public void execute() {
   Optional<String> maybeString = Optional.of("foo");
   String newString = maybeString
           .map(this::runIfExist)
           .orElse(runIfEmpty());
   System.out.println(newString);
}

private String runIfExist(String str) {
   System.out.println("only run if optional is filled ");
   return str;
}

private String runIfEmpty() {
   System.out.println("only run if empty");
   return "empty";
}
```
 
By reading the code, we probably expect that `newString` will be given the value  `"foo"` because `maybeString` is an Optional containing a value. When running the `execute()` method, the following output will end up in your console:

```
only run if optional is filled
only run if empty
foo
```

We can still conclude that the `newString` will end up with the value `"foo"`. The other thing we can see here, is that the string `"only run if empty"` is also printed to the console. That was something I didn't expect at first sight. So even if the Optional contains a value, the method inside the `orElse()` is still being executed. 

When we change the `execute()` method and use `orElseGet()` instead of `orElse()`, we also need to change the method call to runIfEmpty() into a supplier:

```java
public void execute() {
   Optional<String> maybeString = Optional.of("foo");
   String newString = maybeString
           .map(this::runIfExist)
           .orElseGet(() -> runIfEmpty());
   System.out.println(newString);
}
```
 
If we now run the execute() method, we see the following:
```
only run if optional is filled
foo
``` 
When using this implementation, we see that the supplier inside the `orElseGet()` is not executed. Intuitively, this is the behavior I would expect in general when you use `orElse()`

### Conclusion
When using a `map()` to assign a value to a variable, there will not be any problem using `orElse()` for assigning a default value when the Optional is `empty`. You just have to make sure that the code inside the `orElse()` is absolutely side effect free. In fact, the purpose of the `orElse()` method is to assign a default value and nothing more than that.

On the other hand, if you need to steer behavior based on an Optional, you should use the `orElseGet()` construct. You probably don't want your alternative flow to be executed anyway regardless of the value of the Optional.

Because of the nature of declarative programming, the mix-up is made very easily. Moreover, you will only see the result when you actually execute the code. So, of course, I can now advocate writing proper test code (and you should), but it is better to know the difference beforehand. The difference is subtle, but it can have a large impact if you are not aware of it.