Optional Class : filter, map and flatMap : Filtering and Transforming Optional value.

1. Overview

In this article, we will look at 3 different methods of Optional class. These methods are helpful in taking an action on value, if value is present in Optional. The action can be filtering the value in Optional and/or transforming the value.

This article is part of ongoing series of Optional. Read about it more here.

2. filter(Predicate<? super T> predicate)

Optional is a great way to avoid NullPointerException but the usage of Optional should not be limited to that.

It often happens that once the result is returned it is consumed only if it matches the expectations. This expectations can depend on use-case. Let’s take for instance a String result is returned and should be consumed only if it contains certain CharSequence.

In our example we get a result string. We then check if the result is non null and use contains method to check for our CharSequence. If it does, we log the result else we don’t do anything with it.

Without using Optional :

String result = "abc";

if (result != null && result.contains("abc")) {
	// do something with result
	logger.log(Level.INFO, () -> result);
}

Now let us explore filter method of Optional class. If the value is present and the value matches the specified Predicate then filter method just returns this Optional else it returns empty Optional. filter method will throw NullPointerException if specified Predicate is null.

Using Optional :

Optional<String> optString = Optional.of(result);

optString
	.filter(res -> res.contains("abc"))
	.ifPresent(res -> logger.log(Level.INFO, () -> res));

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

map method in Optional provides a way to transform value in Optional from one type to another. The transformation can be of same type too. This method must be used as post-processing on result values of Optional.

Without using Optional :

String result = "   abc  ";

if (result != null && result.contains("abc")) {
	// do something with result
	LOGGER.log(Level.INFO, () -> result.trim());
}

Using Optional :

Optional<String> optString = ….;

optString
	.filter(res -> res.contains("abc"))
	.map(String::trim)
	.ifPresent(res -> LOGGER.log(Level.INFO, () -> res));

5. flatMap(Function<? super T, Optional<U>> mapper)

flatMap method is used when we already have an Optional object and resulting call to method also returns Optional

Optional<String> optString = …; // some method returns Optional

Optional<Optional<String>> optOptString = 
		optString.map(this::getOutputOpt);
Optional<String> getOutputOpt(String input) {
	return input == null
			? Optional.empty() 
			: Optional.of(input);
}

Optional<Optional<T>> is weird and not user friendly to work with. In order to convert this Optional<Optional<T>> to Optional<T> we need to flatten the Optional structure. We can do this by using flatMap method which accepts Function<? super T, Optional<U>>

The Function accepts the parameter as type T in our case T will be Optional<Optional<T>>. And returns Optional<U>.

Optional<String> flatMap = optString.flatMap(this::getOutputOpt);

6. Conclusion

In this article, we discussed 3 different methods that provides us ways to process a value in Optional. We also discussed under which circumstances this method should be used.

Leave a Reply

Your email address will not be published. Required fields are marked *