Why lambda expressions in Java 8 requires variable

2019-02-09 05:51发布

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?

2条回答
爷的心禁止访问
2楼-- · 2019-02-09 05:52

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.

查看更多
做个烂人
3楼-- · 2019-02-09 06:13

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.

查看更多
登录 后发表回答