DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Unraveling Lombok's Code Design Pitfalls: Exploring Encapsulation Issues
  • Dependency Injection in Spring
  • Java: A Time-Tested Programming Language Still Going Strong
  • Legacy Code Refactoring: Tips, Steps, and Best Practices

Trending

  • *You* Can Shape Trend Reports: Join DZone's Software Supply Chain Security Research
  • How to Build Local LLM RAG Apps With Ollama, DeepSeek-R1, and SingleStore
  • Scalable, Resilient Data Orchestration: The Power of Intelligent Systems
  • Stateless vs Stateful Stream Processing With Kafka Streams and Apache Flink
  1. DZone
  2. Coding
  3. Languages
  4. NullPointerException in Java: Causes and Ways to Avoid It

NullPointerException in Java: Causes and Ways to Avoid It

This article explains what NullPointerException is with its common causes and best practices you can do to avoid NPE. Read on to find out more!

By 
Dmitry Egorov user avatar
Dmitry Egorov
DZone Core CORE ·
Updated Jan. 04, 22 · Analysis
Likes (18)
Comment
Save
Tweet
Share
24.9K Views

Join the DZone community and get the full member experience.

Join For Free

NullPointerException: The Most Common Exception

NullPointerException (NPE) is the most common exception in Java. The cause of this exception is known but looks like that in most cases developers prefer to ignore it and don't take any action. I personally expect that reasons for such behavior are the following: 

  • Most of the developers don't see any problem here and recognize all NPE exceptions as the developer's fault. 
  • Developers who are aware of this design problem don't know how to fix it. 

In this article, I'll explain what is the root of this problem and provide ways to fix that problem.

The Root of the Problem: Java Weak Type Safety

Have you heard of Compile Type Safety? If not in this article you can find out what it is and what is the difference between Compile-Time and Type Safety.

Java provides Compile Type Safety and it gives a guarantee to the developer that he can't mismatch different variables types. And, if you did - Java would let him know it even on the compilation step. In the example above we try to assign to String variable Integer value:

String variable.

Java Type Safetiness Ruined by Null Reference

Java validates the type of the variable and the type of assigned value during compilation. What is the problem then? Well, the problem is the NULL value. Null values stand for all non initialized objects. And, as far as any object can be initialized then a Null Value can be assigned to any type.

String venn diagram.

So, Java allows next assignments: 

Java assignments.

What is wrong here? Objects are not initialized so they point to the Null Reference. It seems very natural but actually, it's the root of great evil.

NullPointerException: Consequence of Weak Type Safeties

As far as Java see no difference between Null and real object it leads to impossible operations like the next one below:Impossible operations.


So, from a compiler perspective, there is nothing wrong. Null belongs to String type and Java will not even print a warning. Actually, you can compile even the next code:Compilable code.


But, once we run this program, it will fail with NullPointerException:

NullPointerException.

NullPointerException Definition

NullPointerException is a Runtime exception that is thrown when Java tries to call any method on a real object but in runtime this object references to the Null Reference. More details about exceptions and their nature you can find in this article.

Why Is NullPointerException the Most Popular Exception? 

Developers are humans and use to make mistakes to forget and to haste. As a consequence, they miss to:

  • Initialize objects
  • Validate objects 

There is no cure for human nature and there is nothing to do with it. What are the practical ways to avoid NPE? Let's review an example in the next chapter and try to fix it.  

Explaining NullPointerException Using the User Address Example

In our example, we have a User object with an Address field. Potentially, both of them might be null. Let's see how we can avoid NullPointerException. 

Potential Null pointer exceptions.


Avoiding NullPointerException Using Simple != Null Check

Now, let's prevent this problem by simple checking, not null checking:

Simple checking.

Can we improve this solution? Yes, we can use Optional. Using map function we can write equivalent similar to the previous statement: 

Using map function with Optional.


Does optional provide benefits compared to simple null check? Yes, it does. Optional reinsures us that data we use in ifPresent lambda is not null. But, what if the user or address is null? Then, ifPresent will be silently ignored. 

And, even if we forget to use Optional features, the idea will highlight .get() reminding us to provide the design with a null check. 

Idea highlight that value might be empty.


Optional Was Released in 2014. Why Is It Not That Popular?

The optional feature was released in Java 1.8 but it's not that widely used.  There are a couple of reasons:

  • It's very verbose and pollutes the code (Personally I think it's the main reason, Java itself is pretty wordy and with Optional it's become ridiculously big).
  • It's unclear, behind all map/flatmap/ifpresent you can lose the point of the logic. So ugly null check is straightforward and obvious.
  • Optional itself might lead developers to create more NPEs eg by using Optional.of(nullable).

