Como Verificar se uma Coleção é Nula em Java

JavaBeginner
Pratique Agora

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.

  1. Abra o arquivo HelloJava.java no editor WebIDE, se ele ainda não estiver aberto.

  2. 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 List de strings chamada names e a definimos explicitamente como null. As linhas comentadas mostram o que aconteceria se tentássemos chamar o método size() em uma lista null – resultaria em um NullPointerException.

  3. Salve o arquivo (Ctrl+S ou Cmd+S).

  4. 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.java

    Você não deve ver nenhuma saída se a compilação for bem-sucedida.

  5. Agora, execute o programa:

    java HelloJava

    Você deve ver a saída:

    Program finished.

    Como a linha que causaria o NullPointerException está comentada, o programa é executado sem travar.

Agora, vamos modificar o código para verificar se há null antes de tentar usar a coleção.

  1. Abra HelloJava.java novamente no editor.

  2. Modifique o método main para 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 if que verifica se names não é igual a null (names != null). O código para obter o tamanho e imprimi-lo agora está dentro deste bloco if, o que significa que ele só será executado se names for um objeto de lista válido. O bloco else lida com o caso em que names é null.

  3. Salve o arquivo.

  4. Compile o programa modificado:

    javac HelloJava.java
  5. Execute o programa novamente:

    java HelloJava

    Desta vez, você deve ver a saída:

    The list is null.
    Program finished.

    O programa identificou corretamente que a lista era null e imprimiu a mensagem apropriada, evitando o NullPointerException.

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.

  1. Abra o arquivo HelloJava.java no editor WebIDE.

  2. 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 emptyList que é inicializado como um novo ArrayList vazio. Em seguida, realizamos as mesmas verificações de nulo e vazio em nullList e emptyList para ver a diferença na saída.

  3. Salve o arquivo.

  4. Compile o programa no Terminal:

    javac HelloJava.java
  5. Execute o programa:

    java HelloJava

    Você 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, enquanto emptyList nã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.

  1. Abra HelloJava.java no editor.

  2. Modifique o método main para 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ções names == null e names.isEmpty(). O bloco if será 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. Se names for null, 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 um NullPointerException. Se você escrevesse names.isEmpty() || names == null, e names fosse null, chamar names.isEmpty() causaria um NullPointerException. Sempre verifique se há null primeiro ao combinar com outras verificações no objeto.

  3. Salve o arquivo.

  4. Compile o programa:

    javac HelloJava.java
  5. Execute o programa:

    java HelloJava

    Você 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 null e 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 ser null ou 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>>.

  1. Abra o arquivo HelloJava.java no editor WebIDE.

  2. 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().
  3. Salve o arquivo.

  4. Compile o programa no Terminal:

    javac HelloJava.java
  5. 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.

  1. Abra HelloJava.java no editor.

  2. 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.

  3. Salve o arquivo.

  4. Compile o programa:

    javac HelloJava.java
  5. 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.

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.