Lambda 表达式
这是 Java 函数式编程的核心。Lambda 表达式是 Java 中的主要构造,它在语言中提供了一种新的语法,使我们能够以自然的方式表达函数。
1、Lambda 表达式是面向对象平台中函数式编程原则的实现。
2、Lambda 表达式用于直接在代码中表达函数,而无需面向对象的包装器来支持它们(从语言语法的角度来看)。在方法上,可以看作是匿名方法。
3、由于 lambda 遵循函数的语义,就像在函数式编程语言中一样, 因此它们获得了可以从不可变的、一致的函数中获得的所有并行性和并发性优势。
4、Lambda 表达式可以在任何需要函数式接口的代码中使用,这实际上意味着,在内部,lambda 表达式是函数式接口的实现,因此是语言中的一等公民。它们可以被分配/存储,作为参数传递等。
5、如果匿名类是只包含一个方法的接口的实现,则 Lambdas 可以替换匿名类,然后匿名类可以替换为一个 lambda,这会形成干净且不那么冗长的代码。
现在让我们将 Java 中的 lambda 表达式与 lambda 演算或函数式编程中的构造进行比较:
1、函数是匿名的——lambda表达式也是匿名的
2、函数不会改变状态——lambda 表达式遵循与匿名/本地类相同的闭包规则,即它们只能访问 final 或有效的 final 变量
3、函数可以用作输入或作为其他函数的输出返回
4、Lambda 演算的语法 λx.x2 与 lambda 表达式 x→x^2 的语法相匹配。λx 成为参数部分, dot 成为箭头, expr 主体在这里成为 lambda 表达式的主体。
一、Lambda 表达式语法
让我们来看看用于在代码中表达不同类型函数的语言语法。
1、指定函数参数,参数类型可选,如果只有一个参数可以省略小括号
2、用于分隔参数和正文的箭头运算符
3、如果只有一个语句,函数体、大括号和返回可以省略
语法示例:
1、无参数: () →System.out.println(“Nothing”)
2、一个参数: x →x+2
3、两个参数: (x,y) →x+y
4、带参数类型: (Integer x, Integer y) →x+y
5、多个语句:
(x,y) → {
System.out.println(x);
System.out.println(y);
return x+y;
}
二、Lambda表达式与传统代码的比较
让我们将使用 Lambda 表达式的代码与传统代码进行比较,以了解在代码中使用 lambda 表达式的区别和优势:
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("From a thread");
}
});
Thread t = new Thread(() -> System.out.println("From a thread"));
Collections.sort(Arrays.asList(3, 2, 1), new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
Collections.sort(Arrays.asList(3, 2, 1), (o1, o2) -> o1.compareTo(o2));
List<String> strings = Arrays.asList("R", "O", "B");
for (String string : strings) {
System.out.println(string);
}
List<String> strings = Arrays.asList("R", "O", "B");
strings.forEach(s -> System.out.println(s));
// 或者使用方法引用
strings.forEach(System.out::println);
有关 Lambda 表达式及其在 Java 中实现的更多详细信息,将在后续的文章中讨论。
方法引用
Lambda 表达式用于表达函数的主体,但如果已经编写/定义了函数/方法,则可以在使用 lambda 表达式的任何地方直接将它们用作方法引用。我们可以看到 lambda 表达式和方法具有相同的用法,只是第一个是匿名的,而后者是命名的。因此,如果我们已经定义了一个方法,则方法引用允许我们按名称使用该方法来代替匿名 lambda 表达式。
有四种方法引用:
1、对静态方法的引用
— ContainingClass::staticMethodName
2、对特定对象的实例方法的引用
— containsObject::instanceMethodName
3、对特定类型的任意对象的实例方法的引用
— ContainingType::methodName
4、对构造函数的引用
— ClassName::new
让我们看看四种方法引用的例子:
public class MethodRef {
public void printLowerCase(String s) {
System.out.println(s.toLowerCase());
}
public static void printUpperCase(String s) {
System.out.println(s.toUpperCase());
}
public void publicMethod() {
List<String> list = Arrays.asList("A", "B", "C");
list.forEach(this::printLowerCase);
list.forEach(MethodRef::printUpperCase);
list.forEach(String::toLowerCase);
list.forEach(String::new);
}
}
list.forEach(this::printLowerCase) 是对特定对象的实例方法的引用的示例。请注意,如果在此处使用 lambda 表达式而不是此方法引用,则有一个区别:使用 lambda 表达式时,只能访问 final 或有效的 final 字段,而使用此方法引用,可以访问任何字段。
list.forEach(MethodRef::printUpperCase) 是对静态方法的方法引用的示例。
list.forEach(String::toLowerCase) 是对特定类型的任意对象的实例方法的方法引用的示例。
list.forEach(String::new) 是对构造函数的方法引用的示例。