Uses for Optional
Solution 1:
The main design goal of Optional
is to provide a means for a function returning a value to indicate the absence of a return value. See this discussion. This allows the caller to continue a chain of fluent method calls.
This most closely matches use case #1 in the OP's question. Although, absence of a value is a more precise formulation than null since something like IntStream.findFirst
could never return null.
For use case #2, passing an optional argument to a method, this could be made to work, but it's rather clumsy. Suppose you have a method that takes a string followed by an optional second string. Accepting an Optional
as the second arg would result in code like this:
foo("bar", Optional.of("baz"));
foo("bar", Optional.empty());
Even accepting null is nicer:
foo("bar", "baz");
foo("bar", null);
Probably the best is to have an overloaded method that accepts a single string argument and provides a default for the second:
foo("bar", "baz");
foo("bar");
This does have limitations, but it's much nicer than either of the above.
Use cases #3 and #4, having an Optional
in a class field or in a data structure, is considered a misuse of the API. First, it goes against the main design goal of Optional
as stated at the top. Second, it doesn't add any value.
There are three ways to deal with the absence of a value in an Optional
: to provide a substitute value, to call a function to provide a substitute value, or to throw an exception. If you're storing into a field, you'd do this at initialization or assignment time. If you're adding values into a list, as the OP mentioned, you have the additional choice of simply not adding the value, thereby "flattening" out absent values.
I'm sure somebody could come up with some contrived cases where they really want to store an Optional
in a field or a collection, but in general, it is best to avoid doing this.
Solution 2:
I'm late to the game but for what it's worth, I want to add my 2 Cents. They go against the design goal of Optional
, which is well summarized by Stuart Marks's answer, but I'm still convinced of their validity (obviously).
Use Optional Everywhere
In General
I wrote an entire blog post about using Optional
but it basically comes down to this:
- design your classes to avoid optionality wherever feasibly possible
- in all remaining cases, the default should be to use
Optional
instead ofnull
- possibly make exceptions for:
- local variables
- return values and arguments to private methods
- performance critical code blocks (no guesses, use a profiler)
The first two exceptions can reduce the perceived overhead of wrapping and unwrapping references in Optional
. They are chosen such that a null can never legally pass a boundary from one instance into another.
Note that this will almost never allow Optional
s in collections which is almost as bad as null
s. Just don't do it. ;)
Regarding your questions
- Yes.
- If overloading is no option, yes.
- If other approaches (subclassing, decorating, ...) are no option, yes.
- Please no!
Advantages
Doing this reduces the presence of null
s in your code base, although it does not eradicate them. But that is not even the main point. There are other important advantages:
Clarifies Intent
Using Optional
clearly expresses that the variable is, well, optional. Any reader of your code or consumer of your API will be beaten over the head with the fact that there might be nothing there and that a check is necessary before accessing the value.
Removes Uncertainty
Without Optional
the meaning of a null
occurrence is unclear. It could be a legal representation of a state (see Map.get
) or an implementation error like a missing or failed initialization.
This changes dramatically with the persistent use of Optional
. Here, already the occurrence of null
signifies the presence of a bug. (Because if the value were allowed to be missing, an Optional
would have been used.) This makes debugging a null pointer exception much easier as the question of the meaning of this null
is already answered.
More Null Checks
Now that nothing can be null
anymore, this can be enforced everywhere. Whether with annotations, assertions or plain checks, you never have to think about whether this argument or that return type can be null. It can't!
Disadvantages
Of course, there is no silver bullet...
Performance
Wrapping values (especially primitives) into an extra instance can degrade performance. In tight loops this might become noticeable or even worse.
Note that the compiler might be able to circumvent the extra reference for short lived lifetimes of Optional
s. In Java 10 value types might further reduce or remove the penalty.
Serialization
Optional
is not serializable but a workaround is not overly complicated.
Invariance
Due to the invariance of generic types in Java, certain operations become cumbersome when the actual value type is pushed into a generic type argument. An example is given here (see "Parametric polymorphism").
Solution 3:
Personally, I prefer to use IntelliJ's Code Inspection Tool to use @NotNull
and @Nullable
checks as these are largely compile time (can have some runtime checks) This has lower overhead in terms of code readability and runtime performance. It is not as rigorous as using Optional, however this lack of rigour should be backed by decent unit tests.
public @Nullable Foo findFoo(@NotNull String id);
public @NotNull Foo doSomething(@NotNull String id, @Nullable Bar barOptional);
public class Book {
private List<Pages> pages;
private @Nullable Index index;
}
List<@Nullable Foo> list = ..
This works with Java 5 and no need to wrap and unwrap values. (or create wrapper objects)
Solution 4:
I think the Guava Optional and their wiki page puts it quite well:
Besides the increase in readability that comes from giving null a name, the biggest advantage of Optional is its idiot-proof-ness. It forces you to actively think about the absent case if you want your program to compile at all, since you have to actively unwrap the Optional and address that case. Null makes it disturbingly easy to simply forget things, and though FindBugs helps, we don't think it addresses the issue nearly as well.
This is especially relevant when you're returning values that may or may not be "present." You (and others) are far more likely to forget that other.method(a, b) could return a null value than you're likely to forget that a could be null when you're implementing other.method. Returning Optional makes it impossible for callers to forget that case, since they have to unwrap the object themselves for their code to compile. -- (Source: Guava Wiki - Using and Avoiding null - What's the point?)
Optional
adds some overhead, but I think its clear advantage is to make it explicit
that an object might be absent and it enforces that programmers handle the situation. It prevents that someone forgets the beloved != null
check.
Taking the example of 2, I think it is far more explicit code to write:
if(soundcard.isPresent()){
System.out.println(soundcard.get());
}
than
if(soundcard != null){
System.out.println(soundcard);
}
For me, the Optional
better captures the fact that there is no soundcard present.
My 2¢ about your points:
-
public Optional<Foo> findFoo(String id);
- I am not sure about this. Maybe I would return aResult<Foo>
which might be empty or contain aFoo
. It is a similar concept, but not really anOptional
. -
public Foo doSomething(String id, Optional<Bar> barOptional);
- I would prefer @Nullable and a findbugs check, as in Peter Lawrey's answer - see also this discussion. - Your book example - I am not sure if I would use the Optional internally, that might depend on the complexity. For the "API" of a book, I would use an
Optional<Index> getIndex()
to explicitly indicate that the book might not have an index. - I would not use it in collections, rather not allowing null values in collections
In general, I would try to minimize passing around null
s. (Once burnt...)
I think it is worth to find the appropriate abstractions and indicate to the fellow programmers what a certain return value actually represents.