Consider following class:
class Foo<T> {
void handle(T t) {
System.out.println("handling " + t);
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
Foo<Integer> f = new Foo<>();
list.forEach(f::handle); // compiles fine
//list.forEach(obj -> f.handle(obj));// compilation error
f = new Foo<>(); // reassign f
}
}
Why do I get compilation error for obj -> f.handle(obj)
, but not for f::handle
?
Those are two different constructs that are doing two different things. In the first case, you are getting the method reference of a specific object: this needs to be done only once, after which the JVM has its own reference (so effectively final) to the object f
and can call the handle
method. In the second case, at each call the JVM has to resolve the f
reference and it thus complains that f
must be final
. You could easily write code that sets f
to null
while the forEach
is running and thus cause a NPE.
To add an illustration to Giovanni's answer, we can highlight the difference between f::handle
and obj -> f.handle(obj)
if we replace f
with a method call:
static Set<String> f() {
System.out.println(" f() called");
return new HashSet<>();
}
public static void main(String[] args) {
List<String> empty = Collections.emptyList();
List<String> strings = Arrays.asList("foo", "bar");
System.out.println("method reference, no invocations");
empty.forEach(f()::add);
System.out.println("method reference, 2 invocations");
strings.forEach(f()::add);
System.out.println("lambda, no invocations");
empty.forEach(str -> f().add(str));
System.out.println("lambda, 2 invocations");
strings.forEach(str -> f().add(str));
}
Output:
method reference, no invocations
f() called
method reference, 2 invocations
f() called
lambda, no invocations
lambda, 2 invocations
f() called
f() called
So, as you see .forEach(f()::add)
will evaluate f()
right away and then call add(...)
on the result as many times as the lambda is called.
On the other hand, str -> f().add(str)
will not do anything upfront but will call f() every time the lambda is invoked.