Optional

By the Syncfusion DevTutorials team · Last updated

Handle missing values safely with Optional: create one with of, ofNullable and empty, then use isPresent, ifPresent, orElse and orElseGet.

Think about a parcel locker at a railway station.

You open locker 12. Sometimes there is a parcel inside. Sometimes the locker is empty. Either way, you get a locker. You never get "nothing at all".

Because you always get a locker, you know you must look inside before you take the parcel out.

Java's Optional is like that locker. A method that might not have an answer returns an Optional. It always gives you the box. The box either contains a value or is empty, so the code that receives it must check before using the value.

In simple words, an Optional is a box that holds one value or nothing. It helps you avoid surprises when a value is missing.

What You Will Learn

In this lesson, you will learn:

  1. What Optional is.
  2. Why Optional is used instead of returning null.
  3. How to create an Optional with of, ofNullable and empty.
  4. How to check for a value with isPresent() and isEmpty().
  5. How to run code only when a value exists, with ifPresent().
  6. How to give a fallback value with orElse().
  7. How orElseGet() differs from orElse(), and when to use each.

Start With a Working Example

Press Run.

A search that might not find anything

import java.util.List;
import java.util.Optional;

public class Main {
    static List<String> students = List.of("Asha", "Ravi", "Meena");

    public static void main(String[] args) {
        Optional<String> found = findStartingWith("R");
        Optional<String> missing = findStartingWith("Z");

        System.out.println("Starts with R: " + found.orElse("nobody"));
        System.out.println("Starts with Z: " + missing.orElse("nobody"));
    }

    static Optional<String> findStartingWith(String letter) {
        for (String s : students) {
            if (s.startsWith(letter)) {
                return Optional.of(s);
            }
        }
        return Optional.empty();
    }
}

Expected output:

Starts with R: Ravi
Starts with Z: nobody

The method always returns an Optional. For "R", the box holds Ravi. For "Z", the box is empty.

orElse("nobody") takes the value out of the box, or uses "nobody" when the box is empty. The program does not have to deal with null.

Try This

Change "Z" to "M". The second line now prints Meena, because the box is no longer empty.

What is Optional?

Optional<T> is a class in java.util. An Optional object is a container that holds either:

  • one value of type T, or
  • nothing. This is called an empty Optional.

The type in angle brackets says what the box can hold. Optional<String> might hold a String. Optional<Integer> might hold an Integer.

Optional was added in Java 8. You already met it in the Streams lesson: findFirst() returns an Optional, because a stream might have no elements.

 Optional.of("Ravi")           Optional.empty()
 ┌──────────────┐              ┌──────────────┐
 │   "Ravi"     │              │              │
 └──────────────┘              └──────────────┘
   present                       empty

Optional is mainly meant as a return type. It is a way for a method to say, "I might not have an answer for you".

Why Optional Is Used

Before Optional, a method with no answer usually returned null.

The problem with null is that it looks like a normal value in the method signature. Nothing reminds the caller to check for it. If they forget, the program crashes with a NullPointerException, often far away from where the null came from.

String email = findEmail("Arjun");   // returns null: Arjun has no email
email.toUpperCase();                  // NullPointerException

Optional helps in three ways:

  • The return type tells the truth. Optional<String> clearly says, "this might be empty".
  • The compiler helps. You cannot call toUpperCase() directly on an Optional<String>. You must take the value out first, so you think about the empty case.
  • Empty handling stays short. Methods like orElse can replace longer if (x != null) checks.

null forces a check you can forget, Optional makes it obvious

import java.util.Map;
import java.util.Optional;

public class Main {
    static Map<String, String> emails = Map.of("Asha", "asha@mail.com", "Ravi", "ravi@mail.com");

    public static void main(String[] args) {
        String oldWay = findEmailOrNull("Arjun");
        if (oldWay != null) {
            System.out.println("Old way: " + oldWay.toUpperCase());
        } else {
            System.out.println("Old way: no email");
        }

        String newWay = findEmail("Arjun")
                .map(String::toUpperCase)
                .orElse("no email");
        System.out.println("New way: " + newWay);

        System.out.println("Asha: " + findEmail("Asha").orElse("no email"));
    }

    static String findEmailOrNull(String name) {
        return emails.get(name);
    }

