java 方法参考Bound Receiver和Unbound Receiver之间的差异

ppcbkaq5  于 2023-05-05  发布在  Java
关注(0)|答案(7)|浏览(225)

我正在尝试在代码中使用Java 8方法引用。有四种类型的方法引用可用。
1.静态方法引用。
1.示例方法(绑定接收方)。
1.示例方法(未绑定的接收器)。
1.构造函数参考。
使用 *static方法引用 * 和 *constructor引用 * 我没有问题,但是 instance方法(绑定接收器)instance方法(未绑定接收器) 真的让我很困惑。在 bound receiver中,我们使用一个对象引用变量来调用一个方法,如:

objectRef::Instance Method

unbound receiver中,我们使用类名来调用一个方法,如:

ClassName::Instance Method.

我有以下问题:
1.示例方法需要不同类型的方法引用吗?
1.boundunbound receiver方法引用之间有什么区别?
1.我们应该在哪里使用 bound receiver,在哪里使用 unbound receiver?
我还从Java 8语言特性书籍中找到了对 boundunbound receiver的解释,但仍然与实际概念混淆。

sbdsn5lh

sbdsn5lh1#

String::length这样的未绑定接收器的想法是,你引用的是一个对象的方法,将作为lambda的参数之一提供。例如,lambda表达式(String s) -> s.toUpperCase()可以重写为String::toUpperCase
但是bounded指的是一种情况,当你在lambda中调用一个方法到一个已经存在的外部对象。例如,lambda表达式() -> expensiveTransaction.getValue()可以重写为expensiveTransaction::getValue
三种不同方法参考的情况

  • (args) -> ClassName.staticMethod(args)可以是ClassName::staticMethod//这是静态的(你也可以认为是unBound)
  • (arg0, rest) -> arg0.instanceMethod(rest)可以是ClassName::instanceMethodarg0的类型为ClassName)//未绑定
  • (args) -> expr.instanceMethod(args)可以是expr::instanceMethod//绑定

答案取自 Java 8 in Action 一书

jjhzyzn0

jjhzyzn02#

基本上,未绑定的接收器允许你使用示例方法,就好像它们是第一个参数是声明类型的静态方法一样-所以你可以通过传入任何你想要的示例来将它们用作函数。使用绑定接收器,“目标”示例实际上是函数的一部分。
举个例子可以让这一点更清楚:

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

qnakjoqk3#

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

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

将在PrintStream的特定示例上执行println-System.out示例。因此,System.out.println("x")System.out.println("y")将作为将该方法引用传递给forEach的结果而被执行。
另一方面,如果您希望为类的未指定示例执行方法,则可以使用未绑定的接收器。
例如:

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

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

voase2hg

voase2hg4#

我从最近的一次演讲中捕捉到了这个

jtjikinw

jtjikinw5#

下面是一个例子:

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

baubqpgj

baubqpgj6#

沿着上面的精彩回答。感谢约书亚bloch的精彩解释,effective java第三版。我终于能够理解有界引用和无界引用的含义了。
在有界引用中,接收对象在方法引用中指定。绑定引用在本质上与静态引用相似:函数对象采用与被引用方法相同的参数。
在未绑定引用中,接收对象是在应用函数对象时通过方法声明的参数之前的附加参数指定的。未绑定引用通常用作流管道中的Map和过滤函数
最后,有两种构造函数引用,分别用于类和数组。构造函数引用用作工厂对象。

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

hpcdzsge7#

一个很好的文章我发现here

绑定的非静态方法引用

绑定的非静态方法引用是指绑定到接收器对象的非静态方法。它的语法是objectName::instanceMethodName,其中objectName标识接收方,instanceMethodName标识示例方法。一个例子是s::trim。清单2演示了一个绑定的非静态方法引用。
清单2.绑定的非静态方法引用

import java.util.function.Supplier;

public class MRDemo
{
   public static void main(String[] args)
   {
      String s = "The quick brown fox jumped over the lazy dog";
      print(s::length);
      print(() -> s.length());
      print(new Supplier<Integer>()
      {
         @Override
         public Integer get()
         {
            return s.length(); // closes over s
         }
      });
   }

   public static void print(Supplier<Integer> supplier)
   {
      System.out.println(supplier.get());
   }
}

清单2的main()方法将一个字符串赋给String变量s,然后调用print()类方法,该方法具有获取字符串长度作为该方法参数的功能。print()在方法引用(s::length--length()绑定到s)、等效lambda和等效匿名类上下文中调用。
我定义了print()来使用java.util.function.Supplier预定义的函数接口,它的get()方法返回结果的供应商。在本例中,传递给print()的Supplier示例实现其get()方法以返回s. length();print()输出这个长度。
s::length引入了一个封闭s的闭包。你可以在lambda示例中更清楚地看到这一点。因为lambda没有参数,所以s的值只能从封闭作用域中获得。因此,lambda函数体是一个封闭s的闭包。匿名类的例子使这一点更加清楚。
编译清单2并运行应用程序。您将看到以下输出:

44
44
44

未绑定非静态方法的引用

未绑定的非静态方法引用是指未绑定到接收方对象的非静态方法。它的语法是className::instanceMethodName,其中className标识声明示例方法的类,instanceMethodName标识示例方法。例如String::toLowerCase。
String::toLowerCase是一个未绑定的非静态方法引用,它标识String类的非静态String toLowerCase()方法。然而,由于非静态方法仍然需要接收方对象(在本例中为String对象,用于通过方法引用调用toLowerCase()),因此接收方对象由虚拟机创建。将在此对象上调用toLowerCase()。String::toLowerCase指定一个方法,该方法接受单个String参数(接收器对象),并返回String结果。String::toLowerCase()等价于lambda(String s)-〉{return s. toLowerCase();}.
清单3演示了这个未绑定的非静态方法引用。
清单3. MRDemo版本3:未绑定的非静态方法引用

import java.util.function.Function;

public class MRDemo
{
   public static void main(String[] args)
   {
      print(String::toLowerCase, "STRING TO LOWERCASE");
      print(s -> s.toLowerCase(), "STRING TO LOWERCASE");
      print(new Function<String, String>()
      {
         @Override
         public String apply(String s) // receives argument in parameter s;
         {                             // doesn't need to close over s
            return s.toLowerCase();
         }
      }, "STRING TO LOWERCASE");
   }

   public static void print(Function<String, String> function, String
s)
   {
      System.out.println(function.apply(s));
   }
}

清单3的main()方法调用print()类方法,该方法具有将字符串转换为小写的功能,并将要转换的字符串作为方法的参数。print()在方法引用中调用(String::toLowerCase--toLowerCase()没有绑定到用户指定的对象),等效的lambda,以及等效的匿名类上下文。
我定义了print()来使用java.util.function.Function预定义的函数接口,它表示接受一个参数并产生一个结果的函数。在本例中,传递给print()的Function示例实现其R apply(T t)方法以返回s. toLowerCase();print()输出这个字符串。
虽然String::toLowerCase的String部分使其看起来像是引用了一个类,但实际上只引用了该类的一个示例。匿名类的例子使这个事实更加明显。注意,匿名类示例显示lambda接收一个参数;它不会关闭参数s,因此不是闭包。
编译清单3并运行应用程序。您将看到以下输出:

string to lowercase
string to lowercase
string to lowercase

相关问题