Java Lambda 表达式
Lambda 表达式是 Java 8 引入的重要特性,它允许我们将函数作为参数传递,使代码更加简洁和灵活。
什么是 Lambda 表达式?
Lambda 表达式是一种匿名函数,它可以没有名称,可以作为参数传递或作为返回值。
语法
// 基本语法
(parameters) -> expression
// 或
(parameters) -> { statements; }
示例
// 无参数
() -> System.out.println("Hello")
// 单参数
x -> x * 2
// 多参数
(x, y) -> x + y
// 多行语句
(x, y) -> {
int sum = x + y;
return sum;
}
函数式接口
Lambda 表达式必须绑定到一个函数式接口(Functional Interface)。
什么是函数式接口?
只包含一个抽象方法的接口称为函数式接口,可以用 @FunctionalInterface 注解标记。
@FunctionalInterface
interface MyInterface {
void doSomething();
// 可以包含默认方法和静态方法
default void defaultMethod() {
System.out.println("默认方法");
}
}
常见的函数式接口
| 接口 | 抽象方法 | 说明 |
|---|---|---|
Runnable | void run() | 无参数无返回值 |
Supplier<T> | T get() | 无参数有返回值 |
Consumer<T> | void accept(T t) | 有参数无返回值 |
Function<T,R> | R apply(T t) | 有参数有返回值 |
Predicate<T> | boolean test(T t) | 返回布尔值 |
BiConsumer<T,U> | void accept(T t, U u) | 两个参数无返回值 |
Lambda 表达式示例
与集合结合
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "Diana");
// forEach - 遍历
names.forEach(name -> System.out.println(name));
names.forEach(System.out::println);
// filter - 过滤
List<String> filtered = names.stream()
.filter(name -> name.startsWith("A"))
.collect(Collectors.toList());
// map - 转换
List<Integer> lengths = names.stream()
.map(name -> name.length())
.collect(Collectors.toList());
// sort - 排序
names.sort((a, b) -> a.compareTo(b));
names.sort(String::compareTo);
使用 Comparator
List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
// 按长度排序
names.sort((a, b) -> a.length() - b.length());
// 按字母逆序排序
names.sort((a, b) -> b.compareTo(a));
// 使用 Comparator 静态方法
names.sort(Comparator.comparingInt(String::length));
names.sort(Comparator.reverseOrder());
使用 Predicate
import java.util.function.Predicate;
Predicate<String> isLong = s -> s.length() > 5;
Predicate<String> startsWithA = s -> s.startsWith("A");
// 使用 and
Predicate<String> combined = isLong.and(startsWithA);
// 使用 or
Predicate<String> either = isLong.or(startsWithA);
// 使用 negate
Predicate<String> notLong = isLong.negate();
// 应用
System.out.println(isLong.test("HelloWorld")); // true
System.out.println(either.test("Bob")); // false
使用 Function
import java.util.function.Function;
// 字符串转整数
Function<String, Integer> strToInt = s -> Integer.parseInt(s);
Integer result = strToInt.apply("123");
// 组合函数
Function<Integer, Integer> doubleValue = n -> n * 2;
Function<Integer, Integer> addTen = n -> n + 10;
// andThen: 先执行当前函数,再执行另一个函数
Function<Integer, Integer> combined = doubleValue.andThen(addTen);
System.out.println(combined.apply(5)); // 20 (5*2+10)
// compose: 先执行另一个函数,再执行当前函数
Function<Integer, Integer> composed = doubleValue.compose(addTen);
System.out.println(composed.apply(5)); // 30 (5+10)*2
使用 Supplier
import java.util.function.Supplier;
// 获取当前时间
Supplier<Long> timeSupplier = () -> System.currentTimeMillis();
// 创建对象
Supplier<Person> personSupplier = () -> new Person("张三", 20);
// 使用
System.out.println(timeSupplier.get());
Person p = personSupplier.get();
使用 Consumer
import java.util.function.Consumer;
// 打印
Consumer<String> printer = s -> System.out.println("Value: " + s);
printer.accept("Hello");
// 使用 andThen
Consumer<String> consumer1 = s -> System.out.println("Step1: " + s);
Consumer<String> consumer2 = s -> System.out.println("Step2: " + s);
Consumer<String> combined = consumer1.andThen(consumer2);
combined.accept("Test"); // 先输出 Step1: Test,再输出 Step2: Test
方法引用
方法引用是 Lambda 表达式的简化写法。
类型
// 静态方法引用
Function<String, Integer> parser = Integer::parseInt;
// 实例方法引用
String str = "Hello";
Supplier<Integer> lenSupplier = str::length;
// 构造函数引用
Supplier<ArrayList<String>> listSupplier = ArrayList::new;
Function<Integer, ArrayList<String>> listWithSize = ArrayList::new;
示例
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// 方法引用 - System.out::println
names.forEach(System.out::println);
// 方法引用 - String::toUpperCase
names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// 方法引用 - compareToIgnoreCase
names.sort(String::compareToIgnoreCase);
// 构造函数引用
List<String[]> arrays = names.stream()
.map(name -> new String[]{name})
.collect(Collectors.toList());
// 等价于
List<String[]> arrays2 = names.stream()
.map(String[]::new)
.collect(Collectors.toList());
变量作用域
Lambda 表达式可以访问外部变量,但有一些限制。
访问局部变量
int count = 0;
// Lambda 可以读取局部变量
Runnable r = () -> System.out.println("Count: " + count);
// 但不能修改局部变量(会产生编译错误)
// count++; // 错误
// 使用数组来"绕过"这个限制
int[] counter = {0};
Runnable r2 = () -> counter[0]++;
访问成员变量和静态变量
class MyClass {
int instanceVar = 10;
static int staticVar = 20;
void test() {
// 可以访问和修改成员变量
Runnable r1 = () -> {
instanceVar++;
staticVar++;
};
}
}
this 关键字
在 Lambda 表达式中,this 指向创建 Lambda 的外部类的实例。
class MyClass {
int value = 100;
void test() {
Runnable r = () -> {
// 这里 this 指向 MyClass 的实例
System.out.println(this.value); // 100
};
}
}
典型使用场景
1. 线程创建
// 传统方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello");
}
}).start();
// Lambda 方式
new Thread(() -> System.out.println("Hello")).start();
// 更简洁
new Thread(() -> {
// 执行任务
}).start();
2. 事件处理
button.addActionListener(e -> System.out.println("按钮被点击"));
// 处理多个事件
KeyListener listener = e -> {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
// 处理回车键
}
};
3. 回调
interface Callback {
void onComplete(String result);
}
void asyncOperation(Callback callback) {
// 异步操作完成后调用回调
callback.onComplete("完成");
}
// 使用
asyncOperation(result -> System.out.println("结果: " + result));
4. 过滤和转换
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 过滤偶数
List<Integer> evens = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// 平方
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
// 字符串长度大于3的
List<String> longNames = names.stream()
.filter(name -> name.length() > 3)
.collect(Collectors.toList());
与匿名类的区别
// 匿名类
Runnable anonymous = new Runnable() {
@Override
public void run() {
System.out.println("匿名类");
}
};
// Lambda 表达式
Runnable lambda = () -> System.out.println("Lambda");
主要区别
| 特性 | 匿名类 | Lambda |
|---|---|---|
| 语法 | 冗长 | 简洁 |
| this 关键字 | 指向匿名类本身 | 指向外部类 |
| 编译方式 | 编译为独立的类 | 编译为invokedynamic |
| 序列化 | 可以实现序列化(需实现接口) | 不推荐序列化 |
最佳实践
1. 保持简洁
// 不好的写法
list.stream()
.filter(s -> s.startsWith("A"))
.forEach(s -> System.out.println(s));
// 好的写法
list.stream()
.filter(s -> s.startsWith("A"))
.forEach(System.out::println);
2. 使用类型推断
// 不必要地指定类型
list.stream()
.map((String s) -> s.toUpperCase())
.collect(Collectors.toList());
// 让编译器推断类型
list.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
3. 避免在 Lambda 中修改外部变量
// 不好的写法
int sum = 0;
list.forEach(s -> sum += s.length());
// 好的写法
int sum = list.stream()
.mapToInt(String::length)
.sum();
小结
本章我们学习了:
- Lambda 表达式基础:语法和基本用法
- 函数式接口:常见的函数式接口及其使用
- 方法引用:静态方法、实例方法、构造函数引用
- 变量作用域:Lambda 中访问变量
- 典型场景:线程、事件处理、回调、过滤转换
Lambda 表达式让 Java 具备了函数式编程能力,使代码更加简洁和 expressive。
练习
- 使用 Lambda 表达式对列表进行排序
- 使用 Predicate 过滤满足条件的元素
- 使用 Function 进行数据转换
- 使用 Stream 和 Lambda 实现数据处理
- 将匿名类转换为 Lambda 表达式