Optional Methods in Java - Quiz
Total: 6 questions
1. What is the difference between orElse() and orElseGet() in Optional?
What is the difference between orElse() and orElseGet() in Optional?
They return the same value; they differ in when the fallback is computed. orElse(T other) takes a ready-made object: the argument is an ordinary expression, so it is evaluated every time, before the method is even entered, even when the Optional holds a value and the result is discarded. orElseGet(Supplier<T>) takes a function and invokes it lazily, only when the container is empty. With a literal or a constant on the right-hand side there is no difference: orElse(""), orElse(0), orElse(List.of()). The gap opens as soon as a method call appears there — a database query, a file read, the construction of a heavyweight object, or a method with a side effect such as writing a log entry or bumping a counter: that work is done for nothing and the side effect happens even for a non-empty Optional. Working rule: orElse() for values that already exist, orElseGet() for anything that has to be computed.
2. Why does get() throw NoSuchElementException and what should replace it?
Why does get() throw NoSuchElementException and what should replace it?
An empty Optional holds nothing, so get() has nothing to return and throws java.util.NoSuchElementException with the message No value present. Calling get() without a preceding check is pointless: it merely converts a NullPointerException into a NoSuchElementException. Since Java 10 the javadoc names the no-argument orElseThrow() as the preferred alternative: same behaviour, but the method name makes the risk visible at the call site. When you need your own exception, use orElseThrow(() -> new UserNotFoundException("User not found")). When failing is not an option, use orElse(), orElseGet() or ifPresent(). Note that get() is not marked @Deprecated, so the compiler stays quiet about it.
3. What do map() and filter() do on an Optional and what do they return?
What do map() and filter() do on an Optional and what do they return?
Both return a new Optional instead of a value, which is why they chain. map(Function) applies the function to the value and rewraps the result, and the type may change: summary.map(String::length) turns an Optional<String> into an Optional<Integer>. filter(Predicate) keeps the value when it passes the test and empties the container when it does not. On an empty container neither the map() function nor the filter() predicate is invoked at all — the result is empty immediately, so Optional.empty().map(s -> s.length()).orElse(0) returns 0 without any exception. One detail matters: if the function inside map() returns null, there is no exception — internally map() uses ofNullable(), so you get an empty Optional. If the function itself returns an Optional, use flatMap(), otherwise you end up with a double wrapper Optional<Optional<T>>.
4. Which Optional methods were added after Java 8 and what are they for?
Which Optional methods were added after Java 8 and what are they for?
Java 9: ifPresentOrElse(Consumer, Runnable) — one action for the value and a separate action for the empty case, instead of an if/else around isPresent(); or(Supplier<Optional>) — substitutes an Optional from another source when the container is empty and returns an Optional, so calls stack into a fallback chain (cache, then file, then environment); stream() — turns the container into a Stream of zero or one element, and with flatMap(Optional::stream) it drops empty values from a stream. Java 10: the no-argument orElseThrow() — a self-documenting replacement for get(). Java 11: isEmpty() — returns true for an empty container and reads better than !optional.isPresent(). If your code must compile against Java 8, none of these are available.
5. How do OptionalInt, OptionalLong and OptionalDouble differ from Optional<T>?
How do OptionalInt, OptionalLong and OptionalDouble differ from Optional<T>?
They are dedicated containers for primitives: they avoid boxing into wrapper objects and are what primitive stream methods return — average(), max(), findFirst(). Instead of get() they expose getAsInt(), getAsLong() and getAsDouble(), which return a primitive. Their API is noticeably thinner: you get isPresent(), isEmpty(), ifPresent(), ifPresentOrElse(), orElse(), orElseGet(), orElseThrow() and stream(), but there is no map(), filter(), flatMap() or or() — code such as OptionalInt.of(5).map(i -> i * 2) simply does not compile. If you need transformations, move to the object version: OptionalInt.of(1).stream().boxed().findFirst() gives you an Optional<Integer>, which has them all.
6. Why is Optional as a class field or a method parameter considered an anti-pattern?
Why is Optional as a class field or a method parameter considered an anti-pattern?
Optional was designed as a return type for methods that may find nothing. As a field it adds an extra object per instance and breaks serialization: Optional does not implement Serializable. As a parameter it forces callers to write Optional.of(x) and introduces a third state — somebody can pass null in place of the container itself, and an emptiness check will not save you. Use method overloading for optional parameters, and for state keep a plain field with a getter that returns Optional. For the same reason do not store Optional in collections: a List<Optional<String>> almost always means the empty elements should have been dropped earlier, for example with flatMap(Optional::stream). A healthy sign: the container is created inside a method, lives for a few lines and disappears at an orElse() or an ifPresent().