Optional in java

Optional in java

Sometimes we must handle null values in java and I know that's very problematical because it can throw the most famous exception(NullPointerException - NPE) and QA generate a superbug in Jira. jejeje

So, what is optional? An optional is a container object which may or may not contain a non-null value. Optional are part of Java API since java 8, so you don't need any external dependency to work with it.

I mean, this feature replaces this:

if(param != null) {
    //do one thing
} else {
    // do other thing
}

into this:

//We can follow a flow and avoid the tabs.
Optional.ofNullable(param).orElse(otherParam)

Optional is great, we can encapsulate any object because it supports generics. Thus, we can pass String, Integer, Boolean, User Object, List Object, whatever kind of object that we want.

    @Test
    void optionalExample() {
        Object name = "Sleyter";
        Optional opt1 = Optional.of(name);
        assertEquals(opt1.get(), name);
    }

Additionally, we have some methods to make optional a powerful tool in our code. Some methods are:

Optional empty = Optional.empty() //Optional without value
Optional.ifPresent() // Return true if optional has a value
Optional.ifEmpty() //Return true if optional don't have a value
Optional.get() // Return the object encapsulated, be careful, it can throw NPE
Optional.orElse(new Object()) // In case optional is empty, it return new Object()
Optional.orElseThrow() //In case optional is empty, you can throw an exception.
Optional.or(Supplier<Optional>)//You can choose between two optional to return.

In real projects, Optional can be used in many different places, sometimes to validate if a query found a row or not, other cases are to validate in case the optional doesn't have a value and others is when you have to work with external artifacts and the method needs to return an optional. Therefore, I think these two use cases are the most used when we work with an optional.

1. Finding a record in DB

    @Test
    void findingByEmail() {
        //Looking for a user
        Optional<User> userOptional = userRepo
                .findByEmail("sleyter@hashnode.dev");
        //1 and 2 are equals
        //1.
        User userPresent;
        if(userOptional.isPresent()) {

            userPresent = userOptional.get();
            userPresent.setUpdatedAt(LocalDate.now());
            userPresent = userRepo.save(userPresent);
        } else {
            // If the user doesn't exist, throw an exception.
            throw new RuntimeException("User not found");
        }

        //2.
        User userPresent2 = userOptional.map(user -> {
           user.setUpdatedAt(LocalDate.now());
           return userRepo.save(user);
        }).orElseThrow(() -> new RuntimeException("User not found"));

        assertEquals(userPresent.getEmail(), userPresent2.getEmail());
    }

2. When null means all values

    @Test
    void whenNullMeansAll() {
        //All OperationType, we have 2(Transfer and Payment)
        OperationType filter = null;
        var optional = Optional.ofNullable(filter);
        var filters = optional
            .filter(Objects::nonNull)
            .map(operationType -> new OperationType[]{operationType})
            .orElse(OperationType.values());
        assertEquals(filters.length, 2);
        assertEquals(filters[0], OperationType.TRANSFER);
        assertEquals(filters[1], OperationType.PAYMENT);

        OperationType filter2 = OperationType.TRANSFER;
        var optional2 = Optional.ofNullable(filter2);
        var filters2 = optional2
                .filter(Objects::nonNull)
                .map(operationType -> new OperationType[]{operationType})
                .orElse(OperationType.values());
        assertEquals(filters2.length, 1);
        assertEquals(filters2[0], OperationType.TRANSFER);

    }

Also, optional are part of stream API, so we can make the same operations as streams as well. Optional is like a stream where you only have one or zero elements.

    @Test
    void optionalStream() {
        var nameOrNot = Optional.of("Sleyter");
        long count = nameOrNot.stream()
                .map(String::toUpperCase)
                .filter(name -> name.length() > 3)
                .count();

        assertEquals(count, 1);

        long notCount = nameOrNot.stream()
                .map(String::toUpperCase)
                .filter(name -> name.length() > 10)
                .count();

        assertEquals(notCount, 0);

        String total = nameOrNot.stream()
                .map(name -> name.repeat(2))
                .filter(name -> name.length() % 2 == 0)
                .collect(Collectors.joining());

        assertEquals(total.length(), 14);
    }

Wrap Up

Wow, this was pretty exciting, this is my first article about programming. I hope you enjoy this article as much as I enjoyed writing. I appreciate any comment about the article and I hope to see you in the next article.