Introduction
In this lab, you will learn how to effectively handle null values when working with collections in Java. Collections are fundamental data structures, and it's crucial to write robust code that can safely manage situations where a collection variable might be null. You will explore how to test for null collections, combine null and empty checks for comprehensive validation, and leverage the Optional class for enhanced null safety, ultimately helping you avoid common NullPointerException errors.
Test Collection for Null
In this step, we will explore how to handle null values when working with collections in Java. Collections, like List or Set, are fundamental data structures, and it's crucial to write code that can safely handle situations where a collection variable might be null.
A null value in Java means that a variable does not refer to any object. If you try to access methods or properties of a null object, your program will crash with a NullPointerException. This is a very common error in Java, and learning how to avoid it is essential.
Let's start by creating a simple Java program that demonstrates the problem.
Open the
HelloJava.javafile in the WebIDE editor if it's not already open.Replace the entire contents of the file with the following code:
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."); } }In this code, we declare a
Listof strings callednamesand explicitly set it tonull. The commented-out lines show what would happen if we tried to call thesize()method on anulllist – it would result in aNullPointerException.Save the file (Ctrl+S or Cmd+S).
Now, let's compile the program. Open the Terminal at the bottom of the WebIDE and make sure you are in the
~/projectdirectory. Run the following command:javac HelloJava.javaYou should see no output if the compilation is successful.
Now, run the program:
java HelloJavaYou should see the output:
Program finished.Since the line that would cause the
NullPointerExceptionis commented out, the program runs without crashing.
Now, let's modify the code to check for null before trying to use the collection.
Open
HelloJava.javaagain in the editor.Modify the
mainmethod to include a null check: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."); } }We've added an
ifstatement that checks ifnamesis not equal tonull(names != null). The code to get the size and print it is now inside thisifblock, meaning it will only execute ifnamesis a valid list object. Theelseblock handles the case wherenamesisnull.Save the file.
Compile the modified program:
javac HelloJava.javaRun the program again:
java HelloJavaThis time, you should see the output:
The list is null. Program finished.The program correctly identified that the list was
nulland printed the appropriate message, avoiding theNullPointerException.
This simple if (collection != null) check is the most basic way to prevent NullPointerException when dealing with collections. It's a fundamental technique that you will use frequently in Java programming.
Combine Null and Empty Checks
In the previous step, we learned how to check if a collection is null. However, a collection can also be empty (contain no elements) even if it's not null. In many cases, you might want to treat a null collection and an empty collection similarly, or at least handle both possibilities.
Checking if a collection is empty is done using the isEmpty() method. This method returns true if the collection contains no elements and false otherwise.
Let's modify our program to demonstrate the difference between a null list and an empty list, and then combine the checks.
Open the
HelloJava.javafile in the WebIDE editor.Replace the contents with the following code:
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."); } }We've added an
emptyListwhich is initialized as a new, emptyArrayList. We then perform the same null and empty checks on bothnullListandemptyListto see the difference in output.Save the file.
Compile the program in the Terminal:
javac HelloJava.javaRun the program:
java HelloJavaYou should see output similar to this:
Checking nullList: nullList is null. Checking emptyList: emptyList is not null. emptyList is empty. Program finished.This output clearly shows that
nullListisnull, whileemptyListis notnullbut is empty.
Now, let's combine the null and empty checks into a single condition. A common pattern is to check if a collection is either null or empty.
Open
HelloJava.javain the editor.Modify the
mainmethod to combine the checks: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."); } }We use the logical OR operator (
||) to combine the conditionsnames == nullandnames.isEmpty(). Theifblock will execute if either condition is true. We also added tests with an empty list and a populated list to see how the combined check behaves.Important Note: The order of the conditions in
names == null || names.isEmpty()is crucial. Ifnamesisnull, the first part of the condition (names == null) is true. Because of short-circuit evaluation in Java, the second part (names.isEmpty()) is not evaluated, preventing aNullPointerException. If you wrotenames.isEmpty() || names == null, andnameswasnull, callingnames.isEmpty()would cause aNullPointerException. Always check fornullfirst when combining with other checks on the object.Save the file.
Compile the program:
javac HelloJava.javaRun the program:
java HelloJavaYou should see output similar to this:
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.This demonstrates how the combined check correctly identifies
nulland empty lists, and distinguishes them from non-empty lists. This combined check is a very common and safe way to handle collections that might benullor empty.
Use Optional for Null Safety
In this step, we will explore a more modern approach to handling potential null values in Java using the Optional class, introduced in Java 8. Optional is a container object that may or may not contain a non-null value. It provides a way to represent the presence or absence of a value more explicitly, which can help reduce the risk of NullPointerExceptions.
While if (collection != null) checks are perfectly valid and necessary in many situations, Optional can make your code more readable and expressive, especially when dealing with methods that might return a value or might return null.
Let's see how we can use Optional with a collection. Although Optional is typically used for single values, you might encounter scenarios where a method returns an Optional<List<SomeObject>>.
Open the
HelloJava.javafile in the WebIDE editor.Replace the contents with the following code that demonstrates using
Optionalwith a potentially null list: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."); } }In this code:
- We define a method
getNamesthat returns anOptional<List<String>>. This method simulates a scenario where you might get a list, or you might get nothing (represented by an emptyOptional). - In the
mainmethod, we callgetNameswithtrueandfalseto test both cases. - We use
namesOptional.isPresent()to check if theOptionalcontains a list. - If
isPresent()is true, we usenamesOptional.get()to retrieve the list. This is safe because we've already checked for presence. - Inside the
isPresent()block, we can then perform checks on the list itself, such asnames.isEmpty().
- We define a method
Save the file.
Compile the program in the Terminal:
javac HelloJava.javaRun the program:
java HelloJavaYou should see output similar to this:
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.This output shows how
Optionalhelps us handle the case where a list might not be returned at all.
Optional also provides other useful methods for handling the absence of a value, such as:
orElse(defaultValue): Returns the value if present, otherwise returns a default value.orElseGet(supplier): Returns the value if present, otherwise returns the result of thesupplierfunction.orElseThrow(exceptionSupplier): Returns the value if present, otherwise throws an exception produced by theexceptionSupplier.ifPresent(consumer): Performs the given action if a value is present.
Let's modify the code to use ifPresent for a more concise way to handle the list when it's present.
Open
HelloJava.javain the editor.Modify the
mainmethod to useifPresent: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."); } }We replaced the
if (namesOptional.isPresent()) { ... namesOptional.get() ... }structure withnamesOptional.ifPresent(names -> { ... }). The code inside the lambda expression (names -> { ... }) will only execute if theOptionalcontains a value. We still added anif (!namesOptional.isPresent())check to handle the case where the Optional is empty, asifPresentonly handles the presence case.Save the file.
Compile the program:
javac HelloJava.javaRun the program:
java HelloJavaThe output should be the same as before, demonstrating that
ifPresentprovides an alternative way to handle the presence of a value in anOptional.
Using Optional can make your code's intent clearer regarding whether a value might be absent, and it encourages you to handle that absence explicitly, reducing the likelihood of unexpected NullPointerExceptions.
Summary
In this lab, we learned how to handle null collections in Java to prevent NullPointerException. We started by demonstrating the problem of calling methods on a null collection, which leads to a runtime error.
We then explored different techniques to safely check if a collection is null before attempting to access its elements or properties. This is a fundamental skill for writing robust and error-free Java code.



