Search code examples
javanullpointerexceptionoption-type

Optional.ifPresent throws NPE when action is null AND optional not present


Why does this throw a NullPointerException? The docs for Java 8 state that: "If a value is present, invoke the specified consumer with the value, otherwise do nothing."

    public static void main(String[] args) {

        List<String> uninitialisedList = getList();
        Optional<String> opt = Optional.empty();
        opt.ifPresent(uninitialisedList::add);
    }

    private static List<String> getList() {
        return null;
    }

Solution

  • This is because the method reference expression uninitialisedList::add throws a NullPointerException when uninitialisedList is null. From the Java Language Specification:

    First, if the method reference expression begins with an ExpressionName or a Primary, this subexpression is evaluated. If the subexpression evaluates to null, a NullPointerException is raised, and the method reference expression completes abruptly.

    Even though the add method is not actually called (as the documentation for ifPresent says, it will not be called in this case), the method reference expression still needs to be evaluated before calling ifPresent, because it is an argument of ifPresent. And evaluating the method reference expression is when the NullPointerException is thrown.

    Intuitively, uninitialisedList::add is a "reference" to the add method on uninitialisedList. If uninitialisedList is null, there is no such method, so it is natural for an exception to be thrown.

    Compare this with a lambda expression:

    opt.ifPresent(x -> uninitialisedList.add(x));
    

    Evaluating the lambda expression does not throw a NullPointerException (see the relevant section in the JLS). It just creates a new object implementing the functional interface. As long as the lambda body is not executed, it's all fine.