4

Why does the code not throw a NullPointerException when I use a method reference tied to a variable dog which I later assigned null to?

I am using Java 8.

import java.util.function.Function;

class Dog {
    private int food = 10;

    public int eat(int num) {
        System.out.println("eat " + num);
        this.food -= num;
        return this.food;
    }
}

public class MethodRefrenceDemo {

    public static void main(String[] args) {
        Dog dog = new Dog();
        Function<Integer, Integer> function = dog::eat;

        dog = null;

        // I can still use the method reference
        System.out.println("still have " + function.apply(2));
    }
}
Zabuzard
  • 25,064
  • 8
  • 58
  • 82
jg23 z
  • 39
  • 2
  • 2
    The function is defined with the object `dog` is pointing at, it is not concerned with the `dog` variable itself. – RealSkeptic Jun 05 '19 at 09:13
  • if you call dog.something, will throw the NullPointerException, but your code not. – verejava Jun 05 '19 at 09:14
  • 1
    Related: [Why can method reference use non-final variables?](https://stackoverflow.com/questions/33052917/why-can-method-reference-use-non-final-variables) – Mark Rotteveel Jun 05 '19 at 09:18
  • I [answered](https://stackoverflow.com/questions/30514995/what-is-the-difference-between-a-lambda-and-a-method-reference-at-a-runtime-leve/33966159#33966159) this for another question. I don't think the question is a duplicate, though. – Lii Jun 05 '19 at 09:31
  • 1
    Also related [Is Java “pass-by-reference” or “pass-by-value”?](https://stackoverflow.com/questions/40480/is-java-pass-by-reference-or-pass-by-value) – Zabuzard Jun 05 '19 at 09:32
  • i think the key point is : "When a method reference expression has an expression (rather than a type) preceding the :: separator, that subexpression is evaluated immediately. The result of evaluation is stored until the method of the corresponding functional interface type is invoked;", not " Java is “pass-by-reference” or “pass-by-value”". @Zabuza – jg23 z Jun 05 '19 at 09:46
  • 1
    The pass-by was more meant to be related in a sense that it explains the difference between variables and instances. That nulling a variables does not invalidate the instance, only the variable. Combine that with the more important fact (as others pointed out) that method references dont take your variable but the actual instance behind it. But yeah, thats why I only said related and not duplicate or anything. But the current linked duplicate is pretty good, with JLS excerpts. – Zabuzard Jun 05 '19 at 10:08
  • Nonetheless, your question is a good contribution. Its well asked and the title is better than the duplicate, so people will likely find it when searching for similar problems. – Zabuzard Jun 05 '19 at 10:09

2 Answers2

6

The dog::eat method reference captures the instance referenced by dog, so when you call function.apply(2), the eat method is executed for that instance. It doesn't matter that the dog variable no longer references that instance.

Eran
  • 387,369
  • 54
  • 702
  • 768
1

The variable dog used at the lambda expression is visible only at the scope of the lambda expression since its definition and nullifying the dog will not affect the method reference dog::eat.

An example without usage of dog with the same functionality:

Function<Integer, Integer> function = new Dog()::eat;
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183