• Linas Naginionis
Software Craftsmanship

Optional type and monads

Optional type and monads

We all need to write some validation code from time to time. Usually this logic is implemented using if-else statements. Suppose our method expects Document entity as an argument and if customerId is not set, we should throw an exception.  Sounds simple at first, but what if our customerId is not present in our main Document entity but it is declared in separate Customer sub-entity with a given hierarchy: Document -> CustomerDocument -> Customer -> customerId. At first, we try to validate customerId using imperative style:

public void processDocument(Document document) {
    if (document == null) {
        throw new IllegalArgumentException("Document should exist");
    }

    if (document.customerDocument == null) {
        throw new IllegalArgumentException("Customer document should exist");
    }

    if (document.customerDocument.customer == null) {
        throw new IllegalArgumentException("Customer should exist");
    }

    if (document.customerDocument.customer.customerId == null) {
        throw new IllegalArgumentException("CustomerId should exist");
    }

    doSomethingWithCustomerId(document.customerDocument.customer.customerId);
}

Notice that we need to check if all sub-entities are not null just to be sure that it is safe to use customerId value and we don’t get NullPointerException in the middle of something. But why do we need to do this? We just need to call doSomethingWithCustomerId if customerId exists or throw exception if it’s null! How could we solve this in better way?

Optional<T>

The biggest features in Java 8 of course are lambdas and stream APIs. But Java 8 also introduced Optional<T> class which could help us to do our validation code and get rid of NullPointerExceptions more easily. If you are familiar with functional programming, you should know that Optional<T> is a monad. There are many different cases where monads are useful. We can try to write the same logic using Optional<T> and functional paradigm:

public void processDocument(Document document) {
    Optional.ofNullable(document)
            .map(doc -> doc.customerDocument)
            .map(customerDoc -> customerDoc.customer)
            .map(customer -> customer.customerId)
            .orElseThrow(() -> new IllegalArgumentException("CustomerId should exist"));

    doSomethingWithCustomerId(document.customerDocument.customer.customerId);
}

This is so much better! No need for null checks! No need to define some intermediate exceptions! We don’t care if CustomerDocument or Customer is not assigned, we are just building a pipeline, which will throw an exception if the mapping for one of the steps did produce unassigned value. If customerId is assigned then our doSomethingWithCustomerId method will be called. Simple as that.

3 thoughts on “Optional type and monads

  1. Stefan Glienke

    Unfortunately the syntax sucks compared to the null propagating operator introduced in C# 6.0.

    There the code would look like this:

    if (document?.customerDocument?.customer?.customerId == null)
    throw new IllegalArgumentException(“CustomerId should exist”);

    1. Linas Post author

      I agree that C# syntax is better but since Java also has primitive types, null propogating operator could introduce some inconsistencies to the language.

Leave a Reply

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

*