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 + "";
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 + "";};
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?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With