Java 8 Optional - Good and Bad

Why we need Java Optional?

A code example

1
2
3
4
5
6
7
8
9
10
String version = "UNKNOWN";
if(computer != null){
Soundcard soundcard = computer.getSoundcard();
if(soundcard != null){
USB usb = soundcard.getUSB();
if(usb != null){
version = usb.getVersion();
}
}
}
  • less readable
  • more lines of code
  • easier to forget checking nulls
  • etc...

A better rewrite with Optional in Java 8

1
2
3
4
String name = computer.flatMap(Computer::getSoundcard)
.flatMap(Soundcard::getUSB)
.map(USB::getVersion)
.orElse("UNKNOWN");

How to use

Java 8's Optional was mainly intended for return values from methods, and not for properties of Java classes, as described in Optional in Java SE 8.

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.

  • Not intended for use as a property of a Java Bean: not implement Serializable
  • Not intended for use as a function parameter type. In comparison to nullable parameters Optional is more costly:
  • conditional logic inside the methods is literally contra-productive
  • need to pack an argument in an Optional, is suboptimal for the compiler, and does an unnecessary wrapping.

Optional unifies two states, which have to be unraveled. Hence better suited for result than input, for the complexity of the data flow.


Good vs Bad

Good

  • Force to check null value
  • Remove nested null checks
  • Provide default value and action
  • e.g. maybeSoundcard.orElse(new Soundcard("defaut"));
  • e.g. maybeSoundCard.orElseThrow(IllegalStateException::new);
  • Convenient Stream interface
  • Optional::ifPresent()
  • Optional::flatMap(), cascading Optional objects
  • Optional::map(), extracting and transforming values
  • Optional::filter(), conditional return
  • Optional::orElse(), default values and actions

Bad

  • Wrapper class, added layer of reference, which makes
  • debugging stack traces may be slightly worse
  • performance overhead on wrapping and unwrapping
  • more complex class structure - being a wrapper class
  • It's not serializable making it useless for many cases.
  • Enforce conditional logic like isPresent.
  • Invariance. Certain operations become cumbersome when the actual value type is pushed into a generic type argument.

Optional::flatMap() vs Optional::map()

They both apply a mapper function and return an Optional.

But the mapper function is different. After applying mapper, map() also warps the result in an Optional.

flatMap() takes in Function<? super T, Optional<U>> mapper, the mapper expects an Optional typed output.

It only does mapper.apply(value).

map() takes in Function<? super T, ? extends U> mapper, the mapper expects an U typed output.

It does Optional.ofNullable(mapper.apply(value));

1
2
3
4
// map()
Optional<Optional<SoundCard>> soundCard1 = computerOptional.map(Computer::getSoundCard);
// flatMap()
Optional<SoundCard> soundCard2 = computerOptional.flatMap(Computer::getSoundCard);

Java 9’s Optional::stream

1
2
3
4
5
6
7
8
9
10
11
// In Java 8
List<String> filteredList = listOfOptionals.stream()
.flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty))
// alternative
// .flatMap(o -> o.isPresent() ? Stream.of(o.get()) : Stream.empty())
.collect(Collectors.toList());

// In Java 9
List<String> filteredList = listOfOptionals.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());

References

https://stackoverflow.com/questions/23454952/uses-for-optional

https://stackoverflow.com/questions/31922866/why-should-java-8s-optional-not-be-used-in-arguments

https://stackoverflow.com/questions/29033518/is-it-a-good-practice-to-use-optional-as-an-attribute-in-a-class

http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

https://stackoverflow.com/questions/30864583/java-8-difference-between-optional-flatmap-and-optional-map

http://www.baeldung.com/java-optional

http://www.baeldung.com/java-filter-stream-of-optional