15

I don't understand a couple of things with lambda.

String s = "Hello World";       
Function<Integer, String> f = s::substring;
s = null;
System.out.println(f.apply(5));

Why is the f.apply method still working if s = null. After all, the String object should be deleted by the GC because there is no pointer that points to the object.

One more thing, why don't I need a return statement here?

Function<Integer, String> f = t -> t + "";
5
  • 3
    There absolutely is a pointer to the object. f becomes an object which contains a pointer to "Hello world". a::foo evaluates a and stores a reference to its result. Commented Jul 31, 2018 at 21:36
  • As a side issue, s's initial referent exists in the string constant pool and thus will never be garbage collected (unless the containing class is) Commented Aug 1, 2018 at 5:20
  • 2
    Try to ask only one question per question. Commented Aug 1, 2018 at 7:04
  • 1
    For the first question, also see Why can method reference use non-final variables? Commented Aug 1, 2018 at 7:06
  • @LouisWasserman "Avoid answering questions in comments". Currently this question is on HNQ and the comment has 3 upvotes, that (will definitely) cause false impression on visitors. Commented Aug 1, 2018 at 7:09

3 Answers 3

17

The JLS, Section 15.13.3 describes the runtime evaluation of method references.

The timing of method reference expression evaluation is more complex than that of lambda expressions (§15.27.4). 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; at that point, the result is used as the target reference for the invocation. This means the expression preceding the :: separator is evaluated only when the program encounters the method reference expression, and is not re-evaluated on subsequent invocations on the functional interface type.

(bold emphasis mine)

Basically the reference to s as it is for the method reference is stored for later execution. Here, the string "Hello World" is saved for later execution of the method reference. Because of this, even if you set s to null after the declaration of the method reference, but before you execute the Function, it will still use the string "Hello World".

Setting something to null does not guarantee that the garbage collector will collect it, and it won't guarantee that it's collected immediately.

Also, here, there still is a reference in the method reference, so it won't get garbage collected at all here.

Finally, lambda expression bodies have 2 forms: an expression and a block of statements with (possibly) a return statement. With

Function<Integer, String> f = t -> t + "";

That is an expression. The block statement equivalent would be:

Function<Integer, String> f = t -> { return t + "";};
Sign up to request clarification or add additional context in comments.

8 Comments

In my example JLS means that f is envocing the method and saving the result and when I using the get function he returns me the ruslt. You say that becuase of f is a reference to the method so the object ("Hello World") is still stored in the memory, you say one thing and he say other thing or I didn't understand?
I don't know what example in the JLS you're referring to, but Java does not invoke the method (here, substring) when it encounters the method reference. It will invoke it later when the functional method is invoked, here, apply.
So you are saying that java will save the object (here String) becuase of the pointer to the method (here substring)?
Yes. When the method reference is encountered, s is evaluated and a separate reference to "Hello World" is saved for later execution.
So f indirectly reference to "Hello World"?
|
11

Let's convert that method reference to a lambda and see what happens:

String s = "Hello World";
Function<Integer, String> f = i -> s.substring(i); // Doesn't compile!
s = null;
System.out.println(f.apply(5));

The above doesn't compile because s is being changed outside of the lambda, so it is not effectively final. Therefore, we can deduce that using a method reference caches the value of s before it's actually used.

See: Is method reference caching a good idea in Java 8?

3 Comments

I don't think that is is correct... if you remove the last s = null and decompile the generated classes, you will see that they both (lambda and method references) try to cache the value - it is a different story for a lambda because the rules of "anonymous class" apply
I thought it too, I tried to use annonymos class instead of method reference but it didn't work because I was needed to change "s" to final. This inferring is good for thinking but alot of times I deduce things in programing and somtimes I wasn't right.
@user5327287 exactly. using a lambda is like using an anonymous inner class, but using a method reference is not even close to that. More to read stackoverflow.com/a/33053161/1059372, to be honest, this is a duplicate of that
5

One more thing, why don't I need a return statement here?

When specifying only a single lambda statement, its value is automatically returned from the lambda.

Function<Integer, String> f = s::substring;

Java works with pass-by-value. So, f is a reference copy.

2 Comments

I always thought it by reference and not value.
@user5327287 the proof that it is by reference is that in a method parameter void c(String s) {s = "xx"; } and you main like String s = "bb"; c(s); System.out.println(s); the value will still be 'bb' since non references was changed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.