Java Lambda表达式与函数式接口详解

什么是Lambda表达式

Lambda表达式是Java 8引入的一个重要特性,它提供了一种简洁的方式来表示可传递的匿名函数:一种没有名称的函数,但可以作为参数传递给其他方法。

Lambda表达式语法

基本语法:

1
(parameters) -> expression

或者:

1
(parameters) -> { statements; }

示例:

1
2
3
4
5
6
7
8
9
10
// 不带参数的Lambda表达式
Runnable r = () -> System.out.println("Hello World");

// 带一个参数的Lambda表达式(可省略参数类型)
Consumer<String> c = (String s) -> System.out.println(s);
Consumer<String> c = s -> System.out.println(s); // 简化形式

// 带多个参数的Lambda表达式
Comparator<Integer> comp = (Integer a, Integer b) -> a.compareTo(b);
Comparator<Integer> comp = (a, b) -> a.compareTo(b); // 简化形式

函数式接口

函数式接口是只包含一个抽象方法的接口。Lambda表达式可以用来创建这些接口的实例。

Java 8在java.util.function包中提供了许多预定义的函数式接口:

1. Predicate

接受一个输入参数,返回一个布尔值结果:

1
2
Predicate<String> isEmpty = s -> s.isEmpty();
System.out.println(isEmpty.test("")); // 输出: true

2. Function<T, R>

接受一个输入参数,返回一个结果:

1
2
Function<String, Integer> strLength = s -> s.length();
System.out.println(strLength.apply("Hello")); // 输出: 5

3. Consumer

接受一个输入参数,不返回结果:

1
2
Consumer<String> printer = s -> System.out.println(s);
printer.accept("Hello Lambda"); // 输出: Hello Lambda

4. Supplier

不接受参数,返回一个结果:

1
2
Supplier<Double> randomValue = () -> Math.random();
System.out.println(randomValue.get()); // 输出随机数

5. BinaryOperator

接受两个相同类型的参数,返回一个相同类型的结果:

1
2
BinaryOperator<Integer> add = (a, b) -> a + b;
System.out.println(add.apply(5, 3)); // 输出: 8

方法引用

方法引用是Lambda表达式的另一种形式,它可以让你重用已有的方法定义,并像Lambda表达式一样传递它们:

1
2
3
4
5
6
7
8
9
10
11
12
// 静态方法引用
Consumer<String> printer = System.out::println;

// 特定对象实例方法引用
String str = "Hello";
Supplier<Integer> strLength = str::length;

// 特定类型任意对象的实例方法引用
Function<String, Integer> strLengthFunc = String::length;

// 构造方法引用
Supplier<ArrayList<String>> listCreator = ArrayList::new;

Stream API与Lambda结合使用

Lambda表达式与Stream API结合使用,可以实现强大的集合处理功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Dave");

// 过滤长度大于4的名字
List<String> longNames = names.stream()
.filter(name -> name.length() > 4)
.collect(Collectors.toList());

// 将所有名字转为大写
List<String> upperCaseNames = names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());

// 连接所有名字
String joinedNames = names.stream()
.collect(Collectors.joining(", "));

Lambda表达式的变量捕获

Lambda表达式可以捕获其外部作用域中的变量,但这些变量必须是effectively final(实际上的最终变量):

1
2
3
int factor = 2;
Function<Integer, Integer> multiplier = n -> n * factor;
// factor = 3; // 这行代码会导致编译错误,因为Lambda中使用的变量必须是effectively final

总结

Lambda表达式和函数式接口是Java 8引入的重要特性,它们使得Java代码更加简洁、可读,并支持函数式编程范式。掌握这些特性可以帮助开发者编写更加现代化和高效的Java代码。通过方法引用和变量捕获的特性,Lambda表达式可以与现有代码无缝集成,提高代码的表达能力。