πŸš€ UllrichLumina

Difference between OptionalorElse and OptionalorElseGet

Difference between OptionalorElse and OptionalorElseGet

πŸ“… | πŸ“‚ Category: Java

Navigating the nuances of Java’s Optional class can be tricky, especially when deciding between orElse() and orElseGet(). These seemingly similar methods offer distinct approaches to handling absent values, and understanding their differences is crucial for writing clean, efficient, and predictable Java code. Choosing the wrong method can lead to subtle bugs and performance issues. This article delves into the core distinctions between Optional.orElse() and Optional.orElseGet(), providing clear examples and best practices to help you make informed decisions in your development process. Mastering these methods will empower you to write more robust and maintainable code when dealing with potentially missing values.

Understanding Java’s Optional

Introduced in Java 8, Optional is a container object that may or may not contain a non-null value. It provides a powerful mechanism for handling situations where a value might be absent, eliminating the need for null checks and reducing the risk of NullPointerExceptions. Optional encourages developers to explicitly address the possibility of missing values, leading to more robust code.

Before Java 8, the common practice was to return null when a value was not found. This approach often resulted in unexpected NullPointerExceptions. Optional provides a safer alternative, forcing developers to think about what happens when a value is absent.

Optional.orElse(): The Eager Evaluator

The orElse() method provides a default value to return if the Optional is empty. The key characteristic of orElse() is that its argument is always evaluated, regardless of whether the Optional contains a value. This can have performance implications if the default value computation is expensive.

Consider an example where the default value is the result of a database query. With orElse(), the database query would execute even if the Optional already contains a value. This unnecessary computation can significantly impact performance.

Here’s a simple illustration:

String value = Optional.of("Present").orElse(expensiveComputation()); 

Optional.orElseGet(): The Lazy Evaluator

orElseGet() also provides a default value if the Optional is empty, but unlike orElse(), it takes a Supplier as an argument. This Supplier is evaluated only if the Optional is empty. This lazy evaluation makes orElseGet() a more efficient choice when the default value computation is resource-intensive.

Returning to the database query example, using orElseGet() would only execute the query if the Optional is empty. This avoids unnecessary computations and improves performance.

Here’s how it looks in code:

String value = Optional.of("Present").orElseGet(() -> expensiveComputation()); 

When to Use Which Method

The choice between orElse() and orElseGet() depends on the cost of computing the default value. If the computation is cheap and has no side effects, orElse() is often simpler. However, if the computation is expensive or has side effects, orElseGet() is the preferred option due to its lazy evaluation.

  • Use orElse() for simple, inexpensive default values.
  • Use orElseGet() for expensive computations or operations with side effects.

Here’s a table summarizing the key differences:

Feature orElse() orElseGet()
Evaluation Eager Lazy
Argument Value Supplier
Performance Can be inefficient for expensive computations Efficient for expensive computations

Best Practices and Considerations

Understanding the performance implications of each method is vital for writing optimized code. In scenarios with complex calculations or external service calls, orElseGet() becomes essential for maintaining performance. Incorrect usage of orElse() can lead to hidden performance bottlenecks. β€œPremature optimization is the root of all evil” - Donald Knuth. While this quote is true, understanding the nuances of Optional methods is about writing correct and efficient code, not premature optimization.

Here’s an ordered list outlining best practices:

  1. Favor orElseGet() when the default value computation is expensive.
  2. Consider using orElseThrow() to explicitly handle absent values with exceptions when appropriate.
  3. Clearly document the intended behavior when using Optional in your code.

For further reading on Java best practices, check out this resource: Effective Java.

Featured Snippet: orElseGet() is crucial for performance when dealing with expensive computations or operations with side effects because it uses lazy evaluation, executing the supplier only when the Optional is empty, unlike orElse() which always executes.

Infographic comparing orElse and orElseGet

FAQ

Q: Can I use a lambda expression with orElse()?

A: Yes, you can use a lambda expression, but it will still be evaluated eagerly, negating the performance benefits of lazy evaluation.

External Resources:

By understanding the distinctions between orElse() and orElseGet(), and by adhering to best practices, you can write cleaner, more efficient, and less error-prone Java code. Leveraging the power of Optional effectively contributes to more robust applications. Start implementing these strategies today to enhance your Java development skills and build more reliable software. Explore related concepts like Optional.orElseThrow() and other Java 8 features to further improve your coding practices and harness the full potential of modern Java.

