Java Optional: Ten Methods, Five Real Runs
java.util.Optional<T> is a container that either holds a value or doesn’t. This post runs five small Java files, each compiled and run for real, covering ten Optional methods: creation and presence checks, unwrapping, conditional actions, transformation/filtering, and streams/equality.
Creating an Optional, and checking what’s inside
Optional.of wraps a non-null value, Optional.empty() makes an empty one, and Optional.ofNullable picks between the two depending on whether its argument is null. isPresent/isEmpty report which state you’ve got.
import java.util.Optional;
public class CreatingCheckingDemo {
public static void main(String[] args) {
Optional<String> present = Optional.of("hello");
Optional<String> empty = Optional.empty();
Optional<String> fromNullable = Optional.ofNullable(null);
System.out.println("Optional.of(\"hello\") = " + present);
System.out.println("Optional.empty() = " + empty);
System.out.println("Optional.ofNullable(null) = " + fromNullable);
System.out.println("present.isPresent() = " + present.isPresent());
System.out.println("present.isEmpty() = " + present.isEmpty());
System.out.println("empty.isPresent() = " + empty.isPresent());
System.out.println("empty.isEmpty() = " + empty.isEmpty());
}
}
Real javac+java output, JDK 25 (Zulu):
Optional.of("hello") = Optional[hello]
Optional.empty() = Optional.empty
Optional.ofNullable(null) = Optional.empty
present.isPresent() = true
present.isEmpty() = false
empty.isPresent() = false
empty.isEmpty() = true
Unwrapping: get, orElseThrow, orElse, orElseGet
get() and orElseThrow(supplier) both throw when the Optional is empty; orElse(value) and orElseGet(supplier) both fall back to a default instead of throwing when it’s empty.
import java.util.Optional;
public class UnwrappingDemo {
public static void main(String[] args) {
Optional<String> present = Optional.of("hello");
Optional<String> empty = Optional.empty();
System.out.println("present.get() = " + present.get());
try {
empty.get();
} catch (Exception e) {
System.out.println("empty.get() threw " + e.getClass().getName());
}
try {
empty.orElseThrow(() -> new IllegalStateException("no value"));
} catch (Exception e) {
System.out.println("empty.orElseThrow(...) threw " + e.getClass().getName() + ": " + e.getMessage());
}
System.out.println("empty.orElse(\"default\") = " + empty.orElse("default"));
System.out.println("empty.orElseGet(() -> \"computed\") = " + empty.orElseGet(() -> "computed"));
int[] callCount = {0};
String result = present.orElseGet(() -> {
callCount[0]++;
return "computed";
});
System.out.println("present.orElseGet(...) = " + result + ", supplier called " + callCount[0] + " times");
}
}
Real javac+java output, JDK 25 (Zulu):
present.get() = hello
empty.get() threw java.util.NoSuchElementException
empty.orElseThrow(...) threw java.lang.IllegalStateException: no value
empty.orElse("default") = default
empty.orElseGet(() -> "computed") = computed
present.orElseGet(...) = hello, supplier called 0 times
Acting on a value without unwrapping it: ifPresent, ifPresentOrElse, or
ifPresent(consumer) runs the consumer only when a value is there; ifPresentOrElse(consumer, emptyAction) adds a second callback for the empty case; or(supplier) returns another Optional as a fallback instead of a raw value.
import java.util.Optional;
public class ActingDemo {
public static void main(String[] args) {
Optional<String> present = Optional.of("hello");
Optional<String> empty = Optional.empty();
present.ifPresent(v -> System.out.println("present.ifPresent ran with: " + v));
empty.ifPresent(v -> System.out.println("empty.ifPresent ran with: " + v));
present.ifPresentOrElse(
v -> System.out.println("present.ifPresentOrElse: value branch, v=" + v),
() -> System.out.println("present.ifPresentOrElse: empty branch"));
empty.ifPresentOrElse(
v -> System.out.println("empty.ifPresentOrElse: value branch, v=" + v),
() -> System.out.println("empty.ifPresentOrElse: empty branch"));
Optional<String> fallback = empty.or(() -> Optional.of("fallback"));
System.out.println("empty.or(() -> Optional.of(\"fallback\")) = " + fallback);
Optional<String> unchanged = present.or(() -> Optional.of("fallback"));
System.out.println("present.or(() -> Optional.of(\"fallback\")) = " + unchanged);
}
}
Real javac+java output, JDK 25 (Zulu):
present.ifPresent ran with: hello
present.ifPresentOrElse: value branch, v=hello
empty.ifPresentOrElse: empty branch
empty.or(() -> Optional.of("fallback")) = Optional[fallback]
present.or(() -> Optional.of("fallback")) = Optional[hello]
Transforming and filtering: map, flatMap, filter
map(fn) applies fn to the wrapped value and rewraps the result, passing an empty Optional through unchanged; flatMap does the same but for a function that itself returns an Optional, avoiding a nested Optional<Optional<T>>; filter(predicate) keeps the value only if the predicate matches.
import java.util.Optional;
public class TransformFilterDemo {
public static void main(String[] args) {
Optional<String> present = Optional.of("hello");
Optional<Integer> length = present.map(String::length);
System.out.println("present.map(String::length) = " + length);
Optional<String> empty = Optional.empty();
Optional<Integer> emptyLength = empty.map(String::length);
System.out.println("empty.map(String::length) = " + emptyLength);
Optional<Optional<String>> nested = Optional.of(Optional.of("nested"));
Optional<String> flat = nested.flatMap(o -> o);
System.out.println("nested.flatMap(o -> o) = " + flat);
Optional<String> tooShort = present.filter(v -> v.length() > 10);
Optional<String> longEnough = present.filter(v -> v.length() > 2);
System.out.println("present.filter(len > 10) = " + tooShort);
System.out.println("present.filter(len > 2) = " + longEnough);
}
}
Real javac+java output, JDK 25 (Zulu):
present.map(String::length) = Optional[5]
empty.map(String::length) = Optional.empty
nested.flatMap(o -> o) = Optional[nested]
present.filter(len > 10) = Optional.empty
present.filter(len > 2) = Optional[hello]
Streams and equality: Optional::stream, equals
Optional::stream turns an Optional into a 0- or 1-element Stream, so flatMap(Optional::stream) over a list of optionals drops the empties; equals compares the wrapped values, and two empty Optionals are equal to each other.
import java.util.Optional;
import java.util.List;
public class StreamsEqualityDemo {
public static void main(String[] args) {
List<Optional<String>> optionals = List.of(
Optional.of("a"), Optional.empty(), Optional.of("b"), Optional.empty(), Optional.of("c"));
List<String> collected = optionals.stream()
.flatMap(Optional::stream)
.toList();
System.out.println("flatMap(Optional::stream) over " + optionals.size() + " optionals = " + collected);
Optional<String> a = Optional.of("hello");
Optional<String> b = Optional.of("hello");
Optional<String> c = Optional.of("world");
Optional<String> e1 = Optional.empty();
Optional<String> e2 = Optional.empty();
System.out.println("Optional.of(\"hello\").equals(Optional.of(\"hello\")) = " + a.equals(b));
System.out.println("Optional.of(\"hello\").equals(Optional.of(\"world\")) = " + a.equals(c));
System.out.println("Optional.empty().equals(Optional.empty()) = " + e1.equals(e2));
}
}
Real javac+java output, JDK 25 (Zulu):
flatMap(Optional::stream) over 5 optionals = [a, b, c]
Optional.of("hello").equals(Optional.of("hello")) = true
Optional.of("hello").equals(Optional.of("world")) = false
Optional.empty().equals(Optional.empty()) = true
Takeaway
Across the five runs: of/ofNullable/empty wrap a value, null, or nothing; get()/orElseThrow throw on empty while orElse/orElseGet fall back; ifPresent/ifPresentOrElse ran their callbacks conditionally and or supplied a fallback Optional only when empty; map/flatMap/filter transformed or passed through the wrapped value; and Optional::stream plus equals behaved as their printed output shows — three empties dropped out of five optionals, two empty Optionals equal to each other.