与 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
上的 call
和 second 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
是普通对象实例,println
是 PrintStream
的非 static
方法(documentation) class. See System#out 用于对象文档。
我了解到方法引用中有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
上的 call
和 second 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
是普通对象实例,println
是 PrintStream
的非 static
方法(documentation) class. See System#out 用于对象文档。