Using instance method in java 8 :: method reference with null Object

Viewed 2161

Why when using an instance method in method reference (::) does not throw a NullPointerException if we set the Object instance to null?

  public class MethodReferenceTest {

        public static void main(String...strings){

            PredicateSample predicateSample = new PredicateSample();
            Predicate<String> predicate = predicateSample::isNotEmpty;
            // NullpointerException ???
            predicateSample = null;
            Arrays.asList("a","b","c",null)
                .stream()
                .filter(predicate)
                .forEach(System.out::println);
        }

        static class PredicateSample{
            public boolean isNotEmpty(String s){
                return ((s!=null) && (!s.isEmpty()));
            }
        }
    }

It seems like predicateSample is not invoked after constructing the Predicate?

3 Answers

The key line in the language spec is where it is discussing the runtime evaluation of method references (emphasis mine):

  • If the form is ExpressionName :: [TypeArguments] Identifier or Primary :: [TypeArguments] Identifier ...

    • ....
    • The target reference is the value of ExpressionName or Primary, as determined when the method reference expression was evaluated.

So it doesn't matter if you change the value of the target reference afterwards.

The method reference "remembered" the state of the predicateSample when it was initialized. It has a reference exactly to that new PredicateSample() you have created before.

After 2 lines executed, you have got two references to that new PredicateSample() - the predicateSample and the one "within" the method reference.

Changing the former makes no effect to the latter.


Let's do an interesting trick.

Here is an array to wrap up a new PredicateSample() into a predicates[0]:

PredicateSample[] predicates = new PredicateSample[1];
predicates[0] = new PredicateSample();

Within the lambda, you use a reference to the predicates[0];

Predicate<String> predicate = string -> predicates[0].isNotEmpty(string);

You update the predicates[0] to null and do the same stuff:

predicates[0] = null;
Stream.of("a","b","c").filter(predicate).forEach(System.out::println);
PredicateSample predicateSample = new PredicateSample();
Predicate<String> predicate = predicateSample::isNotEmpty; //it will work fine and initializing with false.
            // NullpointerException ???
predicateSample = null;
Arrays.asList("a","b","c",null).stream().filter(predicate).forEach(System.out::println);

in above code you are initializing predicateSample with null and thereafter there is no use of this object predicateSample.

in your code you have write

Predicate<String> predicate = predicateSample::isNotEmpty;
            // NullpointerException ???
predicateSample = null; // null initialize after `isNotEmpty` call. then why Null Pointer will raised ?

now no use of predicateSample further. then why NPE?

Modified code to throw NPE.

PredicateSample predicateSample = new PredicateSample()
// NullpointerException ???
    predicateSample = null;;
    Predicate<String> predicate = predicateSample::isNotEmpty; //it will not work fine and raise NPE.
              Arrays.asList("a","b","c",null).stream().filter(predicate).forEach(System.out::println);
Related