函数式编程(Functional Programming)是把函数作为基本运算单元,函数可以作为变量,可以接收函数,还可以返回函数。历史上研究函数式编程的理论是Lambda演算,所以我们经常把支持函数式编程的编码风格称为Lambda表达式。
在Java程序中,我们经常遇到一大堆单方法接口,即一个接口只定义了一个方法:
- Comparator
- Runnable
- Callable
以 Comparator
为例,我们想要调用 Arrays.sort()
时,可以传入一个 Comparator
实例,以匿名类方式编写如下:
String[] array = ...
Arrays.sort(array, new Comparator() {
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
上述写法非常繁琐。从Java 8开始,我们可以用Lambda表达式替换单方法接口。改写上述代码如下:
public class Main {
public static void main(String[] args) {
String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
Arrays.sort(array, (s1, s2) -> {
return s1.compareTo(s2);
});
// 输出:Apple, Banana, Lemon, Orange
System.out.println(String.join(", ", array));
}
}
观察Lambda表达式的写法,它只需要写出方法定义:
(s1, s2) -> {
return s1.compareTo(s2);
}
其中,参数是 (s1, s2)
,参数类型可以省略,因为编译器可以自动推断出 String
类型。 -> { ... }
表示方法体,所有代码写在内部即可。返回值的类型也是由编译器自动推断的,这里推断出的返回值是 int
,因此,只要返回 int
,编译器就不会报错。
并且不用书写 class
定义,这样的写法是不是非常简洁。
如果只有一行 return xxx
的代码,完全可以用更简单的写法:
Arrays.sort(array, (s1, s2) -> s1.compareTo(s2));
FunctionalInterface
我们把只定义了单方法的接口称之为 FunctionalInterface
,用注解 @FunctionalInterface
标记。这里以 Callable
接口为例:
@FunctionalInterface
public interface Callable {
V call() throws Exception;
}
再来看 Comparator
接口:
@FunctionalInterface
public interface Comparator {
int compare(T o1, T o2);
boolean equals(Object obj);
default Comparator reversed() {
return Collections.reverseOrder(this);
}
default Comparator thenComparing(Comparator other) {
...
}
...
}
这时候看到 Comparator
接口,一些人认为它不应该是定义单方法的接口。
如果你细看,虽然 Comparator
接口有很多方法,但只有一个抽象方法 int compare(T o1, T o2)
,其他的方法都是 default
方法或 static
方法。还有 boolean equals(Object obj);
是 Object
定义的方法,不算在接口方法内。因此, Comparator
也是一个 FunctionalInterface
。
看到这有兴趣的还能再补充下
方法引用
除了Lambda表达式,我们还可以直接传入方法引用。例如:
public class Main {
public static void main(String[] args) {
String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
Arrays.sort(array, Main::cmp);
System.out.println(String.join(", ", array));
}
static int cmp(String s1, String s2) {
return s1.compareTo(s2);
}
}
上述代码在 Arrays.sort()
中直接传入了静态方法 cmp
的引用,用 Main::cmp
表示。
因此,所谓方法引用,是指如果某个 方法签名和接口恰好一致,就可以直接传入方法引用。
那什么是 方法签名和接口一致呢?
方法参数一致,返回类型相同,我们说两者的 方法签名一致。(不看方法名称,也不看类的继承关系)
Comparator<string></string>
接口定义的方法是 int compare(String, String)
,和静态方法 int cmp(String, String)
就属于方法签名一致,我们可以直接把方法名作为Lambda表达式传入:
Arrays.sort(array, Main::cmp);
我们再看看如何引用实例方法:
public class Main {
public static void main(String[] args) {
String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
Arrays.sort(array, String::compareTo);
System.out.println(String.join(", ", array));
}
}
不但可以编译通过,而且运行结果也是一样的,这说明 String.compareTo()
方法也符合Lambda定义。
观察 String.compareTo()
的方法定义:
public final class String {
public int compareTo(String o) {
...
}
}
有人会疑惑了:这个方法的签名只有一个参数,为什么和 int Comparator<string>.compare(String, String)</string>
能匹配呢?
因为实例方法有一个隐含的 this
参数, String
类的 compareTo()
方法在实际调用的时候,第一个隐含参数总是传入 this
,相当于静态方法:
public static int compareTo(this, String o);
所以, String.compareTo()
方法也可作为方法引用传入。
Original: https://www.cnblogs.com/xnmk-zhan/p/15549793.html
Author: xnmk
Title: Java的Lambda表达式
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/590683/
转载文章受原作者版权保护。转载请注明原作者出处!