So, for the mentioned reasons some teams prefer to use null checks. And in order to avoid any NPE exceptions, cover such logic with a bunch of tests.

Null Check and Optional Do They Solve The Problem? No. 

Two "solutions" are shown above, are they really solutions? Null check together with Optional serving to the same purpose -- to provide validation for data that might be null. Additionally, Optional reminds the developer that returned value can be null. But, in general, the key problem is hidden in human nature -- to forget or miss potential null scenarios. We need a solution that will point to the developer what he missed on the compilation step.

@NotNull @Nullable Annotation Processors Help to Identify Potential Nulls. 

We need a solution that can read our code on the compilation step and notify us that we missed a potential NPE scenario. For this purpose, we can use Java Annotation Processors. Java Annotation Processors have many purposes but can be used for our case as well. In this article, you can find an example of how to use an annotation processor in order to check mutability.

There are a couple of annotation processors related to NPE problems. Not all of them are the same and follow completely different approaches. In the next chapter, we review existent @NotNull and @Nullable annotations providers.

Lombok @NotNull Annotation

Lombok @NotNull Annotation is used to generate not null checks that can prevent execution but only in Runtime. So it doesn't fit our purpose. Shortly, this annotation does the next thing:

Annotation.


Checker Framework @NonNull @Nullable Annotations Processors

Checker Framework provides @NonNull and @Nullable annotations together with a compiler processor step that can identify potential null checks. The framework can find potential nulls by forcing developers to specify Nullability. So, whenever you return something you have to explicitly declare can returned result be Nullable or NotNullable... Let's take a look at the next example:

A simple method that might potentially return Null instead of String:

Simple method to return Null instead of String.

Now, let's use our Checker Framework and see if it would be happy to compile it:
Use Checker Framework.


No, it's not happy at all. It says that we return a potentially nullable string and that it isn't marked with @Nullable annotation. Now, let's mark it as @Nullable, and try to use it:

Use the @Nullable annotation.


Will that framework find anything wrong in that code? Let's run compilation check again:

Run the compilation.


So, it finds a potential problem at line 19 where we try to call .length() on Nullable string. Let's fix it using Null check and Optional ifPresent:

Fix problem using Null check and Optional ifPresent.


And, after compilation, we get a successful build:

Successful build.


Checker Framework Limitations

So far Checker Framework shows good results and highlighted potential NPE. But, at what cost? Now we are obliged to mark all potentially Nullable methods by the @Nullable method. It seems it's a mandatory step, and we can't avoid it. But, it's not the only limitation. Let's create a simple class with two fields one of those we mark as @NonNull:

Simple class with two fields.


Will Checker Framework accept this code? 

Will Checker Framework accept this code?

Checker Framework forces us to have a constructor that initializes id value, eg like this:

Constructor.


So, Framework did not just identify potential NPE, but also force us to follow specific requirements or designs. Actually, this requirement is crucial. And, it's questionable. Should we sacrifice such development flexibility just to fit framework limitations? It's an open question. To play with Checker Framework, you can fetch my example here:

git clone https://github.com/isicju/checker_framework_example


To run your Checker Framework run the following:

mvn clean compile


Checker Framework Alternative: Intellij Idea @NotNull Annotations

Checker Framework is not the only solution and Intellij Idea provides its own annotations @NotNull and @Nullable together with embedded in IDE plugin. Unfortunately, I haven't found a way to add it in the maven compilation step. So, if it exists please let me know in the comments, I'll test it and add it to the article.  

Conclusions and My Personal Opinion

Summarizing the whole article I recommend the following:

  • Prefer Optional instead of passing Null
  • Use Checker Framework 

Honestly, in practice, Checker Framework brings limitations to your development and it's questionable if it is worth it or not. If I had to implement my own solution and it had to be stable in production, I would use Checker Framework even if I had to get rid of Lombok or even Builder Pattern.

Java (programming language) Framework Annotation IT intellij dev Object (computer science)

Opinions expressed by DZone contributors are their own.

Related

  • Unraveling Lombok's Code Design Pitfalls: Exploring Encapsulation Issues
  • Dependency Injection in Spring
  • Java: A Time-Tested Programming Language Still Going Strong
  • Legacy Code Refactoring: Tips, Steps, and Best Practices

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!