Question & Answer :
I am trying to understand the difference between the Optional<T>.orElse() and Optional<T>.orElseGet() methods.

The description for the orElse() method is:

Return the value if present, otherwise return other.

While, the description for the orElseGet() method is:

Return the value if present, otherwise invoke other and return the result of that invocation.

The orElseGet() method takes a Supplier functional interface, which essentially does not take any parameters and returns T.

In which situation would you need to use orElseGet()? If you have a method T myDefault() why wouldn’t you just do optional.orElse(myDefault()) rather than optional.orElseGet(() -> myDefault()) ?

It does not seem that orElseGet() is postponing the execution of the lambda expression to some later time or something, so what’s the point of it? (I would have thought that it would be more useful if it returned a safer Optional<T> whose get() never throws a NoSuchElementException and isPresent() always returns true… but evidently its not, it just returns T like orElse()).

Is there some other difference I am missing?

Short Answer:

  • orElse() will always call the given function whether you want it or not, regardless of Optional.isPresent() value
  • orElseGet() will only call the given function when the Optional.isPresent() == false

In real code, you might want to consider the second approach when the required resource is expensive to get.

// Always get heavy resource getResource(resourceId).orElse(getHeavyResource()); // Get heavy resource when required. getResource(resourceId).orElseGet(() -> getHeavyResource()) 

For more details, consider the following example with this function:

public Optional<String> findMyPhone(int phoneId) 

The difference is as below:

X : buyNewExpensivePhone() called +β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”+β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”+ | Optional.isPresent() | true | false | +β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”+β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”+ | findMyPhone(int phoneId).orElse(buyNewExpensivePhone()) | X | X | +β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”+β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”+ | findMyPhone(int phoneId).orElseGet(() -> buyNewExpensivePhone()) | | X | +β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”+β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”+ 

When optional.isPresent() == false, there is no difference between two ways. However, when optional.isPresent() == true, orElse() always calls the subsequent function whether you want it or not.

Finally, the test case used is as below:

Result:

------------- Scenario 1 - orElse() -------------------- 1.1. Optional.isPresent() == true (Redundant call) Going to a very far store to buy a new expensive phone Used phone: MyCheapPhone 1.2. Optional.isPresent() == false Going to a very far store to buy a new expensive phone Used phone: NewExpensivePhone ------------- Scenario 2 - orElseGet() -------------------- 2.1. Optional.isPresent() == true Used phone: MyCheapPhone 2.2. Optional.isPresent() == false Going to a very far store to buy a new expensive phone Used phone: NewExpensivePhone 

Code:

public class TestOptional { public Optional<String> findMyPhone(int phoneId) { return phoneId == 10 ? Optional.of("MyCheapPhone") : Optional.empty(); } public String buyNewExpensivePhone() { System.out.println("\tGoing to a very far store to buy a new expensive phone"); return "NewExpensivePhone"; } public static void main(String[] args) { TestOptional test = new TestOptional(); String phone; System.out.println("------------- Scenario 1 - orElse() --------------------"); System.out.println(" 1.1. Optional.isPresent() == true (Redundant call)"); phone = test.findMyPhone(10).orElse(test.buyNewExpensivePhone()); System.out.println("\tUsed phone: " + phone + "\n"); System.out.println(" 1.2. Optional.isPresent() == false"); phone = test.findMyPhone(-1).orElse(test.buyNewExpensivePhone()); System.out.println("\tUsed phone: " + phone + "\n"); System.out.println("------------- Scenario 2 - orElseGet() --------------------"); System.out.println(" 2.1. Optional.isPresent() == true"); // Can be written as test::buyNewExpensivePhone phone = test.findMyPhone(10).orElseGet(() -> test.buyNewExpensivePhone()); System.out.println("\tUsed phone: " + phone + "\n"); System.out.println(" 2.2. Optional.isPresent() == false"); phone = test.findMyPhone(-1).orElseGet(() -> test.buyNewExpensivePhone()); System.out.println("\tUsed phone: " + phone + "\n"); } } 

🏷️ Tags: