isPresent() and isEmpty()

Test whether an Optional holds a value, and know when a plain boolean check is right and when map or orElse reads better.

Signature
public boolean isPresent()
public boolean isEmpty()
  • Added in Java Java 8

Return value

booleanisPresent() returns true if a value is present, otherwise false. isEmpty() returns the opposite: true if no value is present.

Errors

Neither method throws.

Changelog

  • PHP Java 11isEmpty() added as the negation of isPresent().

isPresent() returns true when the Optional holds a value. isEmpty(), added in Java 11, returns true when it does not. They are exact opposites, and isEmpty() exists only so you can write if (result.isEmpty()) instead of if (!result.isPresent()).

When a boolean is what you want

Sometimes the question really is yes or no: does this email already have an account, does this order exist so a HEAD request can return 200. Then repository.findByEmail(email).isPresent() is clear and honest, though if the repository has an existsByEmail or existsById query, that avoids loading the whole row. For signups, the check gives a friendly 409, but only a unique constraint in the database stops two concurrent requests both getting through. A guard clause is another good fit: check isEmpty(), return a 404 early, and carry on with the value below.

Optional<Order> order = orders.findById(id);
if (order.isEmpty()) {
    return notFound(id);
}
Order o = order.orElseThrow(); // safe: checked above

The pattern to avoid

The most common misuse is treating Optional like a nullable reference with extra steps:

if (user.isPresent()) {
    return user.get().name();
} else {
    return "anonymous";
}

That is a null check in disguise, and it is easy to call get() without the check in a later edit. The same logic in one expression is user.map(User::name).orElse("anonymous"). Use map, orElse, orElseThrow or ifPresent when you want to do something with the value; use isPresent() and isEmpty() when you only need the answer to "is there one".

Other mistakes

  • Calling isPresent() on a variable that might itself be null. If that can happen, the method that produced it is broken; fix it to return Optional.empty().
  • Fetching the same Optional twice, once to check and once to use, which doubles the database query. Keep the result in a local variable.
  • Assuming isEmpty() exists on old code bases. It needs Java 11 or later.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Rejecting a signup with 409 Conflict when a lookup by email finds an existing account.
  • Returning 404 early from a handler with an isEmpty() guard before working with the loaded entity.
  • Answering a HEAD request or an exists endpoint where only the presence of the resource matters.

Frequently asked questions

What is the difference between isPresent and isEmpty in Java?

They are opposites. isPresent() returns true when the Optional holds a value; isEmpty() returns true when it does not. isEmpty() was added in Java 11 to avoid writing !isPresent().

Is isPresent() followed by get() bad practice?

It is usually discouraged because it is just a null check with more ceremony, and get() can be called without the check by mistake. Prefer map, orElse, orElseThrow or ifPresent when you want to use the value.

More examples

A guard clause in a request handler

isEmpty() handles the missing case first and returns, so the rest of the method works with a real Order. orElseThrow() after the check reads better than get() and can never throw here.

Example · java
record Order(String id, String status) {}

final Map<String, Order> orders = Map.of(
        "A1", new Order("A1", "PAID"),
        "A2", new Order("A2", "SHIPPED"));

Optional<Order> find(String id) {
    return Optional.ofNullable(orders.get(id));
}

String cancel(String id) {
    Optional<Order> order = find(id);
    if (order.isEmpty()) {
        return "404 order " + id + " not found";
    }
    Order o = order.orElseThrow();
    if (o.status().equals("SHIPPED")) {
        return "409 order " + id + " already shipped";
    }
    return "200 cancelled " + id;
}

void main() {
    IO.println(cancel("A1"));
    IO.println(cancel("A2"));
    IO.println(cancel("B7"));
}
// Output:
// 200 cancelled A1
// 409 order A2 already shipped
// 404 order B7 not found

A pure existence check before signup

Here the value itself is not needed, only whether it exists, so isPresent() is the right tool. Stream.findFirst already returns an Optional.

Example · java
final List<String> registered = List.of("[email protected]", "[email protected]");

Optional<String> findByEmail(String email) {
    return registered.stream().filter(e -> e.equalsIgnoreCase(email)).findFirst();
}

String signUp(String email) {
    if (findByEmail(email).isPresent()) {
        return "409 email already registered";
    }
    return "201 created " + email;
}

void main() {
    IO.println(signUp("[email protected]"));
    IO.println(signUp("[email protected]"));
}
// Output:
// 409 email already registered
// 201 created [email protected]

Replacing isPresent() plus get()

Both versions give the same output, but the map/orElse form has no get() call that a later edit could separate from its check. Optional parameters are used here only to keep the demo short.

Example · java
record User(String name) {}

String greetingOld(Optional<User> user) {
    if (user.isPresent()) {
        return "Hello, " + user.get().name();
    } else {
        return "Hello, guest";
    }
}

String greetingNew(Optional<User> user) {
    return user.map(u -> "Hello, " + u.name()).orElse("Hello, guest");
}

void main() {
    Optional<User> ada = Optional.of(new User("Ada"));
    Optional<User> nobody = Optional.empty();
    IO.println(greetingOld(ada) + " | " + greetingNew(ada));
    IO.println(greetingOld(nobody) + " | " + greetingNew(nobody));
}
// Output:
// Hello, Ada | Hello, Ada
// Hello, guest | Hello, guest

Discussion

  • Be the first to comment on this lesson.