There's just one thing that can break the loops we've looked at so far: null
. Let's consider what would happen if numbersToSum
was never initialized.
public void main() { Integer[] numbersToSum; Integer sumOfIntegers = calculateSum(numbersToSum); } public Integer calculateSum(Integer[] numbers){ Integer sum = 0; for(Integer num : numbers){ // System.NullPointerException: Attempt to de-reference a null object sum += num; } return sum; }
The argument numbersToSum
is null when passed into the calculateSum()
method so the corresponding parameter numbers
is also null. Once numbers
is referenced, like it has above in the for-each loop, an exception will be thrown. We can handle this in one of two ways.
The first approach is to modify the code to call calculateSum()
only if numbersToSum
is not null. The motivation behind this is to fail fast by avoiding a call that doesn't need to happen:
public void main() { Integer[] numbersToSum; Integer sumOfIntegers; if(numbersToSum != null) { sumOfIntegers = calculateSum(numbersToSum); // now this line won't be called! } } public Integer calculateSum(Integer[] numbers){ Integer sum = 0; for(Integer num : numbers) { sum += num; } return sum; }
This approach works well, but now sumOfIntegers
is not initialized. This could cause a different null pointer exception if referenced. Let's initialize it to be safe:
public void main() { Integer[] numbersToSum; Integer sumOfIntegers = 0; if(numbersToSum != null) { sumOfIntegers = calculateSum(numbersToSum); } }
In the second approach, calculateSum()
will be responsible for checking its parameters for null. This approach eliminates the need for callers to perform a null check every time they call the method, since the method handles the check internally.
public void main() { Integer[] numbersToSum; Integer sumOfIntegers = calculateSum(numbersToSum); } public Integer calculateSum(Integer[] numbers){ Integer sum = 0; if(numbers == null) { return sum; // will return from the method early if numbers is null. The code below will never be called } for(Integer num : numbers) { sum += num; } return sum; }
Some folks prefer code that does it even earlier, like this:
public void main() { Integer[] numbersToSum; Integer sumOfIntegers = calculateSum(numbersToSum); } public Integer calculateSum(Integer[] numbers){ if(numbers == null) { return 0; } Integer sum = 0; for(Integer num : numbers) { sum += num; } return sum; }
And just to show how many different flavors there are, some folks prefer only have 1 return statement, so they may write something like this:
public void main() { Integer[] numbersToSum; Integer sumOfIntegers = calculateSum(numbersToSum); } public Integer calculateSum(Integer[] numbers){ Integer sum = 0; if(numbers != null) { for(Integer num : numbers) { sum += num; } } return sum; }
There are a lot of different paths that lead to the same outcome. I recommend taking a look at all these examples and deciding which one you prefer. It's important to develop your own coding style and preferences as you continue on your programming journey.
We'll touch on this quickly, but the elements of the list can be null, too:
public void main() { List<Integer> numbersToSum = new List<Integer>{10, 5, null, 5}; Integer sumOfIntegers = calculateSum(numbersToSum); } public Integer calculateSum(Integer[] numbers){ Integer sum = 0; if(numbers != null) { for(Integer num : numbers) { sum += num; // Yikes! } } return sum; }
This will result in the same runtime exception: System.NullPointerException: Attempt to de-reference a null object
except now it's happening on the sum += num
statement because num
is null. To prevent this, we can check for null before performing the calculation:
public void main() { List<Integer> numbersToSum = new List<Integer>{10, 5, null, 5}; Integer sumOfIntegers = calculateSum(numbersToSum); } public Integer calculateSum(Integer[] numbers){ Integer sum = 0; if(numbers != null) { for(Integer num : numbers) { if(num != null){ sum += num; } } } return sum; }