{{announcement.body}}
{{announcement.title}}

Using Java Optional Vs. Vavr Option

DZone 's Guide to

Using Java Optional Vs. Vavr Option

Learn more about implementing Java Optionals, as well at the Vavr library's alternative, Option.

· Java Zone ·
Free Resource

Java Optionals

When it comes to Java, there are so many Options...

Today, I would like to discuss an essential Java topic – the usage of Optional class — and compare it with an alternative from the Vavr library. Optional was initially introduced in Java 8 and defined as “a container object which may or may not contain a non-null value.”

You may also like: 26 Reasons Why Using Optional Correctly Is Not Optional

Developers utilize Optionals in order to avoid null checking in places when code execution leads to "not a result" but also to a null value, and it can, in this regard, result in a NullPointerException. In such cases, Optional offers us some fancy functionality, but not all of it was introduced in the 8th release, some features require Java 11. Another way to handle these issues is with Vavr’s Option class.

In this post, we learn how to use Java’s Optional class, and then compare it to Vavr’s Option class. Note: this code requires Java 11 + and was tested with Vavr 0.10.2.

Let's get started.

Introducing Java Optional

The concept of Optional is not new and has been already implemented in functional programming languages like Haskell or Scala. It proves to be very useful when modeling cases when a method call could return an unknown value or a value that does not exist (e.g. nulls). Let's see how to handle it.

Creation of Optional

First things first, we need to obtain an Optional’s instance. There are several ways to do it – and moreover, we also can create an empty Optional. Check out the first method – creating from value, it is pretty straightforward:

Optional<Integer> four = Optional.of(Integer.valueOf(4));
if (four.isPresent){
System.out.println("Hoorayy! We have a value");
} else {
System.out.println("No value");
}


We build an Optional from an Integer value of 4, meaning that there always should be a value and it cannot be null, but this is just an example. We check an existence or absence of value with the ifPresent() method. You can note that four is not an Integer; it is a container that holds integer inside. When we are sure that the value is inside, we can “unpack” it with the get() method. Ironically, if we use get() without checking, we can end it with NoSuchElementException.

Another way to obtain an Optional is using streams. Several terminal stream’s methods return Optionals, so we can manipulate them and check their existence or absence, for example:

  • findAny 
  • findFirst 
  • max 
  • min 
  • reduce 

Check out the code snippet below:

Optional<Car> car = cars.stream().filter(car->car.getId().equalsIgnoreCase(id)).findFirst();


The next option is to create Optional from code that can potentially produce null, e.g. from Nullable:

Optional<Integer> nullable = Optional.ofNullable(client.getRequestData());


Finally, we can create an empty Optional:

Optional<Integer> nothing = Optional.empty();


How to Use Optional

As long as we obtained Optional, we can use it. One of the most widespread cases is using it in Spring repositories to find one record by Id, so we can build our logic on Optional and avoid null checking (btw, Spring also supports Vavr Options). Let's say we have a book repository and want to find one book.

Optional<Book> book = repository.findOne("some id");


First of all, we can execute some logic, in this case, if the book is presented. We did it with if-else in the previous section, but we don’t need to: Optional provides us a method that accepts a Consumer with the object:

repository.findOne("some id").ifPresent(book -> System.out.println(book));


Or, we can make this even simpler; we can write the same with method references:

repository.findOne("some id").ifPresent(System.out::println);


If we don’t have a book in the repository, we can provide an alternative callback with theifPresentOrElseGet method:

repository.findOne("some id").ifPresentOrElseGet(book->{
// if value is presented
}, ()->{
// if value is absent
});


Alternatively, we can get another value if the result of the operation is not presented:

Book result = repository.findOne("some id").orElse(defaultBook);


However, with Optionals, we need to remember possible drawbacks. In the last example, we “guarantee” ourselves that we could obtain a book anyway; either it presents the underlying repository or comes from orElse. But what if this default value is not constant, but also requires some complex method? First, Java anyway evaluates findOne. Then, it has to process the orElse method. Yes, if it is just a default constant value, it is OK, but it can be, as I said before, time-consuming as well.

Another Example

Let's create a simple example to check how to practically use Optional and Option classes. We would have a CarRepository that would find a car based on the supplied ID (e.g. on the license plate number). Then, we would see how to manipulate Optionals and Options.

First, Let's Add Some Code

Start with the POJO class Car. It follows the immutable pattern, so all fields are final and we have only getters without setters. All data is supplied during initialization.

public class Car {

    private final String name;
    private final String id;
    private final String color;

    public Car (String name, String id, String color){
        this.name = name;
        this.id = id;
        this.color = color;
    }

    public String getId(){
        return id;
    }

    public String getColor() {
        return color;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Car "+name+" with license id "+id+" and of color "+color;
    }
}


The second thing is to create the CarRepository class. It requires two options for finding car by Id — using the old way with a possible null result and using Optional, as we do in Spring repositories.

public class CarRepository {

    private List<Car> cars;

    public CarRepository(){
       getSomeCars();
    }

    Car findCarById(String id){
        for (Car car: cars){
            if (car.getId().equalsIgnoreCase(id)){
                return car;
            }
        }
        return null;
    }

    Optional<Car> findCarByIdWithOptional(String id){
        return cars.stream().filter(car->car.getId().equalsIgnoreCase(id)).findFirst();
    }

    private void getSomeCars(){
        cars = new ArrayList<>();
        cars.add(new Car("tesla", "1A9 4321", "red"));
        cars.add(new Car("volkswagen", "2B1 1292", "blue"));
        cars.add(new Car("skoda", "5C9 9984", "green"));
        cars.add(new Car("audi", "8E4 4321", "silver"));
        cars.add(new Car("mercedes", "3B4 5555", "black"));
        cars.add(new Car("seat", "6U5 3123", "white"));
    }
}


