116

I have this code:

package tests;

import java.util.Hashtable;

public class Tests {

    public static void main(String[] args) {

        Hashtable<String, Boolean> modifiedItems = new Hashtable<String, Boolean>();

        System.out.println("TEST 1");
        System.out.println(modifiedItems.get("item1")); // Prints null
        System.out.println("TEST 2");
        System.out.println(modifiedItems.get("item1") == null); // Prints true
        System.out.println("TEST 3");
        System.out.println(Boolean.valueOf(null)); // Prints false
        System.out.println("TEST 4");
        System.out.println(Boolean.valueOf(modifiedItems.get("item1"))); // Produces NullPointerException
        System.out.println("FINISHED!"); // Never executed
    }
}

My problem is that I don't understand why Test 3 works fine (it prints false and doesn't produce NullPointerException) meanwhile Test 4 throws a NullPointerException. As you can see in tests 1 and 2, null and modifiedItems.get("item1") are equals and null.

The behavior is the same in Java 7 and 8.

Christian Dean
  • 22,138
  • 7
  • 54
  • 87
David E
  • 1,197
  • 2
  • 8
  • 6
  • modifiedItems.get("item1") this is null, you are aware of that, but you assume that passing this to a valueOf will not end up in a NPE? – Stultuske Oct 06 '17 at 08:06
  • 16
    @Stultuske: It's a valid question, given that just two lines above passing a literal `null` to the same function *doesn't* generate an NPE! There's a good reason for it, but it's certainly confusing at first sight :-) – psmears Oct 06 '17 at 13:55
  • 25
    I'm impressed. This is the most interesting null pointer exception question I've seen in years. – candied_orange Oct 06 '17 at 21:21
  • @Jeroen this isn't a dupe of [that question](https://stackoverflow.com/questions/3352791/why-comparing-integer-with-int-can-throw-nullpointerexception-in-java). Whilst it is true that unboxing is common to the two problems, there is no comparison going on here. The key thing about this question is that it occurs because of the way overloads are resolved; and that's quite a different thing from how `==` is applied. – Andy Turner Oct 11 '17 at 08:12

5 Answers5

181

You've got to look carefully at which overload is being invoked:

  • Boolean.valueOf(null) is invoking Boolean.valueOf(String). This doesn't throw an NPE even if supplied with a null parameter.
  • Boolean.valueOf(modifiedItems.get("item1")) is invoking Boolean.valueOf(boolean), because modifiedItems's values are of type Boolean, which requires an unboxing conversion. Since modifiedItems.get("item1") is null, it is the unboxing of that value - not the Boolean.valueOf(...) - which throws the NPE.

The rules for determining which overload is invoked are pretty hairy, but they roughly go like this:

  • In a first pass, a method match is searched for without allowing boxing/unboxing (nor variable arity methods).

    • Because null is an acceptable value for a String but not boolean, Boolean.valueOf(null) is matched to Boolean.valueOf(String) in this pass;
    • Boolean isn't an acceptable for either Boolean.valueOf(String) or Boolean.valueOf(boolean), so no method is matched in this pass for Boolean.valueOf(modifiedItems.get("item1")).
  • In a second pass, a method match is searched for, allowing boxing/unboxing (but still not variable arity methods).

    • A Boolean can be unboxed to boolean, so Boolean.valueOf(boolean) is matched for Boolean.valueOf(modifiedItems.get("item1")) in this pass; but an unboxing conversion has to be inserted by the compiler to invoke it: Boolean.valueOf(modifiedItems.get("item1").booleanValue())
  • (There's a third pass allowing for variable arity methods, but that's not relevant here, as the first two passes matched these cases)

Robin V
  • 83
  • 4
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • 3
    Could code be more clear if we use `Boolean.valueOf(modifiedItems.get("item1").booleanValue())` in source code instead of `Boolean.valueOf(modifiedItems.get("item1"))` ? – CausingUnderflowsEverywhere Oct 06 '17 at 17:34
  • 1
    @CausingUnderflowsEverywhere not really - it is really hard to see that `.booleanValue()` buried in the expression. Two observations: 1) auto(un)boxing is a deliberate feature of Java to remove syntactic cruft; doing it yourself is possible, but not idiomatic; 2) this doesn't help you at all - it certainly doesn't stop the problem from occurring, nor does it give any extra info when the failure does occur (the stack trace would be identical, because the executed code is identical). – Andy Turner Oct 07 '17 at 13:15
  • @CausingUnderflowsEverywhere it is better to use tooling to highlight the issues, e.g. intellij would earn you about potential NPE here. – Andy Turner Oct 07 '17 at 13:17
