4

I would expected that both put operations throw a NullPointerException in the following code, but actually the lambda expression works fine while just the method reference throws a NPE.

public static void main(String... args) {
    Object object = null;
    Map<String, FuncInterface> map = new HashMap<>();

    map.put("key1", () -> object.notify());    // works
    map.put("key2", object::notify);           // throws NPE
}

@FunctionalInterface
private interface FuncInterface {
    public void someAction();
}

What is the difference?

newur
  • 490
  • 4
  • 12

1 Answers1

8

The lambda is evaluated when it's called: if you called map.get("key1").someAction() you would get a NPE.

The method reference is evaluated at creation time, i.e. when you first write object::notify, which throws a NPE straight away.

In particular, the JLS 15.13.3 states:

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.

newur
  • 490
  • 4
  • 12
assylias
  • 321,522
  • 82
  • 660
  • 783
  • Please excuse a .net developer that only occasionally diverts to java, but since object isn't marked as final which rule(s) allows this to compile in the first place (but does complain when you actually assign an object yet again does _not_ whine about the null reference when you do not assign an instance to final Object object) ;-) edit: oh, must be something about being "effectively final"; never mind. – VolkerK Dec 30 '16 at 11:43
  • Not sure I fully understand your question but the compiler doesn't do null checks. You can write `Object o = null; o.toString();` if you want: it will compile. – assylias Dec 30 '16 at 12:06
  • If you change `Object object = null` to `Object object = new Object()`, the compiler will complain about object not being final in the lambda. On the other hand, when you do mark it as final but keep it `null` the compiler does not complain about `object::notify` i.e. null::notify. But yes, I've only noticed after posting my "question" that the compiler did check for "effectively final", so the behaviour is not as inconsistent as I first thought. – VolkerK Dec 30 '16 at 12:12
  • Sounds like the correct answer for me. Strange that many tutorials recommend method references instead of lambdas without mentioning this difference. Even better would be no difference... – newur Jan 05 '17 at 14:19
  • 1
    @newur This Q&A is informative: http://stackoverflow.com/questions/30514995/what-is-the-difference-between-a-lambda-and-a-method-reference-at-a-runtime-leve – assylias Jan 05 '17 at 14:22