What are the three ways to create an Optional in Java, and when do you use each?
answer
- of = non-null, throws NPE on null
- ofNullable = safe wrapper, empty on null
- empty = deliberate no-value (shared singleton)
- of(map.get(key)) is the classic bug → use ofNullable
basics
~10 sOptional.of(x) wraps a value you know is not null; Optional.ofNullable(x) wraps a value that might be null and gives empty if it is null; Optional.empty() makes an empty Optional with no value.
solid answer
~40 sThere are three factory methods. Optional.of(value) creates an Optional holding the value, but throws NullPointerException immediately if you pass null, so use it only when you are certain the value is non-null. Optional.ofNullable(value) is the safe wrapper: if the argument is non-null it behaves like of, and if it is null it returns an empty Optional, so it's what you reach for around legacy or nullable sources such as a map lookup or a nullable field. Optional.empty() returns a shared empty instance representing no value, used as a base case or when you explicitly want to signal absence. The rule of thumb: of when non-null is guaranteed (fail fast on a bug), ofNullable when null is possible, empty for the deliberate no-value case.
code
java · 9 linesString known = "hello";
Optional<String> a = Optional.of(known); // holds "hello"
Optional<String> b = Optional.ofNullable(null); // empty, no exception
Optional<String> c = Optional.empty(); // empty
// The classic bug: Map.get can return null
Map<String, String> m = new HashMap<>();
// Optional<String> wrong = Optional.of(m.get("x")); // throws NPE
Optional<String> right = Optional.ofNullable(m.get("x")); // emptygo deeper
Name the three factories and state of throws on null, ofNullable is safe, empty is the no-value case.
Explain the choose-by-intent rule and identify Optional.of(map.get(key)) as a bug, with ofNullable as the fix.
Discuss fail-fast semantics of of (surfacing bugs at the source), empty() being a cached singleton, and Optional as a value-based, immutable type.
Frame Optional as an API-design tool for making absence explicit at type boundaries; advise of for invariants/postconditions and ofNullable at integration seams with nullable/legacy code.
## What an Optional is In Java, a variable of a reference type (like `String` or `Customer`) can hold either an object or the special value `null`, meaning "nothing here." Calling a method on `null` throws a `NullPointerException` (NPE) at runtime. `Optional<T>` is a small container (a box) that holds *either* one value of type `T` or *nothing*. Its purpose is to make "this might be absent" visible in the type itself, so callers are reminded to handle the empty case instead of forgetting and hitting an NPE. An Optional is **immutable** (you can't change what's inside once created) and is a **value-based class** (don't synchronize on it or compare with `==`). ## The three factory methods (constructors) You never use `new Optional(...)`; the constructor is private. You create one through three static factory methods: 1. **`Optional.of(value)`** — Creates an Optional that contains `value`. If `value` is `null`, it throws a `NullPointerException` *right then*, on the line that calls `of`. Use this only when you are sure the value is non-null. Throwing early is actually helpful: it surfaces a programming bug at its source rather than letting a `null` travel deep into your code. 2. **`Optional.ofNullable(value)`** — The defensive version. If `value` is non-null it behaves exactly like `of`. If `value` is `null` it returns an *empty* Optional instead of throwing. This is the method you use whenever the source could legitimately be null: a `Map.get`, a field that may be unset, a result from older code that uses null to mean "not found." 3. **`Optional.empty()`** — Returns an Optional containing nothing. Internally Java returns a single shared empty instance (a cached singleton), so this is cheap. Use it as a base case, a default, or when you deliberately want to express "no value." ## How to choose - You **know** it's non-null (e.g., you just constructed it): use `of`. You *want* an NPE if it's somehow null, because that would mean a bug. - It **might** be null: use `ofNullable`. This is the single most common safe choice when bridging nullable code into the Optional world. - You want an explicit empty: use `empty()`. ## Common mistake Writing `Optional.of(map.get(key))` is a frequent bug: `Map.get` returns `null` when the key is missing, so `of` throws an NPE — exactly the situation Optional was meant to prevent. The correct call is `Optional.ofNullable(map.get(key))`. ## Tiny example ```java String known = "hello"; Optional<String> a = Optional.of(known); // holds "hello" Optional<String> b = Optional.ofNullable(null); // empty, no throw Optional<String> c = Optional.empty(); // empty // Optional.of(null); // throws NullPointerException ``` With these three you can wrap any source — guaranteed-present, maybe-null, or deliberately-absent — into the same container type, and downstream code handles all of them uniformly.
- Why does Optional.of throw on null instead of just returning empty like ofNullable?Because of expresses the contract "this value must be present." If it's null, that's a programming error, and failing fast at the call site makes the bug easy to locate. ofNullable exists precisely for the case where null is a legitimate possibility.
- Is Optional.empty() a new object each time?No. It returns a single shared, cached empty instance, so calling it repeatedly is cheap and creates no garbage.
Think of three ways to fill a single-item box: of insists the box is never empty and shouts if you try to put nothing in; ofNullable quietly leaves the box empty if you hand it nothing; empty hands you an already-empty box.
saying these in an interview costs you the question
- Saying Optional.of(null) returns an empty Optional — it throws NPE
- Using Optional.of on a value that could be null (e.g. map.get) when ofNullable is needed
- Thinking you create an Optional with new Optional<>() — the constructor is private
- Claiming all three factories are interchangeable