Note that we also populate our repository with some mock cars during initialization, so we don’t have any underlying database. We need to avoid complexities, so let's concentrate on Optional and Option, not on repositories.

Finding Cars With Java Optional

Create a new test with JUnit:

@Test
void getCarById(){
    Car car = repository.findCarById("1A9 4321");
    Assertions.assertNotNull(car);
    Car nullCar = repository.findCarById("M 432 KT");
    Assertions.assertThrows(NullPointerException.class, ()->{
        if (nullCar == null){
            throw new NullPointerException();
        }
    });
}


The above code snippet demonstrates the old way. We found a car with the Czech license plate 1A9 4321 and checked that it exists. Then we found a car that is absent, as it has a Russian license plate and we only have Czech ones in our repository. It is null, so it may lead to NullPointerException.

Next, let's move to Java Optionals. The first step is to obtain an Optional instance from the repository using a dedicated method that returns Optional:

@Test
void getCarByIdWithOptional(){
    Optional<Car> tesla = repository.findCarByIdWithOptional("1A9 4321");
    tesla.ifPresent(System.out::println);
}


In this case, we use the findCarByIdWithOptional method and then print a car (if it presents). If you run it, you will get the following output:

Car tesla with license id 1A9 4321 and of color red


But what if we don’t have that special method in our code? In this situation, we can obtain Optional from a method that could potentially return a null value. It is called nullable.

Optional<Car> nothing = Optional.ofNullable(repository.findCarById("5T1 0965"));
Assertions.assertThrows(NoSuchElementException.class, ()->{
    Car car = nothing.orElseThrow(()->new NoSuchElementException());
});


In this code snippet, we found another way. We create Optional from findCarById and that can return null if no car is found. We manually use the orElseThrow method to throw a NoSuchElementException when the desired car with the license plate 5T1 0965 is present. Another situation is to use orElse with a default value if the requested data is not available in the repository:

Car audi = repository.findCarByIdWithOptional("8E4 4311")
            .orElse(new Car("audi", "1W3 4212", "yellow"));
   if (audi.getColor().equalsIgnoreCase("silver")){
     System.out.println("We have silver audi in garage!");
   } else {
     System.out.println("Sorry, there is no silver audi, but we called you a taxi");
}


Ok, we don’t have the silver Audi in our garage, so we have to call a taxi!

Finding Cars With the Vavr Option

Vavr Option is another way to handle these tasks. First, install Vavr in your project with dependency management (I use Maven):

<dependency>
   <groupId>io.vavr</groupId>
   <artifactId>vavr</artifactId>
   <version>0.10.2</version>
</dependency>


In a nutshell, Vavr has similar APIs to create Option instances. We can create Option from nullable, as shown here:

Option<Car> nothing = Option.of(repository.findCarById("T 543 KK"));


Or we can create an empty container with thenone static method:

Option<Car> nullable = Option.none();


Also, there is a way to create Option from Java Optional! Take a look at the code snippet below:

Option<Car> result = Option.ofOptional(repository.findCarByIdWithOptional("5C9 9984"));


With Vavr Option, we can use the same API as with Optional to accomplish the previously mentioned tasks. For example, we can set a default value in a similar manner:

Option<Car> result = Option.ofOptional(repository.findCarByIdWithOptional("5C9 9984"));
Car skoda = result.getOrElse(new Car("skoda", "5E2 4232", "pink"));
System.out.println(skoda);


Or we can throw an exception based on an absence of requested data:

Option<Car> nullable = Option.none();
Assertions.assertThrows(NoSuchElementException.class, ()->{
nullable.getOrElseThrow(()->new NoSuchElementException());
});


Alternatively, we can perform an action when data is unavailable:

nullable.onEmpty(()->{
///runnable
});


What if we need to perform an action based on a presence of data, as we did with Optional’s ifPresent? We can do this in several ways. There is an equal method toisPresent in Optional that in Option is called isDefined:

if (result.isDefined()){
// do something
}


However, we use Option to get rid of if-else constructs. Can we float in the same way as with Optional? We can perform operations based on existence with peek:

result.peek(val -> System.out.println(val)).onEmpty(() -> System.out.println("Result is missed"));


Also, there are some other very useful methods in Vavr Option that can make your code even more functional than with the built-in Optional class. So, I encourage you to take some time and explore Vavr Option javadocs and experiment with these APIs. I will go ahead and note some cool features like map, narrow, isLazy, and when that you definitely need to check out.

Also, Vavr Option is a part of the Vavr family and is heavily integrated with other Vavr classes, and making such a comparison with Optional in the absence of such classes is not correct. So, I also plan to write other posts on Vavr topics like Try, Collections, and Streams. Stay tuned!

Conclusion

In this post, we talked about the Optional class in Java. The concept of Optional is not something new and has been already implemented in other functional programming languages like Haskell and Scala. It proves to be very useful when modeling cases when a method call could return an unknown value or a value that does not exist (e.g. nulls). Then, we explored its APIs and created some examples finding cars and manipulating results with Optional logic. And finally, we discovered an alternative to Optional – Vavr’s Option and described its methods as well.

Hope you enjoyed! Be sure to leave thoughts or questions in the comments.

Further Reading

26 Reasons Why Using Optional Correctly Is Not Optional

How to Be More Functional in Java With Vavr

A Look at Java Optionals

Topics:
java ,vavr ,java 11 ,functional programming ,tutorial ,option ,optional

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}