JDK-8131323 : java.lang.NullPointerException when lambda replaced by method reference
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 8,9
  • Priority: P4
  • Status: Closed
  • Resolution: Not an Issue
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2015-07-14
  • Updated: 2015-07-27
  • Resolved: 2015-07-15
Related Reports
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.3.9600]

A DESCRIPTION OF THE PROBLEM :
If we replace lambda by method reference, than NPE thrown

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run

public class JdkTest {
    private StoreView storeView;

    class ConfigItem<T> {
        private final Supplier<T> getter;

        public ConfigItem(final Supplier<T> getter) {
            this.getter = getter;
        }
    }

    private ConfigItem<String> supportUser = new ConfigItem<>(() -> storeView.get());
    private ConfigItem<String> supportUser2 = new ConfigItem<>(storeView::get);

    private class StoreView {
        public String get() {
            return "123";
        }
    }

    public static void main(String[] args) {
        new JdkTest();
    }
}

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
successfully finished
ACTUAL -
NullPointerException 

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.NullPointerException
	at com.muranosoft.wrike.persistence.service.zendesk.JdkTest.<init>(JdkTest.java:15)
	at com.muranosoft.wrike.persistence.service.zendesk.JdkTest.main(JdkTest.java:36)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class JdkTest {
    private StoreView storeView;

    class ConfigItem<T> {
        private final Supplier<T> getter;

        public ConfigItem(final Supplier<T> getter) {
            this.getter = getter;
        }
    }

    private ConfigItem<String> supportUser = new ConfigItem<>(() -> storeView.get());
    private ConfigItem<String> supportUser2 = new ConfigItem<>(storeView::get);

    private class StoreView {
        public String get() {
            return "123";
        }
    }

    public static void main(String[] args) {
        new JdkTest();
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
    private StoreView storeView = new StoreView();



Comments
jdk 8 b119 - not repeatable: no NPE's
15-07-2015

JLS 15.13.3. (Run-Time Evaluation of Method References) says: 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. If the subexpression completes abruptly, the method reference expression completes abruptly for the same reason. So, to me, the NPE seems to be correct?
15-07-2015

This is not an issue - note that the lambda features fully lazy evaluation - i.e. 'storeView' is not touched until execution of the lambda body; on the other hand, storeView::get is a bound method references, and the receiver part of a bound method reference is always evaluated upon construction. In other words, this example is not different than something like: class Bar { Baz baz() { return null; } } class Baz { } class Foo { Bar bar; Baz baz = bar.baz(); // <----------- } In the highlighted line above, the program will attempt to dereference a 'null' value, which is the default value to which field 'bar' is initialized (in the absence of an explicit initializer). Here's some relevant spec text: 15.13.3. Run-Time Evaluation of Method References At run time, evaluation of a method reference expression is similar to evaluation of a class instance creation expression, insofar as normal completion produces a reference to an object. Evaluation of a method reference expression is distinct from invocation of the method itself. *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. If the subexpression completes abruptly, the method reference expression completes abruptly for the same reason.*
15-07-2015

1. Run the attached code (JDKTest.java). 2. Checked this for JDK 8, 8u45, 8u51, 8u60 ea b23, 9 ea b72 8: FAIL 8u45: FAIL 8u51: FAIL 8u60 ea b23: FAIL 9 ea b72: FAIL 3. Output with JDK 8u45: > java JdkTest Exception in thread "main" java.lang.NullPointerException at java.util.Objects.requireNonNull(Objects.java:203) at JdkTest.<init>(JdkTest.java:15) at JdkTest.main(JdkTest.java:24) 4. This issue is reproducible with JDK 8-all and 9 ea b72.
15-07-2015