Introducción
En este laboratorio, aprenderás cómo manejar de manera efectiva los valores nulos cuando trabajes con colecciones en Java. Las colecciones son estructuras de datos fundamentales, y es crucial escribir código robusto que pueda manejar de forma segura situaciones en las que una variable de colección podría ser nula. Explorarás cómo probar si una colección es nula, combinar comprobaciones de nulidad y vacío para una validación integral, y aprovechar la clase Optional para una mayor seguridad frente a valores nulos, lo que en última instancia te ayudará a evitar los errores comunes de NullPointerException.
Comprobar si una colección es nula
En este paso, exploraremos cómo manejar valores nulos cuando trabajamos con colecciones en Java. Las colecciones, como List o Set, son estructuras de datos fundamentales, y es crucial escribir código que pueda manejar de forma segura situaciones en las que una variable de colección podría ser null.
Un valor null en Java significa que una variable no se refiere a ningún objeto. Si intentas acceder a métodos o propiedades de un objeto null, tu programa se detendrá con un NullPointerException. Este es un error muy común en Java, y aprender a evitarlo es esencial.
Comencemos creando un simple programa en Java que demuestre el problema.
Abre el archivo
HelloJava.javaen el editor WebIDE si no está abierto.Reemplaza todo el contenido del archivo con el siguiente código:
import java.util.List; public class HelloJava { public static void main(String[] args) { List<String> names = null; // Intencionalmente establecido en null // Esta línea causará un NullPointerException si names es null // int size = names.size(); // System.out.println("Tamaño de la lista: " + size); System.out.println("Programa finalizado."); } }En este código, declaramos una
Listde cadenas llamadanamesy la establecemos explícitamente ennull. Las líneas comentadas muestran lo que sucedería si intentáramos llamar al métodosize()en una listanull– resultaría en unNullPointerException.Guarda el archivo (Ctrl+S o Cmd+S).
Ahora, compilemos el programa. Abre la Terminal en la parte inferior del WebIDE y asegúrate de estar en el directorio
~/project. Ejecuta el siguiente comando:javac HelloJava.javaNo deberías ver salida alguna si la compilación es exitosa.
Ahora, ejecuta el programa:
java HelloJavaDeberías ver la salida:
Programa finalizado.Dado que la línea que causaría el
NullPointerExceptionestá comentada, el programa se ejecuta sin detenerse.
Ahora, modifiquemos el código para comprobar si es null antes de intentar usar la colección.
Abre
HelloJava.javanuevamente en el editor.Modifica el método
mainpara incluir una comprobación de nulidad:import java.util.List; public class HelloJava { public static void main(String[] args) { List<String> names = null; // Intencionalmente establecido en null if (names != null) { // Este código solo se ejecutará si names NO es null int size = names.size(); System.out.println("Tamaño de la lista: " + size); } else { System.out.println("La lista es null."); } System.out.println("Programa finalizado."); } }Hemos agregado una instrucción
ifque comprueba sinamesno es igual anull(names != null). El código para obtener el tamaño y mostrarlo ahora está dentro de este bloqueif, lo que significa que solo se ejecutará sinameses un objeto de lista válido. El bloqueelsemaneja el caso en el quenamesesnull.Guarda el archivo.
Compila el programa modificado:
javac HelloJava.javaEjecuta el programa nuevamente:
java HelloJavaEsta vez, deberías ver la salida:
La lista es null. Programa finalizado.El programa identificó correctamente que la lista era
nully mostró el mensaje adecuado, evitando elNullPointerException.
Esta simple comprobación if (collection != null) es la forma más básica de prevenir NullPointerException cuando se trabaja con colecciones. Es una técnica fundamental que usarás con frecuencia en la programación en Java.
Combinar comprobaciones de nulidad y vacío
En el paso anterior, aprendimos cómo comprobar si una colección es null. Sin embargo, una colección también puede estar vacía (no contener elementos) incluso si no es null. En muchos casos, es posible que desees tratar de manera similar una colección null y una colección vacía, o al menos manejar ambas posibilidades.
Comprobar si una colección está vacía se hace utilizando el método isEmpty(). Este método devuelve true si la colección no contiene elementos y false en caso contrario.
Modifiquemos nuestro programa para demostrar la diferencia entre una lista null y una lista vacía, y luego combinemos las comprobaciones.
Abre el archivo
HelloJava.javaen el editor WebIDE.Reemplaza el contenido con el siguiente 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; // Intencionalmente establecido en null List<String> emptyList = new ArrayList<>(); // Una lista vacía System.out.println("Comprobando nullList:"); if (nullList != null) { System.out.println("nullList no es null."); if (nullList.isEmpty()) { System.out.println("nullList está vacía."); } else { System.out.println("nullList no está vacía."); } } else { System.out.println("nullList es null."); } System.out.println("\nComprobando emptyList:"); if (emptyList != null) { System.out.println("emptyList no es null."); if (emptyList.isEmpty()) { System.out.println("emptyList está vacía."); } else { System.out.println("emptyList no está vacía."); } } else { System.out.println("emptyList es null."); } System.out.println("\nPrograma finalizado."); } }Hemos agregado una
emptyListque se inicializa como una nuevaArrayListvacía. Luego, realizamos las mismas comprobaciones de nulidad y vacío ennullListyemptyListpara ver la diferencia en la salida.Guarda el archivo.
Compila el programa en la Terminal:
javac HelloJava.javaEjecuta el programa:
java HelloJavaDeberías ver una salida similar a esta:
Comprobando nullList: nullList es null. Comprobando emptyList: emptyList no es null. emptyList está vacía. Programa finalizado.Esta salida muestra claramente que
nullListesnull, mientras queemptyListno esnullpero está vacía.
Ahora, combinemos las comprobaciones de nulidad y vacío en una sola condición. Un patrón común es comprobar si una colección es null o está vacía.
Abre
HelloJava.javaen el editor.Modifica el método
mainpara combinar las comprobaciones:import java.util.List; import java.util.ArrayList; public class HelloJava { public static void main(String[] args) { List<String> names = null; // Puede ser null o una lista vacía // Comprobación combinada: ¿es names null O está names vacía? if (names == null || names.isEmpty()) { System.out.println("La lista es null o está vacía."); } else { System.out.println("La lista no es null y no está vacía."); // Puedes iterar o acceder a los elementos de forma segura aquí // Por ejemplo: // System.out.println("Primer elemento: " + names.get(0)); } // Probemos con una lista vacía List<String> anotherList = new ArrayList<>(); System.out.println("\nComprobando anotherList (vacía):"); if (anotherList == null || anotherList.isEmpty()) { System.out.println("anotherList es null o está vacía."); } else { System.out.println("anotherList no es null y no está vacía."); } // Probemos con una lista no vacía List<String> populatedList = new ArrayList<>(); populatedList.add("Item 1"); System.out.println("\nComprobando populatedList (no vacía):"); if (populatedList == null || populatedList.isEmpty()) { System.out.println("populatedList es null o está vacía."); } else { System.out.println("populatedList no es null y no está vacía."); } System.out.println("\nPrograma finalizado."); } }Usamos el operador lógico OR (
||) para combinar las condicionesnames == nullynames.isEmpty(). El bloqueifse ejecutará si cualquiera de las condiciones es verdadera. También agregamos pruebas con una lista vacía y una lista con elementos para ver cómo se comporta la comprobación combinada.Nota importante: El orden de las condiciones en
names == null || names.isEmpty()es crucial. Sinamesesnull, la primera parte de la condición (names == null) es verdadera. Debido a la evaluación de cortocircuito en Java, la segunda parte (names.isEmpty()) no se evalúa, evitando unNullPointerException. Si escribierasnames.isEmpty() || names == null, ynamesfueranull, llamar anames.isEmpty()causaría unNullPointerException. Siempre comprueba si esnullprimero cuando combinas con otras comprobaciones en el objeto.Guarda el archivo.
Compila el programa:
javac HelloJava.javaEjecuta el programa:
java HelloJavaDeberías ver una salida similar a esta:
La lista es null o está vacía. Comprobando anotherList (vacía): anotherList es null o está vacía. Comprobando populatedList (no vacía): populatedList no es null y no está vacía. Programa finalizado.Esto demuestra cómo la comprobación combinada identifica correctamente las listas
nully vacías, y las distingue de las listas no vacías. Esta comprobación combinada es una forma muy común y segura de manejar colecciones que pueden sernullo vacías.
Utilizar Optional para seguridad frente a valores nulos
En este paso, exploraremos un enfoque más moderno para manejar posibles valores null en Java utilizando la clase Optional, introducida en Java 8. Optional es un objeto contenedor que puede o no contener un valor no nulo. Proporciona una forma de representar de manera más explícita la presencia o ausencia de un valor, lo que puede ayudar a reducir el riesgo de NullPointerException.
Si bien las comprobaciones if (collection != null) son perfectamente válidas y necesarias en muchas situaciones, Optional puede hacer que tu código sea más legible y expresivo, especialmente cuando se trata de métodos que pueden devolver un valor o pueden devolver null.
Veamos cómo podemos utilizar Optional con una colección. Aunque Optional se utiliza típicamente para valores individuales, es posible que encuentres escenarios en los que un método devuelva un Optional<List<SomeObject>>.
Abre el archivo
HelloJava.javaen el editor WebIDE.Reemplaza el contenido con el siguiente código que demuestra el uso de
Optionalcon una lista potencialmente nula:import java.util.List; import java.util.ArrayList; import java.util.Optional; // Import Optional public class HelloJava { // Un método que puede devolver un Optional que contiene una lista, o un Optional vacío 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); // Devuelve un Optional que contiene la lista } else { return Optional.empty(); // Devuelve un Optional vacío } } public static void main(String[] args) { // Caso 1: Obtener nombres cuando includeNames es true Optional<List<String>> namesOptional1 = getNames(true); System.out.println("Comprobando namesOptional1:"); // Comprobar si el Optional contiene un valor if (namesOptional1.isPresent()) { List<String> names = namesOptional1.get(); // Obtener la lista del Optional System.out.println("La lista está presente. Tamaño: " + names.size()); // También puedes comprobar si la lista en sí está vacía if (names.isEmpty()) { System.out.println("La lista está vacía."); } else { System.out.println("La lista no está vacía. Primer nombre: " + names.get(0)); } } else { System.out.println("La lista no está presente (Optional está vacío)."); } System.out.println("---"); // Caso 2: Obtener nombres cuando includeNames es false Optional<List<String>> namesOptional2 = getNames(false); System.out.println("Comprobando namesOptional2:"); if (namesOptional2.isPresent()) { List<String> names = namesOptional2.get(); System.out.println("La lista está presente. Tamaño: " + names.size()); if (names.isEmpty()) { System.out.println("La lista está vacía."); } else { System.out.println("La lista no está vacía. Primer nombre: " + names.get(0)); } } else { System.out.println("La lista no está presente (Optional está vacío)."); } System.out.println("\nPrograma finalizado."); } }En este código:
- Definimos un método
getNamesque devuelve unOptional<List<String>>. Este método simula un escenario en el que puedes obtener una lista o no obtener nada (representado por unOptionalvacío). - En el método
main, llamamos agetNamescontrueyfalsepara probar ambos casos. - Usamos
namesOptional.isPresent()para comprobar si elOptionalcontiene una lista. - Si
isPresent()es verdadero, usamosnamesOptional.get()para recuperar la lista. Esto es seguro porque ya hemos comprobado la presencia. - Dentro del bloque
isPresent(), luego podemos realizar comprobaciones en la lista en sí, comonames.isEmpty().
- Definimos un método
Guarda el archivo.
Compila el programa en la Terminal:
javac HelloJava.javaEjecuta el programa:
java HelloJavaDeberías ver una salida similar a esta:
Comprobando namesOptional1: La lista está presente. Tamaño: 2 La lista no está vacía. Primer nombre: Alice --- Comprobando namesOptional2: La lista no está presente (Optional está vacío). Programa finalizado.Esta salida muestra cómo
Optionalnos ayuda a manejar el caso en el que una lista puede no ser devuelta en absoluto.
Optional también proporciona otros métodos útiles para manejar la ausencia de un valor, como:
orElse(defaultValue): Devuelve el valor si está presente, de lo contrario devuelve un valor predeterminado.orElseGet(supplier): Devuelve el valor si está presente, de lo contrario devuelve el resultado de la funciónsupplier.orElseThrow(exceptionSupplier): Devuelve el valor si está presente, de lo contrario lanza una excepción producida por elexceptionSupplier.ifPresent(consumer): Realiza la acción dada si un valor está presente.
Modifiquemos el código para usar ifPresent de una manera más concisa para manejar la lista cuando está presente.
Abre
HelloJava.javaen el editor.Modifica el 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) { // Caso 1: Obtener nombres cuando includeNames es true Optional<List<String>> namesOptional1 = getNames(true); System.out.println("Comprobando namesOptional1 usando ifPresent:"); namesOptional1.ifPresent(names -> { // Este bloque solo se ejecuta si namesOptional1 contiene una lista System.out.println("La lista está presente. Tamaño: " + names.size()); if (names.isEmpty()) { System.out.println("La lista está vacía."); } else { System.out.println("La lista no está vacía. Primer nombre: " + names.get(0)); } }); if (!namesOptional1.isPresent()) { // Todavía se necesita una comprobación si se debe manejar el caso de ausencia System.out.println("La lista no está presente (Optional está vacío)."); } System.out.println("---"); // Caso 2: Obtener nombres cuando includeNames es false Optional<List<String>> namesOptional2 = getNames(false); System.out.println("Comprobando namesOptional2 usando ifPresent:"); namesOptional2.ifPresent(names -> { System.out.println("La lista está presente. Tamaño: " + names.size()); if (names.isEmpty()) { System.out.println("La lista está vacía."); } else { System.out.println("La lista no está vacía. Primer nombre: " + names.get(0)); } }); if (!namesOptional2.isPresent()) { System.out.println("La lista no está presente (Optional está vacío)."); } System.out.println("\nPrograma finalizado."); } }Reemplazamos la estructura
if (namesOptional.isPresent()) { ... namesOptional.get() ... }connamesOptional.ifPresent(names -> { ... }). El código dentro de la expresión lambda (names -> { ... }) solo se ejecutará si elOptionalcontiene un valor. Todavía agregamos una comprobaciónif (!namesOptional.isPresent())para manejar el caso en el que elOptionalestá vacío, ya queifPresentsolo maneja el caso de presencia.Guarda el archivo.
Compila el programa:
javac HelloJava.javaEjecuta el programa:
java HelloJavaLa salida debe ser la misma que antes, lo que demuestra que
ifPresentproporciona una forma alternativa de manejar la presencia de un valor en unOptional.
Utilizar Optional puede hacer que la intención de tu código sea más clara en cuanto a si un valor puede estar ausente, y te anima a manejar esa ausencia de manera explícita, reduciendo la probabilidad de NullPointerException inesperados.
Resumen
En este laboratorio (lab), aprendimos cómo manejar colecciones nulas en Java para prevenir NullPointerException. Comenzamos demostrando el problema de llamar a métodos en una colección nula, lo cual conduce a un error en tiempo de ejecución.
Luego exploramos diferentes técnicas para comprobar de manera segura si una colección es nula antes de intentar acceder a sus elementos o propiedades. Esta es una habilidad fundamental para escribir código Java robusto y libre de errores.



