^ Added in Java 8 (2014)
^ Optional is a solution to a problem. The problem of null
. But, what is null
?
- Uninitialized value (intentional)
- Uninitialized value (accidental)
- Invalid
- Error condition
- Value not present
^ These are just the semantic problems. There are other problems, like "what is a null, even?"
- Not a part of the type system
- A
null
String
for example, has the same type as a non-null
String
- A
- Surfaces as a runtime problem
- It may take a production run to encounter a
NullPointerException
- It may take a production run to encounter a
^ For these reasons -- this ambiguity -- null
has been called a Billion Dollar Mistake.
I call it my billion-dollar mistake. It was the invention of the
null
reference in 1965. … I couldn't resist the temptation to put in anull
reference, simply because it was so easy to implement. -- Tony Hoare
- Value not present
- Part of the type system
- Surfaces problem at compile-time
^ Java had no way to force the handling the absence of something. You had to explicitly check for null
just to be sure something was there. That sometimes lead to bad programming practices, complex, difficult to read code, or overly-defensive programming.
^ Optional<T>
is a type and so the type system, and therefore the compiler, is used to enforce the correct behavior.
^ Reminiscent of returning empty collections instead of null collections
public <Thing> find(criteria) {
Thing thing = doTheWorkToFindTheThing();
return thing; //btw, it might be null lol
}
public Optional<Thing> safeFind(criteria) {
Thing thing = find(criteria);
return Optional.ofNullable(thing); //may be present
}
^ If you're writing a library method that returns a null
to indicate no value present, instead return Optional.ofNullable()
. Your callers will now have to handle the case where a value is not present.
Optional<Thing> thingy = thingFinder.safeFind(criteria);
Thing thing = thingy.orElse(defaultThing);
Optional<Thing> thingy = thingFinder.safeFind(criteria);
Thing thing = thingy.orElseGet(() -> supplier::computeThing);
Optional<Thing> thingy = thingFinder.safeFind(criteria);
Thing thing = thingy.orElseThrow(RuntimeException::new);
Optional<Thing> thingy = thingFinder.safeFind(criteria);
thingy.ifPresent(this::doIt);
Optional<Thing> thingy = thingFinder.safeFind(criteria);
Optional<Other> otherThing = thingy.map(this::transformIt);
Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using
null
for such was overwhelmingly likely to cause errors. -- Brian Goetz
For example, you probably should never use it for something that returns an array of results, or a list of results; instead return an empty array or list. You should almost never use it as a field of something or a method parameter. -- Brian Goetz
Potential Usage | Matches Intended Use |
---|---|
Library method return types representing "no result" | ✅ |
Returning an array of results | ❌ |
Returning a list of results | ❌ |
Field of something | ❌ |
Method parameter | ❌ |
Local variable | ❌ |
Optional.ofNullable(t).orElse(default)
Optional.ofNullable(t).ifPresent(this:doIt);
It's generally a bad idea to create an Optional for the specific purpose of chaining methods from it to get a value. -- Stuart Marks
Remember,
Optional
is a box!
- consumes 16 bytes
- is a separate object (potentially adds GC pressure)
- always requires a dependent load, leading to cache misses
- a single Optional is OK, but if you litter your data structures with many Optional instances, it could easily turn into a performance problem -- Stuart Marks