🔥 Launch Offer: Interview Preparation Bundle @ ₹2,499 ₹9,999 • 75% OFF. Offer expires in 🔥 Launch Offer: Interview Preparation Bundle @ ₹2,499 ₹9,999 • 75% OFF. Offer expires in Interview Preparation Bundle @ ₹2,499 ₹9,999

00:00:00 Get Bundle
home java 8 java-8-optional-and-exception-handling-in-lambda-expressions-complete-guide
Interview guide Java 8 29 sections

Java 8 Optional and Exception Handling in Lambda Expressions (Complete Guide)

Java 8 introduced many powerful features that changed how Java developers write code. Two of the most important additions are: Optional Lambda Expressions and Streams These features help…

Start reading
By javainterviewprep June 16, 2026 7 min read

Java 8 introduced many powerful features that changed how Java developers write code. Two of the most important additions are:

  • Optional
  • Lambda Expressions and Streams

These features help developers write cleaner, safer, and more readable code.

In this article, we’ll cover:

  • Why Optional was introduced
  • How findById() changed in Spring Data JPA
  • Difference between returning null and Optional
  • orElse(), orElseGet(), and orElseThrow()
  • Exception handling inside lambda expressions
  • Why orElseThrow() cannot replace try-catch in streams
  • Best practices and interview questions

1. The Problem Before Java 8

Before Java 8, methods often returned null when no value was found.

Example:

public User findById(Long id);

Usage:

User user = repository.findById(id);

if (user != null) {
System.out.println(user.getName());
}

If the user was not found:

user == null

If developers forgot the null check:

User user = repository.findById(id);

System.out.println(user.getName());

Output:

NullPointerException

One of the most common runtime exceptions in Java applications was:

java.lang.NullPointerException

This happened because developers forgot to check for null.


2. Java 8 Introduced Optional

Java 8 introduced the Optional<T> class.

Instead of returning:

User

methods could now return:

Optional<User>

An Optional simply means:

“A value may or may not be present.”

Instead of returning null, methods return either:

Optional containing User

or

Optional.empty()

This forces developers to handle missing values explicitly.


3. Spring Data JPA and findById()

Modern Spring Data JPA defines findById() as:

Optional<User> findById(Long id);

Therefore this is correct:

Optional<User> user = repository.findById(id);

But this is incorrect:

User user = repository.findById(id);

Compile Error:

Type mismatch:
cannot convert from Optional<User> to User

4. Getting the Actual Object

Since findById() returns an Optional, we need to extract the value.

Using get()

Optional<User> optional = repository.findById(id);

User user = optional.get();

Problem:

If the value doesn’t exist:

NoSuchElementException

Therefore get() is generally not recommended without checking first.


5. Using isPresent()

Optional<User> optional = repository.findById(id);

if (optional.isPresent()) {

User user = optional.get();

System.out.println(user.getName());

}

Safe, but slightly verbose.


6. Using orElse()

User user =
repository.findById(id)
.orElse(new User());

If user exists:

return database user

Otherwise:

return new User()

7. Using orElseGet()

User user =
repository.findById(id)
.orElseGet(User::new);

Difference from orElse():

orElse() creates the object immediately.

orElseGet() creates it only if needed.


8. Using orElseThrow()

Most common production code:

User user =
repository.findById(id)
.orElseThrow(
() -> new RuntimeException("User not found")
);

If user exists:

return user

Otherwise:

throw RuntimeException

This is one of the most common patterns in Spring Boot applications.


9. Why Optional is Better Than Null

Old approach:

findById()

↓

User

↓

null

Developer may forget to check.

Result:

NullPointerException

Modern approach:

findById()

↓

Optional<User>

↓

Developer must decide

↓

orElse()

orElseThrow()

orElseGet()

ifPresent()

This makes code safer and more expressive.


10. Exception Handling Inside Lambda Expressions

Consider the following list:

List<String> list =
Arrays.asList("1","2","ABC","4");

Suppose we write:

list.forEach(
x -> System.out.println(
Integer.parseInt(x)
)
);

Output:

1
2

Then:

NumberFormatException

The program stops immediately because "ABC" cannot be converted into an integer.


11. Handling Exceptions Inside Lambda

We can handle exceptions using try-catch.

list.forEach(x -> {

try {

System.out.println(
Integer.parseInt(x));

} catch (Exception e) {

System.out.println("Invalid Number");

}

});

Output:

1
2
Invalid Number
4

Notice that the loop continues even after encountering invalid input.


12. Exception Handling in Streams

Example:

List<String> numbers =
Arrays.asList("1","2","A","4");

