与 lambda 的方法参考差异

Method reference difference to lambda

我了解到方法引用中有4种类型。但是我不明白“Reference to a static method”和“Reference to特定类型的任意对象的实例方法".

例如:

  List<String> weeks = new ArrayList<>();
  weeks.add("Monday");
  weeks.add("Tuesday");
  weeks.add("Wednesday");
  weeks.add("Thursday");
  weeks.add("Friday");
  weeks.add("Saturday");
  weeks.add("Sunday");
  weeks.stream().map(String::toUpperCase).forEach(System.out::println);

toUpperCase方法不是static方法为什么他们可以用这种方式写而不是用这种方式

 weeks.stream().map(s -> s.toUpperCase()).forEach(System.out::println);

我强烈建议您阅读 Oracle 关于方法引用的文章:https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

也就是lambda表达式的形式:

s->s.toUpperCase()

这是一个方法参考:

String::toUpperCase

在语义上,方法引用与 lambda 表达式相同,只是语法不同。

方法参考了Java中相当智能的功能。因此,当您使用 non-static 方法引用时,例如 String:toUpperCase Java 会自动知道它需要在第一个 parameter.Suppose 上调用 toUpperCase 有两个参数一个 lambda 表达式,然后该方法将 first parameter 上的 callsecond parameter 将作为方法的 argument 传递。举个例子吧。

List<String> empNames = Arrays.asList("Tom","Bob");
String s1 = empNames.stream().reduce("",String::concat); //line -1
String s2 = empNames.stream().reduce("",(a,b)->a.concat(b)); // line -2
System.out.println(s1);
System.out.println(s2);

因此,在上面的第 -1 行示例中,String#concat 方法将 call 第一个参数(即 a 第 2 行)和第二个参数(即 b 对于行 -2) 将作为 argument.

多参数(超过2个)方法也是可以的,但是你需要非常注意参数的顺序。

说明

The method toUpperCase is not a static method why they can write in this way rather than using this way

weeks.stream().map(s->s.toUpperCase()).forEach(System.out::println);

方法引用不限于 static 方法。看看

.map(String::toUpperCase)

相当于

.map(s -> s.toUpperCase())

Java 将只调用您在流中的元素上引用的方法。其实这就是整个参考点。

官方Oracle tutorial对此进行了更详细的解释。


见解、示例

方法 Stream#map (documentation) 具有以下签名:

<R> Stream<R> map​(Function<? super T, ? extends R> mapper)

所以它需要一些 Function。在您的情况下,这是一个 Function<String, String>,它采用 String,对其应用某种方法,然后 returns 一个 String.

现在我们来看看Function(documentation)。它有以下方法:

R apply​(T t)

Applies this function to the given argument.

这正是您提供的方法参考。您提供一个 Function<String, String> 将给定的方法引用应用于所有对象。您的 apply 看起来像:

String apply(String t) {
    return t.toUpperCase();
}

以及 Lambda 表达式

.map(s -> s.toUpperCase())

使用相同的 apply 方法生成 完全相同的 Function

所以你可以做的是

Function<String, String> toUpper1 = String::toUpperCase;
Function<String, String> toUpper2 = s -> s.toUpperCase();

System.out.println(toUpper1.apply("test"));
System.out.println(toUpper2.apply("test"));

并且它们都会输出"TEST",它们的行为相同。

更多详细信息可以在 Java 语言规范 JLS§15.13 中找到。尤其要看看本章末尾的例子。

另外注意,为什么Java连String::toUpperCase都知道应该解释为Function<String, String>?好吧,一般情况下不会。这就是为什么我们总是需要明确指定类型:

// The left side of the statement makes it clear to the compiler
Function<String, String> toUpper1 = String::toUpperCase;

// The signature of the 'map' method makes it clear to the compiler
.map(String::toUpperCase)

另请注意,我们只能使用 功能接口:

来做这些事情
@FunctionalInterface
public interface Function<T, R> { ... }

关于 System.out::println

的注释

出于某种原因,您不会被

搞糊涂
.forEach(System.out::println);

这个方法也不是static

out 是普通对象实例,printlnPrintStream 的非 static 方法(documentation) class. See System#out 用于对象文档。