Usar Optional para Segurança contra Nulos
Nesta etapa, exploraremos uma abordagem mais moderna para lidar com potenciais valores null em Java usando a classe Optional, introduzida no Java 8. Optional é um objeto contêiner que pode ou não conter um valor não nulo. Ele fornece uma maneira de representar a presença ou ausência de um valor de forma mais explícita, o que pode ajudar a reduzir o risco de NullPointerExceptions.
Embora as verificações if (collection != null) sejam perfeitamente válidas e necessárias em muitas situações, Optional pode tornar seu código mais legível e expressivo, especialmente ao lidar com métodos que podem retornar um valor ou podem retornar null.
Vamos ver como podemos usar Optional com uma coleção. Embora Optional seja tipicamente usado para valores únicos, você pode encontrar cenários em que um método retorna um Optional<List<SomeObject>>.
-
Abra o arquivo HelloJava.java no editor WebIDE.
-
Substitua o conteúdo pelo seguinte código que demonstra o uso de Optional com uma lista potencialmente nula:
import java.util.List;
import java.util.ArrayList;
import java.util.Optional; // Import Optional
public class HelloJava {
// A method that might return an Optional containing a list, or an empty Optional
public static Optional<List<String>> getNames(boolean includeNames) {
if (includeNames) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
return Optional.of(names); // Return an Optional containing the list
} else {
return Optional.empty(); // Return an empty Optional
}
}
public static void main(String[] args) {
// Case 1: Get names when includeNames is true
Optional<List<String>> namesOptional1 = getNames(true);
System.out.println("Checking namesOptional1:");
// Check if the Optional contains a value
if (namesOptional1.isPresent()) {
List<String> names = namesOptional1.get(); // Get the list from the Optional
System.out.println("List is present. Size: " + names.size());
// You can also check if the list itself is empty
if (names.isEmpty()) {
System.out.println("List is empty.");
} else {
System.out.println("List is not empty. First name: " + names.get(0));
}
} else {
System.out.println("List is not present (Optional is empty).");
}
System.out.println("---");
// Case 2: Get names when includeNames is false
Optional<List<String>> namesOptional2 = getNames(false);
System.out.println("Checking namesOptional2:");
if (namesOptional2.isPresent()) {
List<String> names = namesOptional2.get();
System.out.println("List is present. Size: " + names.size());
if (names.isEmpty()) {
System.out.println("List is empty.");
} else {
System.out.println("List is not empty. First name: " + names.get(0));
}
} else {
System.out.println("List is not present (Optional is empty).");
}
System.out.println("\nProgram finished.");
}
}
Neste código:
- Definimos um método
getNames que retorna um Optional<List<String>>. Este método simula um cenário em que você pode obter uma lista ou pode não obter nada (representado por um Optional vazio).
- No método
main, chamamos getNames com true e false para testar ambos os casos.
- Usamos
namesOptional.isPresent() para verificar se o Optional contém uma lista.
- Se
isPresent() for verdadeiro, usamos namesOptional.get() para recuperar a lista. Isso é seguro porque já verificamos a presença.
- Dentro do bloco
isPresent(), podemos então realizar verificações na própria lista, como names.isEmpty().
-
Salve o arquivo.
-
Compile o programa no Terminal:
javac HelloJava.java
-
Execute o programa:
java HelloJava
Você deve ver uma saída semelhante a esta:
Checking namesOptional1:
List is present. Size: 2
List is not empty. First name: Alice
---
Checking namesOptional2:
List is not present (Optional is empty).
Program finished.
Esta saída mostra como Optional nos ajuda a lidar com o caso em que uma lista pode não ser retornada.
Optional também fornece outros métodos úteis para lidar com a ausência de um valor, como:
orElse(defaultValue): Retorna o valor se presente, caso contrário, retorna um valor padrão.
orElseGet(supplier): Retorna o valor se presente, caso contrário, retorna o resultado da função supplier.
orElseThrow(exceptionSupplier): Retorna o valor se presente, caso contrário, lança uma exceção produzida pelo exceptionSupplier.
ifPresent(consumer): Executa a ação fornecida se um valor estiver presente.
Vamos modificar o código para usar ifPresent para uma maneira mais concisa de lidar com a lista quando ela estiver presente.
-
Abra HelloJava.java no editor.
-
Modifique o método main para usar ifPresent:
import java.util.List;
import java.util.ArrayList;
import java.util.Optional;
public class HelloJava {
public static Optional<List<String>> getNames(boolean includeNames) {
if (includeNames) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
return Optional.of(names);
} else {
return Optional.empty();
}
}
public static void main(String[] args) {
// Case 1: Get names when includeNames is true
Optional<List<String>> namesOptional1 = getNames(true);
System.out.println("Checking namesOptional1 using ifPresent:");
namesOptional1.ifPresent(names -> {
// This block only runs if namesOptional1 contains a list
System.out.println("List is present. Size: " + names.size());
if (names.isEmpty()) {
System.out.println("List is empty.");
} else {
System.out.println("List is not empty. First name: " + names.get(0));
}
});
if (!namesOptional1.isPresent()) { // Still need a check if you need to handle the absence case
System.out.println("List is not present (Optional is empty).");
}
System.out.println("---");
// Case 2: Get names when includeNames is false
Optional<List<String>> namesOptional2 = getNames(false);
System.out.println("Checking namesOptional2 using ifPresent:");
namesOptional2.ifPresent(names -> {
System.out.println("List is present. Size: " + names.size());
if (names.isEmpty()) {
System.out.println("List is empty.");
} else {
System.out.println("List is not empty. First name: " + names.get(0));
}
});
if (!namesOptional2.isPresent()) {
System.out.println("List is not present (Optional is empty).");
}
System.out.println("\nProgram finished.");
}
}
Substituímos a estrutura if (namesOptional.isPresent()) { ... namesOptional.get() ... } por namesOptional.ifPresent(names -> { ... }). O código dentro da expressão lambda (names -> { ... }) só será executado se o Optional contiver um valor. Ainda adicionamos uma verificação if (!namesOptional.isPresent()) para lidar com o caso em que o Optional está vazio, pois ifPresent lida apenas com o caso de presença.
-
Salve o arquivo.
-
Compile o programa:
javac HelloJava.java
-
Execute o programa:
java HelloJava
A saída deve ser a mesma de antes, demonstrando que ifPresent fornece uma maneira alternativa de lidar com a presença de um valor em um Optional.
Usar Optional pode tornar a intenção do seu código mais clara em relação a se um valor pode estar ausente, e isso o incentiva a lidar com essa ausência explicitamente, reduzindo a probabilidade de NullPointerExceptions inesperados.