Bad approach:

numbers.stream()

.map(Integer::parseInt)

.collect(Collectors.toList());

Fails when processing:

"A"

Exception:

NumberFormatException

Entire stream terminates.


13. Production Approach

Handle exceptions inside the lambda.

List<Integer> result =

numbers.stream()

.map(x -> {

try {

return Integer.parseInt(x);

}

catch (Exception e) {

return 0;

}

})

.collect(Collectors.toList());

Output:

1
2
0
4

14. Can We Use orElseThrow() Instead?

Many beginners ask:

Can I replace try-catch with orElseThrow()?

Answer:

No.

Why?

Because:

Integer.parseInt(x)

returns:

int

It does not return:

Optional<Integer>

Since there is no Optional, there is no orElseThrow() available.


15. Why It Doesn’t Work

This is impossible:

numbers.stream()

.map(Integer::parseInt)

.orElseThrow();

Reason:

  • Stream has no orElseThrow() method.
  • Integer.parseInt() returns int, not Optional<Integer>.

Therefore, orElseThrow() cannot replace exception handling in this scenario.


16. Optional Is Not Exception Handling

This is a common misconception.

Optional is designed to represent:

Value exists

or

Value doesn't exist

It is not designed to catch exceptions.

Exceptions and missing values are different concepts.

SituationSolution
Value may not existOptional
Code throws exceptiontry-catch

17. Can We Use Optional in Streams?

Yes.

Example:

List<Integer> result =

numbers.stream()

.map(x -> {

try {

return Optional.of(Integer.parseInt(x));

}

catch (NumberFormatException e) {

return Optional.<Integer>empty();

}

})

.flatMap(Optional::stream)

.toList();

Output:

1
2
4

The invalid value is skipped.

Notice:

We still needed try-catch.

Optional did not eliminate exception handling.


18. When Should You Use orElseThrow()?

Use it when a method returns an Optional.

Example:

User user =

repository.findById(id)

.orElseThrow(
() -> new UserNotFoundException()
);

Perfect use case.


19. When Should You Use try-catch?

Use it when a method throws an exception.

Example:

Integer.parseInt(text);

Possible exception:

NumberFormatException

Use:

try {

Integer.parseInt(text);

}

catch(NumberFormatException e){

}

20. Comparison Table

FeatureOptionaltry-catch
PurposeHandle missing valuesHandle exceptions
IntroducedJava 8Java 1.0
Used withOptional<T>Any exception-throwing code
ExamplefindById()Integer.parseInt()
Common MethodsorElse(), orElseGet(), orElseThrow(), ifPresent()try, catch, finally

Best Practices

✔ Return Optional<T> when a value may legitimately be absent.

✔ Prefer orElseThrow() in service-layer code when the absence of a value is an error.

✔ Use orElseGet() instead of orElse() if creating the default value is expensive.

✔ Handle exceptions inside lambda expressions if you want processing to continue.

✔ Don’t use Optional.get() without checking for a value first (or use a safer alternative).

✔ Don’t use Optional as a replacement for exception handling.

✔ Keep lambda expressions simple—if exception handling becomes complex, extract the logic into a separate method.


Frequently Asked Interview Questions

1. Why does findById() return Optional<User>?

To avoid returning null and forcing developers to explicitly handle the “not found” case, reducing the risk of NullPointerException.


2. What is the difference between orElse() and orElseGet()?

  • orElse() always creates or evaluates the default value.
  • orElseGet() creates it only when the Optional is empty, making it more efficient for expensive object creation.

3. When should you use orElseThrow()?

When the absence of a value is considered an exceptional situation and you want to fail fast with a specific exception.


4. Can orElseThrow() replace try-catch?

No. orElseThrow() handles empty Optional values, whereas try-catch handles exceptions thrown during code execution.


5. Why does Integer.parseInt() require try-catch?

Because it throws a NumberFormatException for invalid input instead of returning an Optional<Integer>.


Conclusion

Java 8 significantly improved the way developers write code by introducing Optional and lambda expressions. Optional provides a safer alternative to returning null, encouraging developers to handle missing values explicitly through methods such as orElse(), orElseGet(), and orElseThrow(). Meanwhile, lambda expressions and streams enable concise data processing but still require traditional try-catch blocks when dealing with operations that can throw exceptions.

The key takeaway is that Optional and exception handling solve different problems. Use Optional to represent the possible absence of a value, and use try-catch to handle exceptional conditions during execution. Understanding this distinction will help you write cleaner, safer, and more maintainable Java applications, and it is a concept frequently tested in Java and Spring Boot interviews.