Lambda 表达式既匿名函数,指的是在需要函数的地方直接定义函数,而不必定义一个函数并给它赋予一个名称
lambda 是希腊字母 λ 的读音,取名借鉴于数学领域的 λ 演算
JavaScript labmda 表达式
JavaScript 中函数可以作为和普通变量一样被传递,同时支持函数表达式定义函数,ES6 支持了箭头函数定义函数,lambda 表达式使用起来非常的自然
[3, 6, 1, 9, 4].sort((x,y) => x - y);
[3, 6, 1, 9, 4].forEach(console.log);
Java lambda 表达式
Java 8 之前没有 lambda 表达式的支持,实现具有单一抽象方法的接口(如 Runnable、Comparator、ActionListener 等)通常需要使用匿名内部类,这不但代码冗长,而且可读性较差
// 使用匿名内部类实现 Runnable 接口
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello from a thread!");
}
}).start();
Java 8 带来了 lambda 表达式支持,用于实现只有一个抽象方法的接口。它们允许将功能作为方法参数传递,从而实现更灵活和可读的代码
// 使用 Lambda 表达式实现 Runnable 接口
new Thread(() -> System.out.println("Hello from a thread!")).start();
基本语法
Java lambda 表达式由参数列表、箭头符号、函数主体组成
(param1, param2) -> expression
(param1, param2) -> {
// 多行代码
}
- 参数列表:与函数接口的抽象方法参数列表相对应
- 箭头符号:
->用于分隔参数列表和函数体 - 函数体:如果只有一个表达式,可以省略花括号和
return关键字;如果有多条语句,需要使用花括号并显式使用return关键字
字符串数组按照字符串长度排序
String[] words = { "horse", "elephent", "dog", "dolphin" };
// 参数类型可推导时候,可以缺省其类型
Arrays.sort(words, (x, y) -> x.length() - y.length());
Java 编译器可以根据上下文自动推断 Lambda 表达式的参数类型,因此在大多数情况下,无需显式声明参数类型。
函数式接口
函数式编程的核心是把函数传递给对象,面向对象设计的 Java 不能直接传递函数,为了保证简单性和一致性,不新增函数类型,在传递函数时必须构建一个对象,这个对象的类包含此函数,Java 给出的解决方案就是函数式接口
函数式接口(Functional Interface)是指只含有一个抽象方法的接口,可以使用 Lambda 表达式创建一个函数式接口的对象,Comparator接口只有一个抽象方法 compare,可以使用 lambda 表达式
Arrays.sort(words,
(x, y) -> x.length() - y.length());
Arrays.sort 方法会接收实现了 Comparator 的类的对象,在这个对象上调用 compare 方法时会执行这个 lambda 表达式
继承来的方法不算作抽象方法,因为它们已经被实现了。所以即使 Comparator 也有 equals 方法(来自 Object 类),但它仍然被认为是函数式接口,因为它只有一个抽象方法 compare。
java.util.function 中有一个很常用的接口 Predicate
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
// 其余静态或默认方法
}
ArrayList 类的removeIf方法的参数就是 Predicate 类型,这个接口专门用来传递 lambda 表达式
list.removeIf(e -> e == null);
可以通过 @FunctionalInterface 注解自定义函数式接口。该注解不是必须的,但它可以在编译时检查接口是否符合函数式接口的规范
@FunctionalInterface
public interface GreetingService {
void sayMessage(String message);
// 以下方法会导致编译错误,因为函数式接口只能有一个抽象方法
// void anotherMethod();
}
方法引用
如果已经有现成方法实现了想传递给对象的函数,可以使用方法引用。方法引用(Method References)是 Lambda 表达式的一种简化形式,用于直接引用已有的方法、构造方法或特定对象的方法。它使得代码更加简洁和具备可读性
类::静态方法
Timer t = new Timer(1000, System.out::println);
Arrays.sort(strArray, String::compareToIgnoreCase)
对象::实例方法
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
}
Consumer 接口表示一个接受单个输入参数并且没有返回值的操作。它定义了一个名为accept的方法,该方法接受一个参数并对其进行操作。Consumer主要用于在集合遍历、流式操作等场景中,对集合中的元素进行操作
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public void printName() {
System.out.println(name);
}
}
List<Person> people = Arrays.asList(
new Person("John"),
new Person("Jane"),
new Person("Mike"));
Consumer<Person> printNameConsumer = Person::printName;
people.stream().forEach(printNameConsumer);
构造器::new
@FunctionalInterface
public interface Supplier<T> {
T get();
}
Supplier 接口表示一个无参函数,它定义了一个名为 get 的方法,该方法返回一个值。Supplier 主要用于在需要提供数据的场景中
class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// 创建一个Supplier接口
Supplier<Person> personSupplier = Person::new;
// 使用Supplier接口的get方法来创建一个新的Person对象
Person person = personSupplier.get();
person.setName("John");
System.out.println(person.getName());
Stream API
Stream API 让开发者可以通过 lambda 表达式简明扼要的以流水线方式处理集合中的元素,可以对集合中的元素进行过滤、排序、映射、去重等操作,使得数据处理更加灵活。并且采用惰性求值的方式,只有在需要结果的时候才会进行计算,减少了不必要的计算量。Stream API 进行集合数据处理的核心方法有:
- filter():用于对集合中的元素进行过滤
- map():用于对集合中的元素进行映射
- distinct():用于去除集合中的重复元素
- sorted():用于对集合中的元素进行排序
- limit():用于限制集合中元素的数量
- skip():用于跳过集合中的元素
- forEach():用于遍历集合中的元素
- collect():用于将集合中的元素收集到一个集合中
- reduce():用于对集合中的元素进行归约操作
List<String> list = Arrays.asList("apple", "banana", "orange", "pear", "peach");
List<String> filteredList = list.stream()
.filter(s -> s.startsWith("p"))
.map(s -> s + "-OK")
.sorted()
.collect(Collectors.toList());
System.out.println(filteredList);