Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NullPointerException instead of null (JVM Bug?)

I found a strange behaviour in the current version of Java 8. In my opinion the following code should be fine, but the JVM throws a NullPointerException:

Supplier<Object> s = () -> false ? false : false ? false : null;
s.get(); // expected: null, actual: NullPointerException

It doesn't matter, what kind of lambda expression it is (same with java.util.function.Function) or what generic types are used. There can also be more meaningful expressions in place of false ? :. The example above is very short. Here's a more colorful example:

Function<String, Boolean> f = s -> s.equals("0") ? false : s.equals("1") ? true : null;
f.apply("0"); // false
f.apply("1"); // true
f.apply("2"); // expected: null, actual: NullPointerException

However these code pieces run fine:

Supplier<Object> s = () -> null;
s.get(); // null

and

Supplier<Object> s = () -> false ? false : null;
s.get(); // null

Or with function:

Function<String, Boolean> f = s -> {
    if (s.equals("0")) return false;
    else if (s.equals("1")) return true;
    else return null;
};
f.apply("0"); // false
f.apply("1"); // true
f.apply("2"); // null

I tested with two Java versions:

~# java -version

openjdk version "1.8.0_66-internal" OpenJDK Runtime Environment (build 1.8.0_66-internal-b01) OpenJDK 64-Bit Server VM (build 25.66-b01, mixed mode)

C:\>java -version

java version "1.8.0_51" Java(TM) SE Runtime Environment (build 1.8.0_51-b16) Java HotSpot(TM) 64-Bit Server VM (build 25.51-b03, mixed mode)

like image 643
steffen Avatar asked Aug 12 '15 16:08

steffen


1 Answers

This has nothing to do with lambda expressions; it is simply that the return type of the ternary operator in that case is boolean, so auto-unboxing will be used.

NPE is thrown here as well:

public class Main {

    private static Object method() {
        return false ? false : false ? false : null;
    }

    public static void main(String[] args) {
        System.out.println(method());
    }
}

So, what exactly is happening here?

Firstly, 'embedded' expression (false ? false : null) is evaluated according to JLS §15.25:

The conditional operator is syntactically right-associative (it groups right-to-left). Thus, a?b:c?d:e?f:g means the same as a?b:(c?d:(e?f:g)).

The type of the embedded expression is Boolean (boxed boolean), so that both false and null can fit into it.

Then the whole expression is:

false ? false : (Boolean expression)

Then, again according to JLS §15.25:

If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.

So, the first argument is of primitive type boolean (T in the spec), the type of the other is the boxed T (Boolean), so the type of the entire expression is boolean.

Then, in runtime, the embedded expression evaluates to null which is auto-unboxed to boolean, causing the NPE.

like image 184
Dragan Bozanovic Avatar answered Nov 02 '22 10:11

Dragan Bozanovic