9

考虑以下课程:

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

    }
}

为什么我得到编译错误obj -> f.handle(obj),但没有f::handle

4

2 回答 2

15

这是两种不同的构造,它们在做两种不同的事情。在第一种情况下,您将获得特定对象的方法引用:这只需要完成一次,之后 JVM 对对象有自己的引用(非常有效)f并且可以调用该handle方法。在第二种情况下,JVM 在每次调用时都必须解析f引用,因此它抱怨fmust be final。您可以轻松编写在运行时设置f为的代码,从而导致 NPE。nullforEach

于 2014-12-22T21:00:05.280 回答
3

f::handle要为 Giovanni 的答案添加说明,我们可以突出显示与obj -> f.handle(obj)替换f为方法调用之间的区别:

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));
}   

输出:

method reference, no invocations
  f() called
method reference, 2 invocations
  f() called
lambda, no invocations
lambda, 2 invocations
  f() called
  f() called

因此,如您所见,.forEach(f()::add)将立即进行评估f(),然后在调用add(...)lambda 时多次调用结果。

另一方面,str -> f().add(str)不会预先做任何事情,但会在每次调用 lambda 时调用 f()。

于 2014-12-23T04:12:51.790 回答