Guide To Java 8 Optional

Java 8 introduced the Optional class to handle nullable values more effectively and concisely. It eliminates the need for explicit null checks and provides a safer and cleaner way to deal with optional values.

Key Features of Optional

  • Optional ensures that the value is present before performing operations on it.
  • It avoids NullPointerExceptions and makes code more readable and maintainable.
  • It provides various methods for handling optional values, including checking presence, retrieving value, and applying transformations.

1. Creating Optional Objects

There are several ways of creating Optional objects.

a. Optional.empty(): To create an empty Optional object, we simply need to use its empty() static method.

				
					Optional<String> emptyOptional = Optional.empty();
				
			

b. Optional.of(T value): Creates an Optional containing the specified non-null value.

				
					Optional<String> optionalValue = Optional.of("Hello, World!");
				
			

c. Optional.ofNullable(T value): Creates an Optional containing the specified value, or an empty Optional if the value is null.

				
					String nullableValue = getNullableValue();
Optional<String> optionalNullable = Optional.ofNullable(nullableValue);

				
			

2. Accessing the Value

a. T get(): Gets the value if present, otherwise throws NoSuchElementException.

				
					Optional<String> optionalValue = Optional.of("Hello, World!");
String value = optionalValue.get();
System.out.println(value); // Output: Hello, World!

				
			

b. T orElse(T other): Returns the value if present, otherwise returns the specified default value.

				
					Optional<String> optionalValue = Optional.empty();
String result = optionalValue.orElse("Default Value");
System.out.println(result); // Output: Default Value

				
			

c. T orElseGet(Supplier<? extends T> other): Returns the value if present, otherwise invokes the supplier and returns the result of that invocation.

				
					Optional<String> optionalValue = Optional.empty();
String result = optionalValue.orElseGet(() -> "Default Value");
System.out.println(result); // Output: Default Value

				
			

d. T orElseThrow(Supplier<? extends X> exceptionSupplier): Returns the value if present, otherwise throws an exception produced by the provided supplier.

				
					Optional<String> optionalValue = Optional.empty();
String result = optionalValue.orElseThrow(() -> new NoSuchElementException("No value present"));

				
			

3. Difference Between orElse and orElseGet()

orElse(): The orElse() method takes a parameter as the default value to return if the Optional object is empty. It always evaluates the expression passed to it, regardless of whether the Optional object contains a value or not. This can lead to unnecessary computations if the Optional object is not empty. Here’s an example:

				
					import java.util.Optional;

public class OrElseExample {
    public static void main(String[] args) {
        Optional<String> optionalValue = Optional.ofNullable(getValueFromExternalSource());
        String result = optionalValue.orElse("Default Value");

        System.out.println("Result: " + result);
    }

    private static String getValueFromExternalSource() {
        // Simulating a method that may return null
        return null;
    }
}

				
			

In this example, if getValueFromExternalSource returns null, the orElse method will provide the default value “Default Value.”

orElseGet Method: The orElseGet method is similar to orElse but with a key difference. Instead of taking a direct default value, it takes a Supplier as a parameter. A Supplier is a functional interface representing a supplier of results. The Supplier is lazily evaluated, meaning it is only invoked when the optional value is not present. This can be more efficient if creating the default value is expensive or has side effects. Here’s an example:

				
					import java.util.Optional;

public class OrElseGetExample {
    public static void main(String[] args) {
        Optional<String> optionalValue = Optional.ofNullable(getValueFromExternalSource());
        String result = optionalValue.orElseGet(() -> generateDefaultValue());

        System.out.println("Result: " + result);
    }

    private static String getValueFromExternalSource() {
        // Simulating a method that may return null
        return null;
    }

    private static String generateDefaultValue() {
        System.out.println("Generating default value");
        return "Default Value";
    }
}

				
			

In this example, if getValueFromExternalSource returns null, the orElseGet method will invoke the generateDefaultValue method only when needed.

Performance Implications: The orElseGet() method is generally more performant than the orElse() method, as it avoids unnecessary computations if the Optional object is not empty. This is especially important if the default value provider is an expensive operation.

When to Use Each Method: Use orElse() when you already have a default value readily available and don’t need to perform any additional computations to generate the default value. Use orElseGet() when you need to perform computations to generate the default value, especially if those computations are expensive.

4. Checking Value Presence: isPresent() and isEmpty()

When we have an Optional object returned from a method or created by us, we can check if there is a value in it or not with the isPresent() method:

a. boolean isPresent(): Returns true if there is a value present, otherwise false.
				
					Optional<String> optionalValue = Optional.of("Hello, World!");
if (optionalValue.isPresent()) {
    System.out.println("Value is present!");
}

				
			

b. void ifPresent(Consumer<? super T> consumer): If a value is present, perform the given action with the value, otherwise does nothing.

				
					Optional<String> optionalValue = Optional.of("Hello, World!");
optionalValue.ifPresent(val -> System.out.println("Value: " + val));

				
			

c. public boolean isEmpty(): Also, as of Java 11, we can do the opposite with the isEmpty method. The isEmpty() method is an alternative to the isPresent() method, which also checks whether or not an Optional object contains a value. However, the isEmpty() method is more concise and easier to read.

				
					Optional<String> name = Optional.empty();

if (name.isEmpty()) {
    System.out.println("Name is empty");
} else {
    System.out.println("Name is present: " + name.get());
}

				
			

5. Transformations

a.Transforming Value With map(): If a value is present, apply the provided mapping function, otherwise returns an empty Optional.

SyntaxOptional<U> map(Function<? super T, ? extends U> mapper)

				
					Optional<String> optionalValue = Optional.of("Hello, World!");
Optional<Integer> length = optionalValue.map(String::length); //Optional[13]


				
			

b. Transforming Value With flatMap(): If a value is present, apply the provided Optional-bearing mapping function, otherwise returns an empty Optional.

Syntax: Optional<U> flatMap(Function<? super T, Optional<U>> mapper)

				
					Optional<String> optionalValue = Optional.of("Hello, World!");
Optional<String> result = optionalValue.flatMap(val -> Optional.of(val.toUpperCase())); //Optional[HELLO, WORLD!]



				
			

6. Filtering

a. Optional<T> filter(Predicate<? super T> predicate): If a value is present and the value matches the given predicate, returns an Optional describing the value, otherwise returns an empty Optional.

				
					Optional<String> optionalValue = Optional.of("Hello, World!");
Optional<String> filteredValue = optionalValue.filter(val -> val.startsWith("Hello")); //Optional[Hello, World!]


				
			

Conclusion

The Optional class is a powerful tool for handling nullable values in Java. It provides a concise and expressive way to deal with optional values, making code more readable, maintainable, and less prone to NullPointerExceptions. By embracing the use of Optional, you can write more robust and efficient Java code.