Trabalhando com Optional
Tags: java
Published: 2021-04-29 20:43:22

Neste guia você irá aprender como funciona o `Optional`, um novo recurso adicionado no Java 8 e saberá como usá-lo.

# Introdução

A classe `Optional` foi adicionada na versão do 8 no pacote `java.util`. Esta classe foi introduzida para verificar se um valor está presente, evitando o famoso erro de `NullPointerException`.

Um objeto `Optional` é um container que pode ou não conter um valor.

# Métodos mais usados

A classe `Optional` possui vários métodos, entre eles temos o `isPresent()` e `get()`.

- `isPresent()`: Verifica se o objeto `Optional` possui algum valor que seja diferente de vazio ou `null`. Retorna `true` se tiver algum valor, caso contrário retorna `false`.
- `get()`: Recupera o valor de um objeto `Optional`.

> Semelhante ao `isPresent()` o método `isEmpty()` retorna `true` se estiver vazio, caso contrário, retornará `true`. Este método foi adicionado no Java 11.

```java
package to.dev;

import java.util.Optional;

public class OptionalExample {

    public static void main(String[] args) {
        Optional<String> stringOptional = Optional.of("Hello");

        if (stringOptional.isPresent()) {
            System.out.println(stringOptional.get()); // Hello
        }
    }
}
```

Para criar um `Optional` é preciso usar o método `Optional.of(T value)`, este método retorna um `Optional<T>` do mesmo tipo do argumento fornecido em `of(T value)`.
Ex. `Optional<Long> longOptional = Optional.of(1L);`

# `Optional.of(T value)` e `Optional.ofNullable(T value)`

Só use `of(T value)` quando o argumento fornecido **NUNCA** será `null`. Caso contrário, será lançado um `NullPointerException`. Para evitar esse erro use `ofNullable(T value)`

```java
public class OptionalExample {

    public static void main(String[] args) {
        Optional<String> stringOptional = Optional.ofNullable(null);

        if (stringOptional.isPresent()) {
            System.out.println(stringOptional.get());
        }
    }
}
```

# Criando um `Optional` vazio

Para criar um `Optional` vazio use `Optional.empty()`. Ex. `Optional<String> stringOptional = Optional.empty();`

# `Optional` com valores default - `orElse(T other)`

Caso um `Optional` esteja vazio e você queira retorna um valor padão basta usar o método `orElse(T other)`.

```java
package to.dev;

import java.util.Optional;

public class OptionalExample {

    public static void main(String[] args) {
        Optional<Long> longOptional = Optional.ofNullable(null);
        System.out.println(longOptional.orElse(99L)); // 99
    }
}
```

# `Optional` com valores default - `orElseGet(Supplier<? extends T> supplier)`

Esta alternativa recebe um `Supplier` como argumento, permitindo que você execute alguma operação.

> `Supplier`: É uma interface funcional que não recebe argumentos, mas sempre retorna um valor.

```java
package to.dev;

import java.time.LocalDate;
import java.util.Optional;

public class OptionalExample {

    public static void main(String[] args) {
        Optional<Long> longOptional = Optional.ofNullable(null);
        System.out.println(longOptional.orElseGet(() -> { // 29
            int i = LocalDate.now().getDayOfMonth();
            return (long) i;
        }));
    }
}
```

# `orElse(T other)` vs `orElseGet(Supplier<? extends T> supplier)`

`orElse(T other)` retorna o valor dentro do container `Optional` se estiver presente, caso contrário, irá retornar o valor fornecido no argumento do método. `orElseGet(Supplier<? extends T> supplier)`: retorna o valor dentro do container `Optional`, caso contrário, irá invocar o `Supplier` e retornará o resultado da invocação.

# Filtrando um `Optional`

Para filtrar algum valor de `Optional` use o método `filter(Predicate<? super T> predicate)`.

> `Predicate`: É uma interface funcional que recebe um valor e sempre retorna um valor booleano.

```java
package to.dev;

import java.util.Optional;

public class OptionalExample {

    public static void main(String[] args) {
        Optional<Long> longOptional = Optional.ofNullable(30L);

        Optional<Long> longFilter = longOptional.filter(v -> v > 18); // true

        if (longOptional.isPresent()) {
            System.out.println(longFilter.get()); // 30
        }
    }
}
```

# Modificando o valor de um `Optional` com `map(Function<? super T, ? extends U> mapper)`

O método `map()` serve para modificar um valor dentro de um container `Optional`. Para isso é passado uma `Function` com argumento.

> `Function`: É uma interface funcional que recebe um valor como argumento e retorna um valor que pode igual ou diferente do argumento passado.

```java
package to.dev;

import java.util.Optional;

public class OptionalExample {

    public static void main(String[] args) {
        Optional<String> longOptional = Optional.ofNullable("Hello");

        Optional<String> longMap = longOptional.map(v -> {
            if (v.contains("Hello")) {
                return "Hello World";
            } else {
                return "Hello";
            }
        });

        System.out.println(longMap.get()); // Hello World
    }
}
```

# Recebendo um `Optional` de outro `Optional` com `flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)`

O `flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)` é usado quando você recebe um objeto `Optional` de um container `Optional`, `Optional<Optional<Long>> longOptional = Optional.of(Optional.empty());`.

```java
package to.dev;

import java.util.Optional;

public class OptionalExample {

    public static void main(String[] args) {
        Optional<String> stringOptional = Optional.of("Hello World");
        Optional<Optional<String>> optionalStringOptional = Optional.of(stringOptional);

        System.out.println(optionalStringOptional);

        Optional<String> output = optionalStringOptional.flatMap(value -> value.map(String::toLowerCase));

        System.out.println(output);
    }
}
```

# Executando uma lógica se o `Optional` estiver presente

Caso queira executar alguma operação se um valor estiver presente, uma alternativa é usar o método `ifPresent(Consumer<? super T> consumer)`.

> `Consumer`: É uma interface funcional que recebe argumentos, mas não retorna nenhum valor.

```java
package to.dev;

import java.util.Optional;

public class OptionalExample {

    public static void main(String[] args) {
        Optional<String> longOptional = Optional.ofNullable("Hello");

        Optional<String> longMap = longOptional.map(v -> {
            if (v.contains("Hello")) {
                return "Hello World";
            } else {
                return "Hello";
            }
        });

        longMap.ifPresent(v -> System.out.println(v)); // Hello World
    }
}
```

# Tratando exceções com `orElseThrow(Supplier<? extends X> exceptionSupplier)`

O método `orElseThrow(Supplier<? extends X> exceptionSupplier)` retorna o valor caso ele esteja presente, caso contrário, ele irá lançar um exceção fornecida pelo desenvolvedor.

> Este método só está disponível no Java 10.

```java
package to.dev;

import java.util.Optional;

class EmptyException extends Exception {
    public EmptyException() {
        super("Not found value");
    }
}

public class OptionalExample {
    public static void main(String[] args) throws EmptyException {
        Optional<String> stringOptional = Optional.ofNullable(null);

        System.out.println(stringOptional.orElseThrow()); // throws -> NoSuchElementException: No value present
        System.out.println(stringOptional.orElseThrow(EmptyException::new)); // throws -> EmptyException: Not found value

    }
}
```

Referência: [https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Optional.html](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Optional.html)