Introdução
Neste laboratório, você aprenderá como lidar efetivamente com valores nulos ao trabalhar com coleções em Java. Coleções são estruturas de dados fundamentais, e é crucial escrever código robusto que possa gerenciar com segurança situações em que uma variável de coleção pode ser nula. Você explorará como testar coleções nulas, combinar verificações de nulo e vazio para uma validação abrangente e aproveitar a classe Optional para maior segurança contra nulos, ajudando, em última análise, a evitar erros comuns de NullPointerException.
Testar Coleção para Nulo
Nesta etapa, exploraremos como lidar com valores nulos ao trabalhar com coleções em Java. Coleções, como List ou Set, são estruturas de dados fundamentais, e é crucial escrever código que possa lidar com segurança com situações em que uma variável de coleção pode ser null.
Um valor null em Java significa que uma variável não se refere a nenhum objeto. Se você tentar acessar métodos ou propriedades de um objeto null, seu programa travará com um NullPointerException. Este é um erro muito comum em Java, e aprender a evitá-lo é essencial.
Vamos começar criando um programa Java simples que demonstra o problema.
Abra o arquivo
HelloJava.javano editor WebIDE, se ele ainda não estiver aberto.Substitua todo o conteúdo do arquivo pelo seguinte código:
import java.util.List; public class HelloJava { public static void main(String[] args) { List<String> names = null; // Intentionally set to null // This line will cause a NullPointerException if names is null // int size = names.size(); // System.out.println("Size of the list: " + size); System.out.println("Program finished."); } }Neste código, declaramos uma
Listde strings chamadanamese a definimos explicitamente comonull. As linhas comentadas mostram o que aconteceria se tentássemos chamar o métodosize()em uma listanull– resultaria em umNullPointerException.Salve o arquivo (Ctrl+S ou Cmd+S).
Agora, vamos compilar o programa. Abra o Terminal na parte inferior do WebIDE e certifique-se de estar no diretório
~/project. Execute o seguinte comando:javac HelloJava.javaVocê não deve ver nenhuma saída se a compilação for bem-sucedida.
Agora, execute o programa:
java HelloJavaVocê deve ver a saída:
Program finished.Como a linha que causaria o
NullPointerExceptionestá comentada, o programa é executado sem travar.
Agora, vamos modificar o código para verificar se há null antes de tentar usar a coleção.
Abra
HelloJava.javanovamente no editor.Modifique o método
mainpara incluir uma verificação de nulo:import java.util.List; public class HelloJava { public static void main(String[] args) { List<String> names = null; // Intentionally set to null if (names != null) { // This code will only run if names is NOT null int size = names.size(); System.out.println("Size of the list: " + size); } else { System.out.println("The list is null."); } System.out.println("Program finished."); } }Adicionamos uma instrução
ifque verifica senamesnão é igual anull(names != null). O código para obter o tamanho e imprimi-lo agora está dentro deste blocoif, o que significa que ele só será executado senamesfor um objeto de lista válido. O blocoelselida com o caso em quenamesénull.Salve o arquivo.
Compile o programa modificado:
javac HelloJava.javaExecute o programa novamente:
java HelloJavaDesta vez, você deve ver a saída:
The list is null. Program finished.O programa identificou corretamente que a lista era
nulle imprimiu a mensagem apropriada, evitando oNullPointerException.
Esta simples verificação if (collection != null) é a maneira mais básica de evitar NullPointerException ao lidar com coleções. É uma técnica fundamental que você usará com frequência na programação Java.
Combinar Verificações de Nulo e Vazio
Na etapa anterior, aprendemos como verificar se uma coleção é null. No entanto, uma coleção também pode estar vazia (não conter elementos) mesmo que não seja null. Em muitos casos, você pode querer tratar uma coleção null e uma coleção vazia de maneira semelhante, ou pelo menos lidar com ambas as possibilidades.
Verificar se uma coleção está vazia é feito usando o método isEmpty(). Este método retorna true se a coleção não contiver elementos e false caso contrário.
Vamos modificar nosso programa para demonstrar a diferença entre uma lista null e uma lista vazia e, em seguida, combinar as verificações.
Abra o arquivo
HelloJava.javano editor WebIDE.Substitua o conteúdo pelo seguinte código:
import java.util.List; import java.util.ArrayList; // Import ArrayList public class HelloJava { public static void main(String[] args) { List<String> nullList = null; // Intentionally set to null List<String> emptyList = new ArrayList<>(); // An empty list System.out.println("Checking nullList:"); if (nullList != null) { System.out.println("nullList is not null."); if (nullList.isEmpty()) { System.out.println("nullList is empty."); } else { System.out.println("nullList is not empty."); } } else { System.out.println("nullList is null."); } System.out.println("\nChecking emptyList:"); if (emptyList != null) { System.out.println("emptyList is not null."); if (emptyList.isEmpty()) { System.out.println("emptyList is empty."); } else { System.out.println("emptyList is not empty."); } } else { System.out.println("emptyList is null."); } System.out.println("\nProgram finished."); } }Adicionamos um
emptyListque é inicializado como um novoArrayListvazio. Em seguida, realizamos as mesmas verificações de nulo e vazio emnullListeemptyListpara ver a diferença na saída.Salve o arquivo.
Compile o programa no Terminal:
javac HelloJava.javaExecute o programa:
java HelloJavaVocê deve ver uma saída semelhante a esta:
Checking nullList: nullList is null. Checking emptyList: emptyList is not null. emptyList is empty. Program finished.Esta saída mostra claramente que
nullListénull, enquantoemptyListnão énull, mas está vazia.
Agora, vamos combinar as verificações de nulo e vazio em uma única condição. Um padrão comum é verificar se uma coleção é null ou vazia.
Abra
HelloJava.javano editor.Modifique o método
mainpara combinar as verificações:import java.util.List; import java.util.ArrayList; public class HelloJava { public static void main(String[] args) { List<String> names = null; // Can be null or an empty list // Combined check: is names null OR is names empty? if (names == null || names.isEmpty()) { System.out.println("The list is null or empty."); } else { System.out.println("The list is not null and not empty."); // You can safely iterate or access elements here // For example: // System.out.println("First element: " + names.get(0)); } // Let's test with an empty list List<String> anotherList = new ArrayList<>(); System.out.println("\nChecking anotherList (empty):"); if (anotherList == null || anotherList.isEmpty()) { System.out.println("anotherList is null or empty."); } else { System.out.println("anotherList is not null and not empty."); } // Let's test with a non-empty list List<String> populatedList = new ArrayList<>(); populatedList.add("Item 1"); System.out.println("\nChecking populatedList (not empty):"); if (populatedList == null || populatedList.isEmpty()) { System.out.println("populatedList is null or empty."); } else { System.out.println("populatedList is not null and not empty."); } System.out.println("\nProgram finished."); } }Usamos o operador lógico OR (
||) para combinar as condiçõesnames == nullenames.isEmpty(). O blocoifserá executado se qualquer condição for verdadeira. Também adicionamos testes com uma lista vazia e uma lista preenchida para ver como a verificação combinada se comporta.Nota Importante: A ordem das condições em
names == null || names.isEmpty()é crucial. Senamesfornull, a primeira parte da condição (names == null) é verdadeira. Devido à avaliação de curto-circuito em Java, a segunda parte (names.isEmpty()) não é avaliada, impedindo umNullPointerException. Se você escrevessenames.isEmpty() || names == null, enamesfossenull, chamarnames.isEmpty()causaria umNullPointerException. Sempre verifique se hánullprimeiro ao combinar com outras verificações no objeto.Salve o arquivo.
Compile o programa:
javac HelloJava.javaExecute o programa:
java HelloJavaVocê deve ver uma saída semelhante a esta:
The list is null or empty. Checking anotherList (empty): anotherList is null or empty. Checking populatedList (not empty): populatedList is not null and not empty. Program finished.Isso demonstra como a verificação combinada identifica corretamente listas
nulle vazias e as distingue de listas não vazias. Essa verificação combinada é uma maneira muito comum e segura de lidar com coleções que podem sernullou vazias.
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.javano editor WebIDE.Substitua o conteúdo pelo seguinte código que demonstra o uso de
Optionalcom 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
getNamesque retorna umOptional<List<String>>. Este método simula um cenário em que você pode obter uma lista ou pode não obter nada (representado por umOptionalvazio). - No método
main, chamamosgetNamescomtrueefalsepara testar ambos os casos. - Usamos
namesOptional.isPresent()para verificar se oOptionalcontém uma lista. - Se
isPresent()for verdadeiro, usamosnamesOptional.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, comonames.isEmpty().
- Definimos um método
Salve o arquivo.
Compile o programa no Terminal:
javac HelloJava.javaExecute o programa:
java HelloJavaVocê 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
Optionalnos 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çãosupplier.orElseThrow(exceptionSupplier): Retorna o valor se presente, caso contrário, lança uma exceção produzida peloexceptionSupplier.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.javano editor.Modifique o método
mainpara usarifPresent: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() ... }pornamesOptional.ifPresent(names -> { ... }). O código dentro da expressão lambda (names -> { ... }) só será executado se oOptionalcontiver um valor. Ainda adicionamos uma verificaçãoif (!namesOptional.isPresent())para lidar com o caso em que o Optional está vazio, poisifPresentlida apenas com o caso de presença.Salve o arquivo.
Compile o programa:
javac HelloJava.javaExecute o programa:
java HelloJavaA saída deve ser a mesma de antes, demonstrando que
ifPresentfornece uma maneira alternativa de lidar com a presença de um valor em umOptional.
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.
Resumo
Neste laboratório, aprendemos como lidar com coleções nulas em Java para evitar NullPointerException. Começamos demonstrando o problema de chamar métodos em uma coleção nula, o que leva a um erro em tempo de execução.
Em seguida, exploramos diferentes técnicas para verificar com segurança se uma coleção é nula antes de tentar acessar seus elementos ou propriedades. Esta é uma habilidade fundamental para escrever código Java robusto e sem erros.