    static Optional<String> findEmail(String name) {
        return Optional.ofNullable(emails.get(name));
    }
}

Expected output:

Old way: no email
New way: no email
Asha: asha@mail.com

The old way needs an if check. Forgetting it would crash the program.

The new way reads as one sentence: find the email, turn it into capitals, or else say "no email". map only runs when there is a value, just like map on a stream.

Optional is designed for return values. Do not use it for fields, method parameters or collections. A list that might have no items should simply be an empty list, not an Optional<List<...>>.

Creating Optionals

There are three ways to create an Optional.

MethodUse whenIf the value is null
Optional.of(value)You are sure the value is not nullThrows NullPointerException
Optional.ofNullable(value)The value might be nullReturns an empty Optional
Optional.empty()You want an empty box on purpose—

of, ofNullable and empty

import java.util.Optional;

public class Main {
    public static void main(String[] args) {
        Optional<String> city = Optional.of("Chennai");
        Optional<String> maybeCity = Optional.ofNullable(null);
        Optional<String> nothing = Optional.empty();

        System.out.println("of: " + city);
        System.out.println("ofNullable(null): " + maybeCity);
        System.out.println("empty: " + nothing);

        String fromDatabase = null;
        Optional<String> safe = Optional.ofNullable(fromDatabase);
        System.out.println("Safe box is empty? " + safe.isEmpty());
    }
}

Expected output:

of: Optional[Chennai]
ofNullable(null): Optional.empty
empty: Optional.empty
Safe box is empty? true

When printed, a full box shows Optional[value], and an empty box shows Optional.empty.

ofNullable is the safe choice when the value comes from somewhere you do not control, like a map lookup or a database.

Optional.of(null) throws a NullPointerException straight away. Use Optional.of only when you are certain the value exists. When in doubt, use Optional.ofNullable.

isPresent()

isPresent() returns true if the box holds a value, and false if it is empty.

isEmpty() (added in Java 11) does the opposite: it returns true if the box is empty.

You can then take the value out with get(). But get() on an empty box throws a NoSuchElementException, so only call it after checking.

Check first, then take the value out

import java.util.Optional;

public class Main {
    public static void main(String[] args) {
        Optional<Integer> marks = Optional.of(87);
        Optional<Integer> absent = Optional.empty();

        System.out.println("marks present? " + marks.isPresent());
        System.out.println("absent present? " + absent.isPresent());
        System.out.println("absent empty? " + absent.isEmpty());

        if (marks.isPresent()) {
            int m = marks.get();
            System.out.println("Marks plus bonus: " + (m + 5));
        }

        if (absent.isPresent()) {
            System.out.println("This line never prints");
        } else {
            System.out.println("No marks recorded");
        }
    }
}

Expected output:

marks present? true
absent present? false
absent empty? true
Marks plus bonus: 92
No marks recorded

This works, but it looks a lot like the old if (x != null) style. The methods in the rest of this lesson are usually shorter and safer.

ifPresent()

ifPresent(action) runs an action only when the box holds a value. If the box is empty, nothing happens.

The action is a Consumer, usually written as a lambda.

ifPresentOrElse(action, emptyAction) (added in Java 9) also lets you say what to do when the box is empty.

Run code only when there is a value

import java.util.Optional;

public class Main {
    public static void main(String[] args) {
        Optional<String> coupon = Optional.of("SAVE20");
        Optional<String> noCoupon = Optional.empty();

        coupon.ifPresent(c -> System.out.println("Applying coupon " + c));
        noCoupon.ifPresent(c -> System.out.println("This never prints"));

        noCoupon.ifPresentOrElse(
                c -> System.out.println("Applying coupon " + c),
                () -> System.out.println("No coupon, full price"));
    }
}

Expected output:

Applying coupon SAVE20
No coupon, full price

noCoupon.ifPresent(...) printed nothing at all, because the box was empty.

Use ifPresent when there is something to do with the value. Use orElse when you need to get a value back.

orElse()

orElse(fallback) returns the value in the box, or the fallback if the box is empty.

It always gives you a real value, so you can use the result straight away.

orElse always gives you something to use

import java.util.Map;
import java.util.Optional;

