Java 8 Optionals pattern

What is an optional?

An optional is a concept that was introduced to represent the case when no result is produced. An optional wraps a value that is returned from a function and now your function can return a <no value> wrapped in an optional. This means that the optional can be empty.

For example consider the following stream which finds the user with max age with name “Jacob”. What if the list of users is empty. In that case we have nothing to return . This is where optionals come handy. It gives a way to specify that the function can return a no value.


List<User> users = new ArrayList<>();


Optional<User> jacob = users
                        .stream()
                            .filter(user -> user.getName().equals("Jacob"))
                                .reduce( MaxAgeHelper::maxAgeUser);
public class MaxAgeHelper {
 public static User maxAgeUser(User user1, User user2){ return user1.getAge() > user2.getAge() ? user1 : user2; } 
}

How can we create optionals?

The main three patterns for creating  optionals are

Optional<User> emptyOptional = Optional.empty();
Optional<User> userOptional = Optional.of(user); //This method will throw a NullpointerException if user is null Optional<User> userOptional = Optional.ofNullable(user); // This will return an empty Optional if user is null

What are common ways of using optional?

  1. This is the most common way an optional is used.
if(userOptional.isPresent())
return userOptional.get();
else
{
....
}

If the user has a default value then the following pattern can be used.


user = userOptional.orElse(User.default());

3. The following has the additional advantage that the default user won’t be built until it is needed. This method is preferred over the previous one.


User user = userOptional.orElseGet(()-> User.default());

What is map/filter/flatmap methods doing in Optional? Can an optional be considered a special stream?

An optional can be considered as a special stream which can hold zero or one element.

Consider the following class




public class TaxCalculator {


    public static Optional applyCoupon(Item item){
        if(item == null)
            return Optional.empty();
        else if(!CouponHelper.isCouponApplicable(item))
            return Optional.of(new Double (item.getPriceAfterDiscount()))
        else
            return Optional.of(new Double(item.getPrice()));
    }


    public static Optional calcuateTaxableAmount(Double price){

        if(price < 0 )
            return Optional.empty();
        else
            return Optional.of(new Double(price * 13 / 100));
    }




}

Now I have a list of items on which I have to calculate taxes. But the functions in TaxCalculator will return Optionals if it cannot apply a coupon or the amount is negative for taxable amount.

I am going to define a lambda Function which takes an item and applies coupon and then calculates taxes for the item.



         Function<Item,Stream<Double>>taxCalculator = (Item account) -> TaxCalculator.applyCoupon(account)
                .flatMap(TaxCalculator::calcuateTaxableAmount)  //map function from Optional and not stream so returns Optional
                .map(amount -> Stream.of(amount))  //Optional<Stream<Double>>
                .orElseGet(() -> Stream.empty());


Here what I did was to convert Optional into a Stream. Now the advantage here is that we don’t have to worry if Item is null or the price is negative. Note that flatMap,map functions are functions of Optional and not from Stream. And each of them returns an Optional.

The flatMap function above will return an Optional and the map would return an Optional<Stream<Double>>.

Now I can apply the function to a stream of items as follows and get the taxes calculated.



        List items = new ArrayList<>();

        List taxes = items.stream().flatMap(taxCalculator).collect(Collectors.toList());

Leave a Reply

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