理解Lambda表达式
·
理解Lambda表达式
Lambda 表达式是 Java 8 最重要的新特性,它让代码变得更简洁、更优雅。
一句话理解 Lambda
Lambda 就是一个匿名函数,可以理解为"一段可以传递的代码"。
好比点外卖:
- 传统方式:打电话给餐厅,说一堆话(定义方法)
- Lambda 方式:在APP上点"宫保鸡丁"(直接给实现)
从匿名类到 Lambda 的进化
传统写法(Java 8 之前)
// 1. 创建一个 Runnable 接口的实现
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
};
Lambda 写法(Java 8+)
Runnable task = () -> System.out.println("Hello World");
对比:
- 代码量:5行 → 1行
- 可读性:啰嗦 → 简洁
- 重点:模板代码 → 业务逻辑
Lambda 表达式语法
(参数列表) -> { 方法体 }
最简单的三种形式:
// 1. 无参数,无返回值
() -> System.out.println("Hello");
// 2. 一个参数,无返回值(可省略括号)
x -> System.out.println(x);
(x) -> System.out.println(x);
// 3. 多个参数,有返回值
(int a, int b) -> { return a + b; };
// 可简化为:
(a, b) -> a + b; // 只有一条语句时,可省略 return 和 {}
核心:函数式接口
Lambda 必须配合函数式接口使用。
什么是函数式接口?
只有一个抽象方法的接口(可以有多个默认方法或静态方法)。
// 自定义函数式接口
@FunctionalInterface // 注解,可选,但推荐加上
interface MyFunction {
int operate(int a, int b);
// 只能有一个抽象方法
// 可以有默认方法
default void print() {
System.out.println("默认方法");
}
// 可以有静态方法
static void hello() {
System.out.println("静态方法");
}
}
Java 内置的常用函数式接口
| 接口 | 参数 | 返回值 | 用途 | 示例 |
|---|---|---|---|---|
Consumer<T> | T | void | 消费型接口 | (x) -> System.out.println(x) |
Supplier<T> | 无 | T | 供给型接口 | () -> new Random().nextInt() |
Function<T,R> | T | R | 函数型接口 | (x) -> x * 2 |
Predicate<T> | T | boolean | 断言型接口 | (x) -> x > 0 |
Runnable | 无 | void | 无参无返回值 | () -> System.out.println("Hi") |
实际应用场景
场景1:替代匿名内部类
// 传统方式
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按钮被点击");
}
});
// Lambda 方式
button.addActionListener(e -> System.out.println("按钮被点击"));
场景2:遍历集合
List<String> list = Arrays.asList("Java", "Python", "C++");
// 传统 for 循环
for (String s : list) {
System.out.println(s);
}
// 使用 forEach + Lambda
list.forEach(s -> System.out.println(s));
// 方法引用(更简洁)
list.forEach(System.out::println);
场景3:Stream API 结合(最常用!)
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 1. 过滤出偶数
List<Integer> evens = numbers.stream()
.filter(n -> n % 2 == 0) // Predicate: 判断是否为偶数
.collect(Collectors.toList()); // [2, 4, 6, 8, 10]
// 2. 每个数乘以2
List<Integer> doubled = numbers.stream()
.map(n -> n * 2) // Function: 映射操作
.collect(Collectors.toList()); // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
// 3. 求和
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b); // BinaryOperator: 累加
// 或使用方法引用
// .reduce(0, Integer::sum);
// 4. 排序
List<Integer> sorted = numbers.stream()
.sorted((a, b) -> b - a) // 降序排序
.collect(Collectors.toList());
// 5. 分组
Map<String, List<Integer>> groups = numbers.stream()
.collect(Collectors.groupingBy(
n -> n > 5 ? "大于5" : "小于等于5"
));
场景4:线程创建
// 传统方式
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程运行");
}
});
// Lambda 方式
Thread t2 = new Thread(() -> {
System.out.println("线程运行");
// 可以写多行代码
System.out.println("第二行");
});
t2.start();
场景5:Comparator 排序
List<String> names = Arrays.asList("张三", "李四", "王五");
// 传统方式
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
// Lambda 方式
Collections.sort(names, (a, b) -> a.compareTo(b));
// 更简洁的方式
names.sort((a, b) -> a.compareTo(b));
// 或使用方法引用
names.sort(String::compareTo);
方法引用
方法引用是 Lambda 的一种简洁写法,有4种形式:
// 1. 静态方法引用:类名::静态方法
Function<String, Integer> parser = Integer::parseInt;
// 等价于:s -> Integer.parseInt(s)
// 2. 实例方法引用:对象::实例方法
String str = "Hello";
Supplier<Integer> lengthSupplier = str::length;
// 等价于:() -> str.length()
// 3. 特定类型的任意对象方法引用:类名::实例方法
Function<String, String> upper = String::toUpperCase;
// 等价于:s -> s.toUpperCase()
// 4. 构造方法引用:类名::new
Supplier<List<String>> listSupplier = ArrayList::new;
// 等价于:() -> new ArrayList<>()
Function<Integer, String[]> arraySupplier = String[]::new;
// 等价于:size -> new String[size]
实际开发中的例子
例子1:数据处理
// 模拟从数据库查询的用户列表
List<User> users = Arrays.asList(
new User("张三", 25, "技术部"),
new User("李四", 30, "产品部"),
new User("王五", 22, "技术部"),
new User("赵六", 28, "市场部")
);
// 需求1:找出技术部的员工
List<User> techUsers = users.stream()
.filter(user -> "技术部".equals(user.getDepartment()))
.collect(Collectors.toList());
// 需求2:获取所有员工的姓名
List<String> names = users.stream()
.map(User::getName) // 方法引用
.collect(Collectors.toList());
// 需求3:按部门分组
Map<String, List<User>> byDept = users.stream()
.collect(Collectors.groupingBy(User::getDepartment));
// 需求4:计算平均年龄
double avgAge = users.stream()
.mapToInt(User::getAge) // 转为IntStream
.average()
.orElse(0.0);
// 需求5:找到年龄最大的员工
Optional<User> oldest = users.stream()
.max(Comparator.comparingInt(User::getAge));
oldest.ifPresent(user ->
System.out.println("年龄最大的员工:" + user.getName())
);
例子2:文件操作
// 读取文件,过滤空行,转为大写
try {
List<String> lines = Files.lines(Paths.get("data.txt"))
.filter(line -> !line.trim().isEmpty()) // 过滤空行
.map(String::toUpperCase) // 转为大写
.collect(Collectors.toList());
} catch (IOException e) {
e.printStackTrace();
}
例子3:事件处理
// 在 Swing 或 JavaFX 中
JButton button = new JButton("点击我");
// 传统方式
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// 复杂的业务逻辑
doSomething();
updateUI();
logAction();
}
});
// Lambda 方式(多行代码用 {})
button.addActionListener(e -> {
doSomething();
updateUI();
logAction();
});
// 如果方法体很复杂,可以提取方法
button.addActionListener(e -> handleButtonClick());
private void handleButtonClick() {
doSomething();
updateUI();
logAction();
}
注意事项
1. 变量作用域
int num = 10;
Runnable r = () -> {
// 可以读取外部变量(必须是 final 或 effectively final)
System.out.println(num); // ✅
// 不能修改外部变量
// num = 20; // ❌ 编译错误
};
2. this 关键字的区别
public class LambdaThis {
private String name = "外部类";
public void test() {
// 传统匿名类
Runnable r1 = new Runnable() {
private String name = "匿名类";
@Override
public void run() {
System.out.println(this.name); // "匿名类"
}
};
// Lambda
Runnable r2 = () -> {
// System.out.println(this.name); // "外部类"
// Lambda 中的 this 指向外部类的实例
};
}
}
3. 性能考虑
// 简单操作适合用 Lambda
list.forEach(System.out::println);
// 复杂逻辑不适合
list.forEach(item -> {
// 这里有很多行复杂逻辑...
// 建议提取为独立方法
processItem(item);
});
Lambda 与 Stream 的关系
Lambda 是基础,Stream 是应用:
// 没有 Lambda 的 Stream(难以想象)
list.stream()
.filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.startsWith("A");
}
})
.map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
})
.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer n) {
System.out.println(n);
}
});
// 有 Lambda 的 Stream(简洁优雅)
list.stream()
.filter(s -> s.startsWith("A"))
.map(String::length)
.forEach(System.out::println);
学习路径建议
- 先掌握基本语法:
(参数) -> 表达式 - 理解函数式接口:
@FunctionalInterface - 练习常用接口:
Consumer、Function、Predicate、Supplier - 结合 Stream API:这是 Lambda 最常用的场景
- 学习方法引用:让代码更简洁
- 理解原理:Lambda 的实现机制(invokedynamic)
练习任务
创建一个员工管理系统,使用 Lambda 实现:
// Employee 类
class Employee {
private String name;
private int age;
private String department;
private double salary;
// getters/setters
}
// 任务:
List<Employee> employees = getEmployees();
// 1. 找出工资大于10000的员工
// 2. 按部门分组
// 3. 计算每个部门的平均工资
// 4. 找出年龄最大的3个员工
// 5. 将员工姓名拼接成字符串,用逗号分隔
// 6. 按工资降序排序
总结:Lambda 表达式让 Java 代码从"面向对象"向"函数式编程"迈进了一大步。它不是万能的,但在集合操作、事件处理、并行计算等场景下能显著提升代码的可读性和简洁性。
建议:多写、多练。刚开始可能不习惯,但用熟后会"回不去"了。
更多推荐

所有评论(0)