public class Main {
    static Map<String, Integer> stock = Map.of("Pens", 40, "Bags", 6);

    public static void main(String[] args) {
        int pens = Optional.ofNullable(stock.get("Pens")).orElse(0);
        int shoes = Optional.ofNullable(stock.get("Shoes")).orElse(0);

        System.out.println("Pens in stock: " + pens);
        System.out.println("Shoes in stock: " + shoes);

        String nickname = Optional.<String>empty().orElse("Guest");
        System.out.println("Hello, " + nickname);
    }
}

Expected output:

Pens in stock: 40
Shoes in stock: 0
Hello, Guest

Shoes are not in the map, so the box was empty, and orElse(0) gave 0.

Optional.<String>empty() tells Java the box would hold a String, so orElse("Guest") fits.

orElseGet()

orElseGet(supplier) also returns the value, or a fallback when the box is empty. The difference is how the fallback is made.

  • orElse(fallback) takes a ready-made value. That value is worked out every time, even if the box is full and it is not needed.
  • orElseGet(supplier) takes a Supplier: a lambda that makes the value. The lambda only runs when the box is empty.

This matters when the fallback is expensive to make, such as reading a file or calling a database.

orElse always builds the fallback, orElseGet only when needed

import java.util.Optional;

public class Main {
    public static void main(String[] args) {
        Optional<String> saved = Optional.of("Dark theme");

        System.out.println("Using orElse:");
        String a = saved.orElse(loadDefaultTheme());
        System.out.println("  Result: " + a);

        System.out.println("Using orElseGet:");
        String b = saved.orElseGet(() -> loadDefaultTheme());
        System.out.println("  Result: " + b);

        System.out.println("orElseGet on an empty box:");
        String c = Optional.<String>empty().orElseGet(Main::loadDefaultTheme);
        System.out.println("  Result: " + c);
    }

    static String loadDefaultTheme() {
        System.out.println("  (loading the default theme from disk...)");
        return "Light theme";
    }
}

Expected output:

Using orElse:
  (loading the default theme from disk...)
  Result: Dark theme
Using orElseGet:
  Result: Dark theme
orElseGet on an empty box:
  (loading the default theme from disk...)
  Result: Light theme

With orElse, Java ran loadDefaultTheme() even though the box already held "Dark theme". The work was wasted.

With orElseGet, the lambda was skipped because the box was full. It only ran for the empty box.

orElse(value)orElseGet(supplier)
TakesA ready valueA lambda that makes a value
Fallback is worked outAlwaysOnly when the box is empty
Best forSimple, cheap values like 0, "" or "Guest"Expensive work, like reading a file or a database
Exampleopt.orElse("Guest")opt.orElseGet(() -> loadFromDb())

Easy Way to Remember: orElse for a constant, orElseGet for a calculation.

More Ways to Handle an Empty Box

MethodWhat it does
orElseThrow()Returns the value, or throws NoSuchElementException
orElseThrow(() -> new X(...))Returns the value, or throws your own exception
map(fn)Changes the value, if there is one
filter(rule)Keeps the value only if it passes the rule

Common mistakes

The following mistakes are common when using Optional. Each row shows what you see, why it happens and how to fix it.

SymptomCauseFix
NoSuchElementException: No value presentget() on an empty OptionalUse orElse, orElseGet or ifPresent
NullPointerException when creating an OptionalOptional.of(null)Use Optional.ofNullable
NullPointerException from an Optional variableA method returned null instead of an OptionalReturn Optional.empty()
A slow program, even when values existExpensive work passed to orElseUse orElseGet with a lambda
Code full of isPresent() / get() pairsTreating Optional like a null checkUse ifPresent, map and orElse

What to Remember

  • An Optional is a box that holds one value or nothing.
  • It makes "there might be no answer" clear in a method's return type, and helps avoid NullPointerException.
  • Create one with Optional.of (never null), Optional.ofNullable (maybe null) or Optional.empty().
  • isPresent() and isEmpty() check the box. Only call get() after checking, or better, avoid it.
  • ifPresent() runs code only when there is a value.
  • orElse() gives a fallback that is always worked out. orElseGet() builds the fallback only when the box is empty.
  • Use Optional for return values, not for fields, parameters or collections.

Next: Pattern Matching