Course Content
Stream API
Stream API
Handling Values with the Optional Class
In Java, variables that store objects can hold a null
value. This often leads to NullPointerException
if null
isn't handled properly. These errors make code less reliable and harder to maintain. This is where Optional
comes in.
Think of it as a box—it may contain a value, or it may be empty. Instead of using if
statements to check for null
, you work with this "box" and use convenient methods to safely retrieve the value if it exists.
Syntax and Usage of Optional
The main goal of Optional
is to avoid NullPointerException
by replacing standard if (value != null)
checks with more readable methods.
There are three ways to create an Optional
:
Optional.empty()
– creates an emptyOptional
with no value;Optional.of(value)
– wraps the given object, but only if it's guaranteed to be non-null;Optional.ofNullable(value)
– wraps the object, even if it'snull
, creating either a populatedOptional
or an empty one.
Main
package com.example; import java.util.Optional; public class Main { public static void main(String[] args) { Optional<String> emptyOpt = Optional.empty(); Optional<String> optWithValue = Optional.of("Laptop"); Optional<String> optNullable = Optional.ofNullable(null); System.out.println("Empty Optional: " + emptyOpt); System.out.println("Optional with value: " + optWithValue); System.out.println("Optional with null: " + optNullable); } }
If you pass null
to Optional.of(value)
, the program will throw a NullPointerException
, so ofNullable()
should always be used for potentially empty values.
Retrieving Values from Optional
To extract a value from an Optional
, you can use the get()
method.
Main
package com.example; import java.util.Optional; public class Main { public static void main(String[] args) { Optional<String> optWithValue = Optional.of("Laptop"); System.out.println("Final string: " + optWithValue.get()); } }
Here, get()
returns the actual object stored inside the Optional
. However, if the Optional
is empty (contains null
), calling get()
will throw a NoSuchElementException
.
Checking for a Value
When working with Optional
, you need to check if it contains a value. One way to do this is with isPresent()
, which returns true
if a value is present. However, ifPresent()
is often preferred, as it executes a provided lambda expression only if the value exists.
Main
package com.example; import java.util.Optional; public class Main { public static void main(String[] args) { Optional<String> productOpt = Optional.ofNullable(null); if (productOpt.isPresent()) { System.out.println("Product found: " + productOpt.get()); } else { System.out.println("Product not found."); } // A more concise approach productOpt.ifPresent(product -> System.out.println("Product: " + product)); } }
In the first example, you manually check for a value using isPresent()
before calling get()
. The second example removes the need for an if
statement by using a lambda expression that runs only if the product is present.
Providing a Default Value
Sometimes, when a value is missing, returning an alternative is a good idea. This can be done with orElse()
, which provides a fallback value. If generating the fallback requires computation, orElseGet()
is more efficient, as it only executes the function when necessary.
Main
package com.example; import java.util.Optional; public class Main { public static void main(String[] args) { Optional<String> productOpt = Optional.ofNullable(null); String product = productOpt.orElse("Default product"); System.out.println("Selected product: " + product); String productLazy = productOpt.orElseGet(() -> "Fallback product"); System.out.println("Selected product (lazy): " + productLazy); } }
The difference is that orElse()
always creates the fallback value, even if it's not needed, while orElseGet()
calls the provided function only if Optional
is empty.
Throwing an Exception If Value Is Missing
In some cases, the absence of a value is an error. In such situations, orElseThrow()
can be used to throw an exception.
Main
package com.example; import java.util.Optional; public class Main { public static void main(String[] args) { Optional<String> productOpt = Optional.ofNullable(null); String product = productOpt.orElseThrow(() -> new RuntimeException("Product not found")); System.out.println("Product: " + product); } }
Here, if the Optional
is empty, the program throws a RuntimeException
. This is useful when a missing value represents a critical error.
Transforming Values
Often, an Optional
holds complex objects, but you may only need to work with specific fields. In such cases, you use map()
, which applies a given function if a value is present.
Main
package com.example; import java.util.Optional; public class Main { public static void main(String[] args) { Optional<String> productOpt = Optional.ofNullable("Laptop"); Optional<Integer> nameLengthOpt = productOpt.map(String::length); nameLengthOpt.ifPresent(length -> System.out.println("Product name length: " + length)); } }
If the Optional
is empty, map()
simply returns Optional.empty()
. Otherwise, it applies String::length
and returns an Optional<Integer>
.
Filtering Values
Sometimes, you need to keep a value only if it meets a certain condition. The filter()
method helps by retaining the value if the given predicate returns true
or returning Optional.empty()
if the condition is not met.
Main
package com.example; import java.util.Optional; public class Main { public static void main(String[] args) { Optional<String> productOpt = Optional.of("Laptop"); Optional<String> filteredProductOpt = productOpt.filter(name -> name.length() > 5); filteredProductOpt.ifPresent(name -> System.out.println("Filtered product: " + name)); } }
If the string's length is greater than 5, the value is retained; otherwise, the Optional
becomes empty.
1. What happens if you call get()
on an empty Optional
?
2. What will this code print?
3. What will be the result of the following code?
4. Which method is best for providing a default value that is only computed when needed?
Thanks for your feedback!