28

I've encountered a bug in my codebase, I've narrowed down to what caused this behavior. The first test case fails, whereas the last two succeed.

@Test
public void testBooleanNull1() {
    Boolean nullB = null;
    assertFalse(Boolean.valueOf(nullB));
}

@Test
public void testBooleanNull2() {
    String nullS = null;
    assertFalse(Boolean.valueOf(nullS));
}
@Test
public void testBooleanNull3() {
    assertFalse(Boolean.valueOf(null));
}

I know that Boolean.valueOf is an overloaded method with two variants one takes a String and the other takes a primitive of type boolean.

I suspect that this is happening because of auto-boxing but I'm not sure if that is the case, furthermore I don't know why null is being converted to a Boolean as far as I know null is not a valid primitive type.

I've moved on to using BooleanUtils from Apache Commons, I asked this here to better understand why the behavior is this way.

nikhil
  • 8,925
  • 21
  • 62
  • 102

4 Answers4

31

This

Boolean.valueOf(nullB)

is an invocation of Boolean#valueOf(boolean).

It fails because the unboxing of the Boolean value nullB fails with a NullPointerException. In other words, it becomes

boolean val = nullB.booleanValue(); // at runtime nullB stores the value null
Boolean.valueOf(val)

This process is described in the JLS here

If r is a reference of type Boolean, then unboxing conversion converts r into r.booleanValue()

This

Boolean.valueOf(null)

is invoking the overloaded version that accepts a String (since null is not a valid expression of type boolean).

Returns a Boolean with a value represented by the specified string. The Boolean returned represents a true value if the string argument is not null and is equal, ignoring case, to the string "true".

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
5

Overloaded method resolution is described in JLS:

15.12.2. Compile-Time Step 2: Determine Method Signature

...

  1. The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.

In our case, choosing between Boolean.valueOf(boolean) and Boolean.valueOf(String) compiler chooses valueOf(String) because it does not required unboxing.

Community
  • 1
  • 1
Evgeniy Dorofeev
  • 133,369
  • 30
  • 199
  • 275
2

Taking a look at the source code of class Boolean

public static Boolean valueOf(String s) {
    return toBoolean(s) ? TRUE : FALSE;
}

.. and then:

private static boolean toBoolean(String name) {
   return ((name != null) && name.equalsIgnoreCase("true"));
}

If the String argument is null for Boolean.valueOf(String s), it will return false, hence the second case succeeds.

In the first case, on the use of Boolean.valueOf which takes the data type as boolean, here is what the documentation states:

public static Boolean valueOf(boolean b)

Returns a Boolean instance representing the specified boolean value. If the specified boolean value is true, this method returns Boolean.TRUE; if it is false, this method returns Boolean.FALSE. If a new Boolean instance is not required, this method should generally be used in preference to the constructor Boolean(boolean), as this method is likely to yield significantly better space and time performance.

Parameters:
b - a boolean value.
Returns:
a Boolean instance representing b.

The parameter required here is a boolean value which accepts True or False and not Boolean value which accepts True , False or null. Since you passed a Boolean value and not boolean value, I guess that's the reason it fails.

parul1288
  • 29
  • 3
1

Why the second one will succeed? Look at the doc of public static Boolean valueOf(String s):

Returns a Boolean with a value represented by the specified string. The Boolean returned represents a true value if the string argument is not null and is equal, ignoring case, to the string "true".

You passed a null in, so it will return false.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Jason Hu
  • 6,239
  • 1
  • 20
  • 41