【问题标题】:Java 8: Difference between method reference Bound Receiver and UnBound ReceiverJava 8:方法参考 Bound Receiver 和 UnBound Receiver 的区别
【发布时间】:2022-10-31 22:50:22
【问题描述】:

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

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

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

    objectRef::Instance Method
    

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

    ClassName::Instance Method.
    

    我有以下问题:

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

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

【问题讨论】:

    标签: java lambda java-8 method-reference


    【解决方案1】:

    unBound 接收者的想法,比如String::length,你指的是一个 一个对象的方法将作为 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 // 这是绑定的

    从中检索到的答案Java 8 实战

    【讨论】:

    • 未绑定的接收器是否可以成为采用参数的方法?
    • 如果该方法是从 lambda 参数调用的,并且参数也是 lambda 参数,那么是。看第二个例子ClassName::instanceMethod
    • 对于未绑定的接收器,“...作为 lambda 参数之一提供”:“其中之一”是否应更改为“第一个”?
    【解决方案2】:

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

    一个例子可能会使这一点更清楚:

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

    【讨论】:

    • 一个小查询。你写了: Supplier<String> supplier = t2::method;并提供评论为: // the supplier is bound to t1 。是打字错误还是我理解不正确?
    【解决方案3】:

    当您希望为某个类的特定实例执行该方法时,您可以使用绑定接收器。

    例如 :

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

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

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

    例如 :

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

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

      • 我猜可能是 Brian Goetz 但我不记得了抱歉
      • Devoxx FR - Effective Java,第三版 Keepin' it Effective (J. Bloch) - youtu.be/hSfylUXhpkA?t=833
      【解决方案5】:

      这是一个例子:

      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

      【讨论】:

        【解决方案6】:

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

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

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

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

        Type of 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]
        

        【讨论】:

        猜你喜欢
        • 2013-01-25
        • 2015-10-17
        • 1970-01-01
        • 1970-01-01
        • 2015-12-11
        • 1970-01-01
        • 2011-03-12
        • 2013-12-26
        • 1970-01-01
        相关资源
        最近更新 更多