How To handle NullPointerException Using Optional Class

Search for a command to run...

No comments yet. Be the first to comment.
The :: operator refers to a method reference. A method reference is a simplified way of writing a lambda expression to call a method. Method references allow you to call a method by its name. The syntax for a method reference is as follows: Object ::...
AI has transformed how I work as a technical writer. Large tech companies are developing and sharpening these tools, but some of them are better than others depending on what you want to accomplish. T

I've been writing professionally for 3+ years, and on this personal blog for 5+ years. Throughout this time, I've built a large network made of developers, other technical writers, solutions engineers

I’ve been writing online for almost five years, with a professional focus on technical writing for over two years. If you struggle to create technical content that is easy to digest, this article is for you. Where do I start? If you don’t know where ...

LinkedIn is the only social networking site purposely built to help job seekers. Over the years, LinkedIn has evolved by offering two types of experiences: Job searching Content creation This article will describe how to maximize your LinkedIn fo...

In the last 12+ months as a technical writer, I've worked on several projects highlighting three technical writing elements. This article will introduce you to: Information Architecture Docs-as-code Content strategy Information Architecture (IA)...

If you use Java as your primary programming language, you've come across a NullPointerException at some point in your life.
At a very high level, a NullPointerException is a RuntimeException that happens when you try to access a variable, but this variable is pointing to no object.
Take a look at the code snippet below:
public class Country {
public static void main(String[] args) {
String[] countries = new String[5];
String asianCountry = countries[3].toUpperCase();
System.out.println(asianCountry);
}
}
What we get is:
Exception in thread "main" java.lang.NullPointerException
at Country.main(Country.java:7)
We're getting a NullPointerException because we have a String array of countries, and we're trying to access its third element, which is null.
We can use some alternative ways to handle the NullPointerException, such as surrounding the code in a try...catch block (like in exampleOne) or if statements (like in exampleTwo).
public class Country {
public static void main(String[] args) {
String country = "India";
exampleOne(null);
exampleTwo(null);
}
static void exampleOne(String country){
try {
System.out.println("Printing the country using try/catch: " + country.toUpperCase());
} catch(NullPointerException e){
System.out.println("Throwing a NullPointerException");
}
}
static void exampleTwo(String country){
if(country != null){
System.out.println("Printing the country using IF/ELSE statement: " + country.toUpperCase());
} else {
System.out.println("There is no such country");
}
}
}
Outcome:
Throwing a NullPointerException
There is no such country
But this way, we're just making the code unpleasant to read.
Plus, what if we have to check more data? The code would become more verbose.
Java 8 has introduced the Optional class in the Java.util package, which functions like a box that may or may not contain null values.
At runtime, we won't get a NullPointerException.
We don't have to carry out null checks.
We reduce boilerplate code.
We get to develop tidier APIs.
The example above can be rewritten like this using the Optional class.
import java.util.Optional;
public class Country {
public static void main(String[] args) {
String[] countries = new String[5];
Optional<String> checkCountry = Optional.ofNullable(countries[3]);
if(checkCountry.isPresent()){
String country = countries[3].toUpperCase();
System.out.println(country);
} else {
System.out.println("The country is null");
}
}
}
We created a String Optional corresponding to the third country in the string array.
The ofNullable() method returns an Optional with that value if it exists. Otherwise, it returns an empty Optional.
In the if...else block, you're seeing if the country is present in the Optional or not. If it is, we print out the country in uppercase. Otherwise, we print a message saying, "The country is null."
Since we haven't initialized the String array, the output would be:
The country is null
Otherwise, you can initialize the variable like here:
import java.util.Optional;
public class Country {
public static void main(String[] args) {
String[] countries = new String[5];
countries[3] = "India";
Optional<String> checkCountry = Optional.ofNullable(countries[3]);
if(checkCountry.isPresent()){
String country = countries[3].toUpperCase();
System.out.println(country);
} else {
System.out.println("The country is null");
}
}
}
And the output would be:
INDIA
Some more examples of Optional class methods:
ifPresent() public static void main(String[] args) {
Optional<String> country = Optional.of("India");
country.ifPresent( value -> System.out.println(value.toUpperCase()));
}
The outcome is:
INDIA
The value is present, and it's going to be printed in uppercase. If the value is not present, nothing would be printed.
ifPresentOrElse() public static void main(String[] args) {
Optional<String> country = Optional.of("India");
country.ifPresentOrElse(value -> System.out.println("The country is: " + value),
() -> System.out.println("The country is not present"));
}
The outcome is:
The country is: India
This method takes in Consumer and Runnables functional interfaces.
The Consumer will be triggered only if the value is present. Runnable if the value is absent. In this case, the value is present, and therefore the Consumer will be called.
map()
public static void main(String[] args) {
Optional<String> country = Optional.of("America");
Optional<Integer> lengthOfCountry = country.map(String::length);
System.out.println("The length of America is: " + lengthOfCountry.get());
}
The outcome is:
The length of America is: 7
The map() method transforms a value into another value. In our case, you're taking an Optional String and converting it into an Integer that is equal to its length.
flatMap()import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class Country {
private Optional<String> capital;
public Country(Optional<String> capital) {
this.capital = capital;
}
public Optional<String> getCapital() {
return capital;
}
public static void main(String[] args) {
Optional<Country> africanCapital = Optional.of(new Country(Optional.of("Johannersburg")));
Optional<Country> europeanCapital = Optional.of(new Country(Optional.of("Rome")));
Optional<Country> asianCapital = Optional.of(new Country(Optional.of("Seoul")));
List<Optional<Country>> capitalList = Arrays.asList(africanCapital, europeanCapital, asianCapital);
System.out.println(capitalList
.stream()
.map(x -> x.map(Country::getCapital)).collect(Collectors.toList()));
System.out.println(capitalList
.stream()
.map(x -> x.flatMap(Country::getCapital)).collect(Collectors.toList()));
}
}
The outcome is:
[Optional[Johannersburg], Optional[Rome], Optional[Seoul]]
In this example, you have wrapped three Optional Country objects into a list.
Then, we print the list item using the flatMap() method. What it does is give back a list with no additional Optional.
If we were going to use the map() method to perform the same, the code would look like this:
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class Country {
private Optional<String> capital;
public Country(Optional<String> capital) {
this.capital = capital;
}
public Optional<String> getCapital() {
return capital;
}
public static void main(String[] args) {
Optional<Country> africanCapital = Optional.of(new Country(Optional.of("Johannersburg")));
Optional<Country> europeanCapital = Optional.of(new Country(Optional.of("Rome")));
Optional<Country> asianCapital = Optional.of(new Country(Optional.of("Seoul")));
List<Optional<Country>> capitalList = Arrays.asList(africanCapital, europeanCapital, asianCapital);
System.out.println(capitalList
.stream()
.map(x -> x.map(Country::getCapital)).collect(Collectors.toList()));
}
}
And the outcome would be:
[Optional[Optional[Johannersburg]], Optional[Optional[Rome]], Optional[Optional[Seoul]]]
This is because the map() method returns the Optional and its value. Flatmap() the method gives back a smoother result.
At return type.
Combined with streams.
Related: Java 8 Streams
You shouldn't use it as a private field because it's not serializable (meaning that you wouldn't be able to transform that object into a format we can store in the memory).
You would make the code more complicated to read in method parameters or constructors.
It's critical to highlight that Optional is not a way to avoid all types of null checks. For example, you'll still need to consider the inputs taken in constructors and methods.
I hope that you've found this helpful. Optional is an exciting addition, but be careful not to overuse it as it may potentially kill off its benefits! 😊
Until next time!
🙋🏾♀️