Java Pitfalls: Unboxing null Booleans

scottshipp

scottshipp

Posted on November 19, 2021

Java Pitfalls: Unboxing null Booleans

Photo by Vlad Bagacian from Pexels

Java is a great language but it can really bite you if you don't watch out! Here's the first installment of a series of helpful things to watch out for in Java!

😰 Today's Pitfall

The following code sometimes throws a NullPointerException. Can you guess why?

public boolean checkIfDelivered(Food food) {
    if(food == null) {
        return false;
    } else {
        return food.wasDelivered();
    }
}
Enter fullscreen mode Exit fullscreen mode

It's hard to imagine a NullPointerException happening here, isn't it? Considering the null check?

Yet, here is the exception you may see from Java code like this:

Exception in thread "main" java.lang.NullPointerException
    at com.company.MyClass.checkIfDelivered(MyClass.java:20)
    at com.company.MyClass.main(MyClass.java:13)
Enter fullscreen mode Exit fullscreen mode

OK so it's actually impossible to see the problem without another piece of vital information. Here's part of the Food interface you should know about.

interface Food {
    Boolean wasDelivered();
}
Enter fullscreen mode Exit fullscreen mode

🤔 Why it happens

This all has to do with conflicting types. The return type of Food.wasDelivered() is Boolean, with a capital "B." But the return type of the checkIfDelivered() method is boolean.

Yes, that's right: it's all because of one letter. 😆

Well, that's not entirely fair. There's a lot more to it than a letter: boolean is a primitive type in Java, whereas Boolean is a special class called a "wrapper class."

Primitive types in Java are represented by reserved keywords like boolean, int, and char. Notice that they are always shortened and lowercased.

Every primitive can be "wrapped" in a corresponding Java class called a "wrapper class." Each wrapper class is capitalized according to Java's class naming convention and lengthened to its full name. int becomes Integer, char becomes Character, and boolean becomes Boolean.

The Java tutorial explains it this way:

…the Java platform provides wrapper classes for each of the primitive data types. These classes "wrap" the primitive in an object. Often, the wrapping is done by the compiler—if you use a primitive where an object is expected, the compiler boxes the primitive in its wrapper class for you. Similarly, if you use a number object when a primitive is expected, the compiler unboxes the object for you.

—From the Oracle Java Tutorial

By the way, it is more common to use the terms "boxing" and "unboxing" instead of "wrapping" and "unwrapping." You can read more about autoboxing in the Java tutorial.

The automatic "unboxing" of a Boolean to a boolean is exactly the problem with the code snippet above! The food.wasDelivered() method returns a Boolean object, which means it can also return null. The checkIfDelivered method returns a primitive boolean, which means if you try and return food.wasDelivered() the compiler attempts to unbox it.

And…BOOM! 💥 When it is null, a NullPointerException results.

👀 Where you'll see it

It's common these days to retrieve data over the network from web services. When that happens, the response is usually in a format like JSON, which does not have the same "primitive" types as Java.

It may be common for the JSON representation of a Food to look like this:

{
    wasDelivered: null
}
Enter fullscreen mode Exit fullscreen mode

Null is an entirely valid value in the JSON schema.

Or it may just be missing altogether, like this:

{}
Enter fullscreen mode Exit fullscreen mode

In either of these cases, most Java developers will use a JSON-to-Java conversion library, such as Jackson, to map the JSON object to a Java object. These libraries can be configured to default the value of wasDelivered on the resulting Java object to false or true, but more often either of the above cases will just result in the value being null.

If the resulting object is a Food object like in our example code, you now know what will happen. 😈

🛠️ How to fix it

It's considered a best practice to prefer the use of primitive types in Java because of the performance overhead that comes from autoboxing. So, if you can, avoid the problem rather than fixing it. Use boolean throughout your code and then you won't have to think that much about what happens if the JVM attempts to box or unbox a value.

But, as you can see from the above example of a JSON response being mapped to a Java object, you're not going to be able to avoid it in some cases. It may even be preferable in our Food example that wasDelivered is stored as a Boolean, because it may be useful as a way to know that the value was null or missing in the JSON response.

So that means you just need to exercise your due diligence when retrieving the value.

The offending method can be rewritten just slightly as follows, and all will be well:

    public boolean checkIfDelivered(Food food) {
        if(food == null || food.wasDelivered() == null) {
            return false;
        } else {
            return food.wasDelivered();
        }
    }
Enter fullscreen mode Exit fullscreen mode

You may find that you write a lot of boilerplate methods like the above. In that case, wouldn't it be nice to have a utility to use instead? The popular Apache Commons Lang library has the exact thing you need.

It's called BooleanUtils and has two nice methods you should be aware of. The first, toBoolean, translates the wrapper Boolean to false if it has a null value. The second, toBooleanDefaultIfNull, lets you explicitly define the behavior, meaning you can default to true if that makes more sense for your case.

Here is our offending method rewritten with toBoolean:

public boolean checkIfDelivered(Food food) {
    if(food == null) {
        return false;
    } else {
        return BooleanUtils.toBoolean(food.wasDelivered());
    }
}
Enter fullscreen mode Exit fullscreen mode

Homework

Now that you know a little more about autoboxing, don't forget to read the Java tutorial to learn more.

💖 💪 🙅 🚩
scottshipp
scottshipp

Posted on November 19, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related