Java 8:方法引用Bound Receiver和UnBound Receiver之间的区别

时间:2016-03-10 11:10:59

标签: java lambda java-8 method-reference

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

  1. 静态方法参考。
  2. 实例方法(绑定接收器)。
  3. 实例方法(UnBound接收器)。
  4. 构造函数参考。
  5. 使用Static method referenceConstructor reference我没有问题,但Instance Method (Bound receiver)Instance Method (UnBound receiver)确实让我很困惑。在Bound接收器中,我们使用Object引用变量来调用类似的方法:

    objectRef::Instance Method
    

    UnBound接收器中,我们使用类名来调用类似的方法:

    ClassName::Instance Method.
    

    我有以下问题:

    1. 对实例方法的不同类型的方法引用有什么需要?
    2. BoundUnbound接收方法引用之间有什么区别?
    3. 我们应该在哪里使用Bound接收器?我们应该在哪里使用Unbound接收器?
    4. 我还从Java 8 language features books找到了对BoundUnbound接收器的解释,但仍然与实际概念相混淆。

6 个答案:

答案 0 :(得分:16)

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

但是Bounded指的是当你在一个方法中调用一个方法时的情况 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

在Action Book

中从Java 8中退出的答案

答案 1 :(得分:9)

基本上,未绑定的接收器允许您使用实例方法,就好像它们是具有声明类型的第一个参数的静态方法一样 - 因此您可以通过传入任何您想要的实例来将它们用作函数。使用绑定接收器," 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;
    }
}

答案 2 :(得分:3)

如果希望为某个类的特定实例执行该方法,则使用绑定接收器。

例如:

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

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

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

例如:

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

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

答案 3 :(得分:0)

伴随着上面的优秀答案。 感谢joshua bloch的精彩解释,有效的java第三版。我终于能够绕过有界和无界的参考手段。

  

在有界引用中,接收对象在方法中指定   参考。绑定引用在本质上与静态类似   引用:函数对象采用与。相同的参数   参考方法。

     

在未绑定的引用中,接收对象在指定时指定   函数对象通过前面的附加参数应用   方法的声明参数。未绑定的引用通常用作   流管道中的映射和过滤功能

     

最后,对于类,有两种构造函数引用   和数组。构造函数引用充当工厂对象。

`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]`

答案 4 :(得分:0)

我是从最近的演讲中捕捉到的

enter image description here

答案 5 :(得分:0)

这是一个例子:

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