Function pointer to String method in Java

2019-01-25 05:03发布

问题:

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 + "";

回答1:

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 + "";};


回答2:

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:

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.



标签: java lambda