ifPresent() and ifPresentOrElse()
Run an action only when an Optional holds a value, or pick between two actions, without unwrapping it by hand.
public void ifPresent(Consumer<? super T> action)
public void ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)- Added in Java Java 8
Parameters
| Parameter | Type | Description |
|---|---|---|
$action | Consumer<? super T> | Called with the value when a value is present. |
$emptyAction | Runnable | ifPresentOrElse only: called when no value is present. |
Return value
voidNothing. ifPresent runs action with the value if one is present. ifPresentOrElse runs action with the value if present, otherwise runs emptyAction.
Errors
ifPresent throws NullPointerException if a value is present and action is null. ifPresentOrElse throws NullPointerException if a value is present and action is null, or if no value is present and emptyAction is null.
Changelog
- PHP Java 9ifPresentOrElse(Consumer, Runnable) added.
ifPresent(action) calls the action with the value when there is one and does nothing otherwise. ifPresentOrElse(action, emptyAction), added in Java 9, also runs a Runnable for the empty case. Both return void: they are for doing something, not for computing a result.
findUserEmail(event.userId()).ifPresentOrElse(
to -> mailer.send(to, template),
() -> log.warn("no email for user " + event.userId()));When to use them
Reach for these when the value triggers a side effect: send an email, write an audit entry, apply a field from a PATCH body, add to a list. They replace the if (opt.isPresent()) { use(opt.get()); } block with a single call and keep the value out of scope when it is absent.
When you need a value back, use map with orElse or orElseThrow instead. Because the lambdas are Consumer and Runnable, they cannot return anything, and they cannot reassign a local variable from the surrounding method (locals used in a lambda must be effectively final). People often try to set a String response inside both branches and hit that compile error; that is the signal to switch to map(...).orElse(...).
Mistakes people make
- Expecting
ifPresentto chain. It returnsvoid, so nothing can follow it. - Throwing a checked exception from the action.
Consumer.acceptdeclares none, so it will not compile; wrap it or restructure. - Putting long business logic in the lambdas. If either branch is more than a line or two, extract a method and pass a method reference such as
ifPresent(this::sendWelcomeEmail).
Back-end angle
A PATCH handler that takes a partial JSON body is a good fit: each optional field in the request body is read as an Optional and applied with ifPresent(entity::setX), so fields the client did not send are left alone. One limit: a field sent as JSON null looks the same as a missing one here. If null must mean "clear this field", as in JSON Merge Patch, check whether the key is present instead. Event consumers use ifPresentOrElse to process a message when its target exists and record a skip otherwise, instead of failing the whole batch.
Example
When to use it
- Applying only the fields a client sent in a PATCH request body to an existing entity.
- Sending a notification when a user has an email address on file and logging a skip when not.
- Writing an audit entry or metric only when a lookup in a request handler found a record.
Frequently asked questions
How do I do something if an Optional has a value, and something else if not?
Use ifPresentOrElse(value -> ..., () -> ...), available since Java 9. On Java 8, use ifPresent for the present case and an isPresent() check for the other, or map/orElse if you need a value.
Can I return a value from Optional.ifPresent?
No. ifPresent and ifPresentOrElse return void and take a Consumer and a Runnable, which return nothing. Use map(...).orElse(...) or orElseThrow when you need a result.
Why can't I assign a variable inside ifPresent?
Local variables used inside a lambda must be effectively final, so you cannot reassign them there. Restructure with map and orElse so the expression produces the value instead.
More examples
Applying a partial update (PATCH)
Only the fields present in the body are applied; the others keep their current values. The method references p::setName and p::setCity fit the Consumer parameter directly.
class Profile {
String name = "Ada";
String city = "London";
void setName(String name) { this.name = name; }
void setCity(String city) { this.city = city; }
@Override
public String toString() { return "Profile[name=" + name + ", city=" + city + "]"; }
}
void patch(Profile p, Map<String, String> body) {
Optional.ofNullable(body.get("name")).ifPresent(p::setName);
Optional.ofNullable(body.get("city")).ifPresent(p::setCity);
}
void main() {
Profile p = new Profile();
patch(p, Map.of("city", "Cambridge"));
IO.println(p);
patch(p, Map.of("name", "Ada L."));
IO.println(p);
}
// Output:
// Profile[name=Ada, city=Cambridge]
// Profile[name=Ada L., city=Cambridge]Collecting found items, logging missing ones
The lambdas may call methods on the list because the variable found itself is never reassigned. Unknown SKUs are reported without stopping the loop.
final Map<String, Integer> stock = Map.of("TEE-01", 12, "MUG-02", 0);
Optional<Integer> stockFor(String sku) {
return Optional.ofNullable(stock.get(sku));
}
void main() {
List<String> found = new ArrayList<>();
for (String sku : List.of("TEE-01", "HAT-09", "MUG-02")) {
stockFor(sku).ifPresentOrElse(
qty -> found.add(sku + "=" + qty),
() -> IO.println("unknown sku " + sku));
}
IO.println(found);
}
// Output:
// unknown sku HAT-09
// [TEE-01=12, MUG-02=0]Computing a result: use map, not ifPresentOrElse
Assigning a local variable inside the lambdas fails because captured locals must be effectively final, so the response is built with map and orElse. ifPresentOrElse is kept for the side effect of logging.
final Map<String, String> users = Map.of("u1", "ada");
Optional<String> find(String id) {
return Optional.ofNullable(users.get(id));
}
String respond(String id) {
// String response; find(id).ifPresentOrElse(u -> response = ..., ...) would not compile
return find(id).map(u -> "200 " + u).orElse("404 " + id);
}
void main() {
IO.println(respond("u1"));
IO.println(respond("u2"));
find("u2").ifPresentOrElse(u -> {}, () -> IO.println("log: lookup miss for u2"));
}
// Output:
// 200 ada
// 404 u2
// log: lookup miss for u2
Discussion