O que é opcional?
O parâmetro Opcional é usado para transportar objetos e permitir que referências nulas sejam tratadas por várias APIs. Vejamos o trecho de código:Coffee coffee = new Coffee();
Integer quantity = coffee.getSugar().getQuantity();
Temos uma instância Coffee na qual obtemos um pouco de açúcar de uma instância do objeto Sugar . Se assumirmos que o valor da quantidade nunca foi definido no construtor Coffee , então coffee.getSugar().getQuantity() retornará um NullPointerException . Claro, sempre podemos usar as boas e velhas verificações de nulos para resolver o problema.
Coffee coffee = new Coffee();
Integer quantity = 0;
if (coffee.getSugar() != null) {
quantity = coffee.getSugar().getQuantity();
}
Agora tudo parece estar bem. Mas ao escrever código Java, é melhor evitarmos implementar verificações de nulos . Vamos ver como isso pode ser feito usando Opcional.
Como criar Opcional
Existem três maneiras de criar objetos opcionais:-
of(T value) — instanciação de um objeto opcional não nulo. Esteja ciente de que usar of() para se referir a um objeto nulo gerará um NullPointerException .
-
ofNullable(T value) - cria um valor Opcional para um objeto que pode ser nulo.
-
vazio() - Cria uma instância Opcional que representa uma referência a null .
// пример использования Optional.of(T Value)
String name = "foo";
Optional<String> stringExample = Optional.of(name)
// пример использования Optional.ofNullable(T Value)
Integer age = null;
Optional<Integer> integerExample= Optional.ofNullable(age)
// пример использования Optional.empty()
Optional<Object> emptyExample = Optional.empty();
Então você tem um objeto Opcional. Agora vamos dar uma olhada nos dois métodos principais para Opcional:
-
isPresent() – Este método informa se o objeto Opcional contém um valor não nulo.
-
get() - Recupera o valor de Opcional com o valor atual. Esteja ciente de que chamar get() em um opcional vazio resultará em NullPointerException .
Melhorando a verificação de nulos com opcional
Então, como podemos melhorar o código acima? Com Opcional podemos entender a presença de um objeto usando isPresent() e recuperá-lo usando get() . Vamos começar empacotando o resultado de coffee.getSugar() com Opcional e usando o método isPresent() . Isso nos ajudará a determinar se getSugar() retorna nulo.Coffee coffee = new Coffee();
Optional<String> sugar = Optional.ofNullable(coffee.getSugar());
int quantity = 0;
if (sugar.isPresent()) {
Sugar sugar = sugar.get();
int quantity = sugar.getQuantity();
}
Olhando para este exemplo, empacotar o resultado de coffee.getSugar() em Opcional não parece agregar nenhum valor, mas sim adicionar complicações. Podemos melhorar o resultado usando o que considero minhas funções favoritas da classe Opcional:
-
map(Function<? super T,? extends U> mapper) - Mapeia o valor contido em Opcional para a função fornecida. Se o parâmetro Opcional estiver vazio, map() retornará Opcional.empty() .
-
orElse(T other) é uma versão “especial” do método get() . Pode obter o valor contido em Opcional. Porém, no caso de um Opcional vazio, isso retornará o valor passado para o método orElse() .
Coffee coffee = new Coffee();
Integer quantity = Optional.ofNullable(coffee.getSugar())
.map(it -> it.getQuantity())
.orElse(0);
Isso é muito legal – pelo menos eu acho. Agora, se no caso de valor vazio não quisermos retornar o valor padrão, então precisamos lançar algum tipo de exceção. orElseThrow(Supplier<? extends X> exceçãoSupplier) retorna o valor contido nos parâmetros Opcionais ou lança uma exceção se Opcional estiver vazio.
Coffee coffee = new Coffee();
Integer quantity = Optional.ofNullable(coffee.getSugar())
.map(it -> it.getQuantity())
.orElseThrow(IllegalArgumentException::new);
Como você pode ver, Opcional oferece várias vantagens:
- abstrai verificações nulas
- fornece uma API para lidar com objetos nulos
- permite que a abordagem declarativa expresse o que está sendo alcançado
Como se tornar eficaz com Opcional
Em meu trabalho, uso Opcional como tipo de retorno quando um método pode retornar um estado “sem resultado”. Eu costumo usá-lo ao definir tipos de retorno para métodos.Optional<Coffee> findByName(String name) {
...
}
Às vezes isso não é necessário. Por exemplo, se eu tiver um método que retorne um int , como getQuantity() na classe Sugar , então o método poderá retornar 0 se o resultado for nulo para representar “sem quantidade”. Agora, sabendo disso, podemos pensar que o parâmetro Sugar na classe Coffee pode ser representado como Opcional. À primeira vista, parece uma boa ideia já que, em tese, o açúcar não precisa estar presente no café. No entanto, é aqui que gostaria de abordar quando não usar Opcional. Devemos evitar o uso de Opcional nos seguintes cenários:
-
Como tipos de parâmetros para POJOs , como DTOs . Os opcionais não são serializáveis, portanto, usá-los em um POJO torna o objeto não serializável.
-
Como um argumento de método. Se um argumento de método puder ser null , então, de uma perspectiva de código puro, passar null ainda é preferível a passar Opcional. Além disso, você pode criar métodos sobrecarregados para lidar abstratamente com a ausência de um argumento de método nulo.
-
Para representar um objeto Collection que está faltando. As coleções podem estar vazias, portanto, uma Collection vazia , como um Set ou List vazio , deve ser usada para representar uma Collection sem valores.
GO TO FULL VERSION