13

Since modifiedItems.get returns a Boolean (which is not castable to a String), the signature that would be used is Boolean.valueOf(boolean), where the Boolean is outboxed to a primitive boolean. Once null is returned there, the outboxing fails with a NullPointerException.

Mureinik
  • 297,002
  • 52
  • 306
  • 350
11

Method signature

The method Boolean.valueOf(...) has two signatures:

  1. public static Boolean valueOf(boolean b)
  2. public static Boolean valueOf(String s)

Your modifiedItems value is Boolean. You cannot cast Boolean to String so consequently the first signature will be chosen

Boolean unboxing

In your statement

Boolean.valueOf(modifiedItems.get("item1"))

which can be read as

Boolean.valueOf(modifiedItems.get("item1").booleanValue())   

However, modifiedItems.get("item1") returns null so you'll basically have

null.booleanValue()

which obviously leads to a NullPointerException

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
Al-un
  • 3,102
  • 2
  • 21
  • 40
  • Incorrect wording, thanks for pointing and answer is updated following your feedback. Apologies, I haven't seen your answer while writing and I see that mine looks like yours. Should I remove my answer to avoid confusion for OP? – Al-un Oct 06 '17 at 08:31
  • 4
    Don't delete it on my account. Remember, this isn't a zero-sum game: people can (and do) upvote multiple answers. – Andy Turner Oct 06 '17 at 08:59
3

As Andy already very well described the reason of NullPointerException:

which is due to Boolean un-boxing:

Boolean.valueOf(modifiedItems.get("item1"))

get converted into:

Boolean.valueOf(modifiedItems.get("item1").booleanValue())

at runtime and then it throw NullPointerException if modifiedItems.get("item1") is null.

Now I would like to add one more point here that un-boxing of the following classes to their respective primitives can also produce NullPointerException exception if their corresponding returned objects are null.

  1. byte - Byte
  2. char - Character
  3. float - Float
  4. int - Integer
  5. long - Long
  6. short - Short
  7. double - Double

Here is the code:

    Hashtable<String, Boolean> modifiedItems1 = new Hashtable<String, Boolean>();
    System.out.println(Boolean.valueOf(modifiedItems1.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Byte> modifiedItems2 = new Hashtable<String, Byte>();
    System.out.println(Byte.valueOf(modifiedItems2.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Character> modifiedItems3 = new Hashtable<String, Character>();
    System.out.println(Character.valueOf(modifiedItems3.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Float> modifiedItems4 = new Hashtable<String, Float>();
    System.out.println(Float.valueOf(modifiedItems4.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Integer> modifiedItems5 = new Hashtable<String, Integer>();
    System.out.println(Integer.valueOf(modifiedItems5.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Long> modifiedItems6 = new Hashtable<String, Long>();
    System.out.println(Long.valueOf(modifiedItems6.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Short> modifiedItems7 = new Hashtable<String, Short>();
    System.out.println(Short.valueOf(modifiedItems7.get("item1")));//Exception in thread "main" java.lang.NullPointerException

    Hashtable<String, Double> modifiedItems8 = new Hashtable<String, Double>();
    System.out.println(Double.valueOf(modifiedItems8.get("item1")));//Exception in thread "main" java.lang.NullPointerException
Mohit Tyagi
  • 2,788
  • 4
  • 17
  • 29
0

A way to understand it is when Boolean.valueOf(null) is invoked, java is precisely being told to evaluate null.

However, when Boolean.valueOf(modifiedItems.get("item1")) is invoked, java is told to obtain a value from the HashTable of object type Boolean, but it doesn't find the type Boolean it finds a dead end instead (null) even though it expected Boolean. The NullPointerException exception is thrown because the creators of this part of java decided this situation is an instance of something in the program going wrong that needs the programmer's attention. (Something unintended happened.)

In this case it is more the difference between deliberately declaring that you intended the null to be there, and java finding a missing reference to an object (null) where an object was intended to be found.

See more information about NullPointerException in this answer: https://stackoverflow.com/a/25721181/4425643