design-electrical-interior-103587

cropped-button Introduction

Vavr library is intended to “turn Java upside down”.

It is a functional library for Java, which brings you great functional core structures and immutable data types. It is the really nice extension for standard Java 8 functional features.

In this article, I want to introduce you Try control sturcture from Vavr library, which helps us to get rid of try-catch blocks in our code.

I assume you know the basics of functional programming and already know lambda expressions from Java 8.

Let’s start…

cropped-button Checked exceptions problem

First of all – let’s focus on checked exceptions in Java. Those exceptions extend Exception class – like for example IOException.class.

Checked exceptions must be caught in catch statement or rethrowed to caller method.
The fact that compiler forces us to handle checked exceptions makes a lot of developers angry. It leads us to write a lot of boilerplate code with a lot of error-prone try-catch statements.

That’s why checked exceptions do not exist in Scala. That’s why designers of Hibernate library replaced checked exceptions with runtime ones.

Sadly, for now, we have to deal with checked exceptions in Java. When you develop web applications, and you make HTTP calls, I’m sure that at some point you’d countered IOException and you had to handle it.

Can we remove try-catch statements from our code?

Let’s see how Try structure from Vavr can help us with checked exceptions handling…

cropped-button Introduction to Try

The Try control structure is a container for an operation which may be successful or not.

It gives us the ability to write safe code without using try-catch statements.

Result in Try can be either Success or a Failure. What is worth to mention, we can treat unsafe operations and its results as immutable values.

Try which is the container for the result of unsafe operation, can contain success value, or failure exception.

 

To create Try value, we can use static method from Vavrs API class:

Try aTry = API.Try(() -> someUnsafeMethod());

 

But because Vavr library is intended to bring us a little bit of flexible Scala syntax, we can import API as static, and we have more elegant:

Try aTry = Try(() -> someUnsafeMethod());

 

The result of the above line is Try container which contains someUnsafeMethod() success result, or failure exception as an inner value.

cropped-button Let’s Try it

I always learn the most by doing examples, so let’s consider a real case scenario.

Let’s say that we have microservice which process some items. Our goal is to implement a safe method, which makes call to remote storehouse to get additional storehouse data for given item.

We have an contract for fetching stock data:

public interface StorehouseApi {

    /**
     * Calls remote storehouse webservice to get storehouse information for Item.
     */
    ItemStockValue stockValueFor(Item item) throws IOException;
    
}

 

And the interface client looks like this:

public class ItemService {
    
    private final StorehouseApi storehouseApi;

    public Integer fetchQuantityFor(Item item) {
        try {
            ItemStockValue itemStockValue = storehouseApi.stockValueFor(item);
            return itemStockValue.quantity();
        } catch (IOException e) {
            throw new FetchItemException(e);
        }
    }
}

 

As we can see, IOException is handled in a try-catch statement. It is handled in an imperative way – we have two blocks either for success case or failure case.

Let’s bring Try to the party:

    public Integer fetchQuantityFor(Item item) {
        ItemStockValue stockValue = 
                Try(() -> storehouseApi.stockValueFor(item))
                .getOrElseThrow(() -> new FetchItemException("Unable to fetch item"));

        return stockValue.quantity();
    }

 

Great, we’ve replaced try-catch blocks with Try control structure. 🙂

The code that we’ve created still looks too imperative. We have two instructions here – first fetch ItemStockValue, and then return quantity from it.

Because Try is a container for unsafe operation result, it brings us some methods for the internal value manipulation. We have, for instance, map method for inner success value transformation.

The result of map operation is new Try container, with mapped value inside.

Untitled Diagram(3)
map() method apply function which transforms value inside the container. The result is new Try container with transformed value.

 

Applying map() transformation to our code:

    public Integer fetchQuantityFor(Item item) {
        return Try(() -> storehouseApi.stockValueFor(item))
                .map(stockValue -> stockValue.quantity())
                .getOrElseThrow(() -> new FetchItemException("Bad luck :<"));
    }

 

Great! with Try container we’ve made fetchQuantityFor(Item item)  method as pure functional expression.

cropped-button Exploring, exploring, exploring…

Getting to item example, we can, instead of throwing an unchecked exception, return default value:

    public Integer fetchQuantityFor(Item item) {
        return Try(() -> storehouseApi.stockValueFor(item))
                .map(stockValue -> stockValue.quantity())
                .getOrElse(0);
    }

 

We can define success and failure handling, just like in Javascript promises:

    public void addItemToBasket(Item item) {
        return Try(() -> storehouseApi.stockValueFor(item))
                .onSuccess(stockValue -> basket.add(item, stockValue))
                .onFailure(exception -> handleFailure(exception));
    }

 

We can ask container whether there is success or failure:

Try<ItemStockValue> aTry = Try(() -> storehouseApi.stockValueFor(item));
        
boolean isSuccess = aTry.isSuccess();
boolean orMaybeFailure = aTry.isFailure();

 

We can use all functional programming benefits, by combining and chaining unsafe operations executions:

    public void addItemToBasket(Item item) {
         Try(() -> storehouseApi.stockValueFor(item))
         .andThenTry(stockValue -> basketApi.createBasketRecord(item, stockValue))        
         .andThenTry(basketEntry -> storehouseApi.updateQuantityOfItemBasedOn(basketEntry))
         .onFailure(() -> rollback());
    }

 

Got failure after the first unsafe operation? Want to invoke another unsafe operation on failure? No problem:

Try(() -> storehouseApi.stockValueFor(item))
.getOrElseTry(() -> storehouseApi.stockValueFromSecondStorehouse(item));

 

In case you’ve been looking for finaly clause, do not worry:

    public void addItemToBasket(Item item) {
        Try(() -> storehouseApi.stockValueFor(item))
                .onSuccess(stockValue -> basketApi.addToBasket(item, stockValue))
                .onFailure(e -> logError(e))
                .andFinally(() -> cleanUp());
    } 

cropped-button To sum up

In this blog post, we’ve learned the concept of Vavr Try container.

With this control structure, we can deal with checked (or even unchecked) exceptions in Java more gracefully – by using functional expressions.

Try share with us a lot of methods for function chaining and composing so our exception handling can be reduced to one phase – which is less error-prone and more pleasing to the eye.

Enjoy! 🙂