Java8:方法参考Bound Receiver和UnBound Receiver的区别

Java 8: Difference between method reference Bound Receiver and UnBound Receiver

我正在尝试在我的代码中使用 Java 8 个方法引用。有四种类型的方法引用可用。

  1. 静态方法参考。
  2. 实例方法(绑定接收器)。
  3. 实例方法(未绑定接收器)。
  4. 构造函数参考。

Static method referenceConstructor reference 我没有问题,但是 Instance Method (Bound receiver)Instance Method (UnBound receiver) 真的让我很困惑。在 Bound 接收器中,我们使用一个对象引用变量来调用如下方法:

objectRef::Instance Method

UnBound 接收器中,我们使用 Class 名称来调用如下方法:

ClassName::Instance Method.

我有以下问题:

  1. 实例方法需要不同类型的方法引用是什么?
  2. BoundUnbound 接收器方法引用有什么区别?
  3. 我们应该在哪里使用 Bound 接收器,我们应该在哪里使用 Unbound 接收器?

我也从Java 8 language features books找到了BoundUnboundreceiver的解释,但还是和实际的概念混淆了。

基本上,未绑定的接收器允许您使用实例方法,就好像它们是具有声明类型的第一个参数的静态方法一样 - 因此您可以通过传入任何您想要的实例来将它们用作函数。对于绑定接收器,"target" 实例实际上是函数的一部分。

举个例子可能会更清楚:

import java.util.function.*;

public class Test {

    private final String name;

    public Test(String name) {
        this.name = name;
    }

    public static void main(String[] args) {
        Test t1 = new Test("t1");
        Test t2 = new Test("t2");

        Supplier<String> supplier = t2::method;
        Function<Test, String> function = Test::method;

        // No need to say which instance to call it on -
        // the supplier is bound to t2            
        System.out.println(supplier.get());

        // The function is unbound, so you need to specify
        // which instance to call it on
        System.out.println(function.apply(t1));
        System.out.println(function.apply(t2));
    }

    public String method() {
        return name;
    }
}

当您希望针对某些 class 的特定实例执行该方法时,您可以使用绑定接收器。

例如:

Stream.of("x","y").forEach(System.out::println);

将在 PrintStream 的特定实例 - System.out 实例上执行 println。因此 System.out.println("x")System.out.println("y") 将作为将该方法引用传递给 forEach.

的结果执行

另一方面,如果您希望为 class 的未指定实例执行该方法,您可以使用未绑定的接收器。

例如:

Stream.of("x","y","").filter(String::isEmpty);

将在 Stream 的每个 String 实例上执行 isEmpty() - 即 "x".isEmpty()"y".isEmpty()"".isEmpty().

String::length 等 unBound 接收者的想法是你指的是 将作为 lambda 参数之一提供的对象的方法。例如, lambda 表达式 (String s) -> s.toUpperCase() 可以重写为 String::toUpperCase.

但是有界指的是你在一个方法中调用一个方法的情况 已存在的外部对象 的 lambda。例如,lambda 表达式 () -> expensiveTransaction.getValue() 可以重写为 expensiveTransaction::getValue.

三种不同方法引用的情况

(args) -> ClassName.staticMethod(args) 可以是 ClassName::staticMethod // 这是静态的(你也可以认为是未绑定的)

(arg0, rest) -> arg0.instanceMethod(rest) 可以是 ClassName::instanceMethodarg0 的类型是 ClassName) // 这是未绑定的

(args) -> expr.instanceMethod(args) 可以是 expr::instanceMethod // This is Bound

答案取自 Java 8 in Action book

以及上面的优秀答案。 感谢 joshua bloch 的精彩讲解,有效 java 第三版。我终于能够理解有界和无界引用的含义。

In bounded reference, the receiving object is specified in the method reference. Bound references are similar in nature to static references: the function object takes the same arguments as the referenced method.

In unbound references, the receiving object is specified when the function object is applied, via an additional parameter before the method’s declared parameters. Unbound references are often used as mapping and filter functions in stream pipelines

Finally, there are two kinds of constructor references, for classes and arrays. Constructor references serve as factory objects.

`Method Ref    |     Example                   |    Lambda Equivalent
  Static       |   Integer::parseInt           |   str -> Integer.parseInt(str)
  Bound        |   Instant.now()::isAfter      |   Instant then = Instant.now(); 
                                               |   t -> then.isAfter(t)
  Unbound      |  String::toLowerCase          |   str -> str.toLowerCase()
  Class 
Constructor    |  TreeMap<K,V>::new            |   () -> new TreeMap
  Array 
Constructor    |   int[]::new                  |  len -> new int[len]`

我从最近的演示文稿中捕捉到了这个

这是一个例子:

public static void main(String[] args) {
    // unbound
    UnaryOperator<String> u = String::toUpperCase;
    System.out.println(u.apply("hello"));

    // bound
    String a = "hello";
    Supplier<String> r = a::toUpperCase;
    System.out.println(r.get());
}

这将输出两行HELLO