首页 > 编程笔记 > Java笔记 阅读:6

Java Lambda表达式的用法(新手必看)

Lambda 表达式是一个可传递的代码块,可以在以后执行一次或多次。Java 程序中指定 Lambda 代码块是很有帮助的,例如:
然而,Java 是一种面向对象的语言,其中(几乎)所有内容都是对象。Java 中没有函数类型。为此,函数被表示为对象,是实现了特定接口的类的实例。Lambda 表达式可以提供一种更加方便的语法,来创建这种实例。

考虑一个排序示例。我们传入代码,来检查一个字符串是否比另一个字符串短。例如,我们的一个运算:
first.length() - second.length()
其中的 first 和 second 是什么?它们都是字符串。Java 是一种强类型语言,我们还必须指定它们的类型:
(String first, String second) -> first.length() - second.length()
这就是你看到的第 1 个 Lambda 表达式(lambda expression)。这样的表达式就是一个代码块,再加上传入代码的变量规范。

为什么叫 Lambda 这个名字呢?很多年前,当时计算机还没有诞生,逻辑学家阿隆佐·丘奇(Alonzo Church)就想形式化地表示能有效计算的数学函数(奇怪的是,有些函数已知存在,但没有人知道如何计算它们的值)。他使用希腊字母 Lambda(λ)来标记参数:
λfirst.λsecond. first.length() – second.length()
为什么使用字母 λ 呢?是因为丘奇用完了字母表的字母么?事实上,权威的《数学原理》 (Principia Mathematica)一书中就使用重音符 ^ 表示函数参数,受此启发,丘奇使用大写的 Lambda(Λ)。但是最后,他又改为使用小写版本。从那以后,带有参数变量的表达式就被称为 Lambda 表达式。

如果 Lambda 表达式的主体执行的运算不适合用单一的表达式实现,那么也可以像编写方法那样,使用一对花括号和显式的 return 语句来实现 Lambda,例如:
(String first, String second) -> {
    int difference = first.length() < second.length();
    if (difference < 0) return -1;
    else if (difference > 0) return 1;
    else return 0;
}

即使 Lambda 表达式没有参数,仍然要提供空括号,就像无参数方法那样:
Runnable task = () -> { for (int i = 0; i < 1000; i++) doWork(); }

如果可以推导出 Lambda 表达式的参数类型,则可以省略其类型。例如:
Comparator<String> comp
= (first, second) -> first.length() - second.length();
// Same as (String first, String second)
这里,编译器可以推导出 first 和 second 必然是字符串,因为 Lambda 表达式将赋值给字符串比较器。

如果方法只有一个参数,而且这个参数类型可以推导得出,甚至可以省略圆括号:
ActionListener listener = event ->
System.out.println("Thanks for clicking!");
// Instead of (event) -> or (ActionEvent event) ->

无须指定 Lambda 表达式的返回类型。这是因为编译器能够从它的主体中推导出返回类型,并检查它是否与预期类型匹配。例如,表达式:
(String first, String second) -> first.length() - second.length()
可以在需要 int 类型(或兼容类型,例如 Integer、long 或 double)结果的代码中使用。

Java函数式接口

正如你已经看到的,Java 中有许多表示动作的接口,例如 Runnable 或 Comparator 接口。Lambda 表达式能够与这种接口兼容。

对于只有单个抽象方法(single abstract method)的接口,需要这种接口的对象时,就可以提供 Lambda 表达式。这样的接口称为函数式接口(functional interface)。

为了演示如何转换为函数式接口,考虑 Arrays.sort 方法。它的第二个参数需要 Comparator 的实例,Comparator 就是一个单函数接口,所以可以提供一个 Lambda 表达式:
Arrays.sort(names, (first, second) -> first.length() - second.length());
这样,Arrays.sort 方法的第二个参数变量将接收实现了 Comparator<String> 的某个类的对象。在该对象上调用 compare 方法将执行 Lambda 表达式的主体。这些对象和类的管理完全依赖于具体的实现并且被高度优化。

在大多数支持函数字面量的编程语言中,都可以声明例如 (String, String) -> int 的函数类型,再声明这些类型的变量,然后将函数放入这些变量中,并且调用它们。

在 Java 中,Lambda 表达式只能做一件事,将其放在类型为函数式接口的变量中,这样就可以将其转换成为该接口的一个实例。

注意,不能将 Lambda 表达式赋值给类型为 Object 的变量,Object 类是 Java 中所有类的通用超类。Object 是一个类,不是一个函数式接口。

Java API 提供了大量的函数式接口。以下是其中一个示例:
public interface Predicate<T> {
boolean test(T t);
// Additional default and static methods
}

ArrayList 类有一个参数为 Predicate 的 removeIf 方法。它是专门为接收 Lambda 表达式而设计的。例如,以下语句从数组列表中删除所有 null 值:
list.removeIf(e -> e == null);

相关文章