Java Lambda 表达式入门:简化代码的函数式编程技巧
·
一、函数式接口:Lambda 的底层基石(补充核心细节)
Lambda 表达式的使用前提是函数式接口(Functional Interface),这是理解 Lambda 的核心,补充关键知识点:
1. 函数式接口的严格定义
- 接口中仅有一个抽象方法(可包含默认方法、静态方法、Object 类的方法重写);
- 可通过
@FunctionalInterface注解标记(非强制,但推荐使用,编译器会校验是否符合函数式接口规范)。
示例:标准函数式接口(带注解)
java
运行
// 正确:仅一个抽象方法,符合函数式接口
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b); // 唯一抽象方法
// 允许添加默认方法(Java 8+)
default void printResult(int result) {
System.out.println("计算结果:" + result);
}
// 允许添加静态方法
static int getDefaultValue() {
return 0;
}
// 允许重写Object类的方法(不算抽象方法)
@Override
boolean equals(Object obj);
}
// 错误:多个抽象方法,加@FunctionalInterface会编译报错
// @FunctionalInterface
// public interface BadInterface {
// void method1();
// void method2();
// }
2. JDK 内置核心函数式接口(实战无需自定义)
JDK 8 已内置大量常用函数式接口,无需手动定义,直接使用即可:
表格
| 接口名 | 抽象方法 | 功能描述 | 示例场景 |
|---|---|---|---|
Runnable |
void run() |
无参无返回值 | 线程任务、异步执行 |
Consumer<T> |
void accept(T t) |
单参数无返回值 | 遍历集合(forEach) |
Supplier<T> |
T get() |
无参有返回值 | 数据生成、懒加载 |
Function<T, R> |
R apply(T t) |
单参数有返回值 | 数据转换、映射 |
BiFunction<T, U, R> |
R apply(T t, U u) |
双参数有返回值 | 二元运算、组合判断 |
Predicate<T> |
boolean test(T t) |
单参数返回布尔值 | 条件过滤、判断 |
实战示例:使用内置接口替代自定义接口
java
运行
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
public class BuiltInFunctionalInterfaceDemo {
public static void main(String[] args) {
// 1. Consumer:单参数无返回值(替代自定义NoReturnOneParam)
Consumer<Integer> printNum = (a) -> System.out.println("参数:" + a);
printNum.accept(10);
// 2. Supplier:无参有返回值(生成随机数)
Supplier<Double> randomNum = () -> Math.random();
System.out.println("随机数:" + randomNum.get());
// 3. Function:单参数有返回值(字符串转长度)
Function<String, Integer> strLength = s -> s.length();
System.out.println("字符串长度:" + strLength.apply("Java Lambda"));
// 4. Predicate:单参数返回布尔值(判断是否偶数)
Predicate<Integer> isEven = num -> num % 2 == 0;
System.out.println("是否偶数:" + isEven.test(8));
}
}
二、Lambda 语法进阶简化规则(补充完整场景)
原文提到了基础简化规则,这里补充完整的简化逻辑,让代码更极致简洁:
表格
| 场景 | 原始写法 | 简化写法 | 简化规则 |
|---|---|---|---|
| 无参无返回值 | () -> { System.out.println("test"); } |
() -> System.out.println("test") |
单行代码省略大括号 |
| 单参数无返回值 | (a) -> { System.out.println(a); } |
a -> System.out.println(a) |
单参数省略小括号 + 单行代码省略大括号 |
| 多参数指定类型 | (int a, int b) -> { return a + b; } |
(a, b) -> a + b |
省略参数类型 + 单行返回省略大括号和 return |
| 单参数有返回值 | (a) -> { return a * 2; } |
a -> a * 2 |
省略小括号 + 省略大括号和 return |
核心简化原则:
- 参数类型可省略(编译器自动推断,称为 “类型推断”);
- 单参数可省略小括号;
- 代码块仅一行时,可省略大括号;
- 代码块仅一行且有返回值时,省略大括号的同时必须省略
return。
三、方法引用:Lambda 的终极简化(补充核心用法)
当 Lambda 表达式的逻辑只是 “调用一个已存在的方法” 时,可使用方法引用进一步简化,语法:类名/对象名::方法名。
1. 方法引用的 4 种核心形式
表格
| 方法引用类型 | 语法 | 对应 Lambda 写法 | 示例 |
|---|---|---|---|
| 对象::实例方法 | obj::method |
(参数) -> obj.method(参数) |
System.out::println 替代 s -> System.out.println(s) |
| 类::静态方法 | Class::staticMethod |
(参数) -> Class.staticMethod(参数) |
Math::max 替代 (a,b) -> Math.max(a,b) |
| 类::实例方法 | Class::method |
(obj, 参数) -> obj.method(参数) |
String::length 替代 s -> s.length() |
| 构造器引用 | Class::new |
(参数) -> new Class(参数) |
ArrayList::new 替代 () -> new ArrayList<>() |
实战示例:方法引用简化 Lambda
java
运行
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
public class MethodReferenceDemo {
public static void main(String[] args) {
// 1. 对象::实例方法(System.out是PrintStream对象,println是实例方法)
List<String> list = Arrays.asList("Java", "Lambda", "Method Reference");
// 原始Lambda:s -> System.out.println(s)
list.forEach(System.out::println);
// 2. 类::静态方法(Math.max是静态方法)
// 原始Lambda:(a,b) -> Math.max(a,b)
BiFunction<Integer, Integer, Integer> maxFunc = Math::max;
System.out.println("最大值:" + maxFunc.apply(10, 20));
// 3. 类::实例方法(String.length是实例方法)
// 原始Lambda:s -> s.length()
Function<String, Integer> lengthFunc = String::length;
System.out.println("字符串长度:" + lengthFunc.apply("Java"));
// 4. 构造器引用(ArrayList构造器)
// 原始Lambda:() -> new ArrayList<>()
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> newList = listSupplier.get();
newList.add("Test");
System.out.println("新集合:" + newList);
}
}
四、Lambda 实战场景扩展(补充高频使用场景)
1. 集合遍历(替代 for 循环)
java
运行
import java.util.ArrayList;
import java.util.List;
public class LambdaCollectionDemo {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>();
fruits.add("苹果");
fruits.add("香蕉");
fruits.add("橙子");
// 传统for循环
for (int i = 0; i < fruits.size(); i++) {
System.out.println(fruits.get(i));
}
// 增强for循环
for (String fruit : fruits) {
System.out.println(fruit);
}
// Lambda + forEach(最简)
fruits.forEach(fruit -> System.out.println(fruit));
// 方法引用进一步简化
fruits.forEach(System.out::println);
}
}
2. 集合排序(替代 Comparator 匿名内部类)
java
运行
import java.util.ArrayList;
import java.util.List;
class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + "}";
}
}
public class LambdaSortDemo {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("张三", 20));
students.add(new Student("李四", 18));
students.add(new Student("王五", 22));
// 传统匿名内部类排序(按年龄升序)
/*
Collections.sort(students, new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
return s1.getAge() - s2.getAge();
}
});
*/
// Lambda简化排序
students.sort((s1, s2) -> s1.getAge() - s2.getAge());
// 方法引用+Comparator静态方法进一步简化
// students.sort(Comparator.comparingInt(Student::getAge));
System.out.println("按年龄排序后:");
students.forEach(System.out::println);
}
}
3. 线程创建(原文基础上简化)
java
运行
public class LambdaThreadDemo {
public static void main(String[] args) {
// 传统匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程启动:" + Thread.currentThread().getName());
}
}).start();
// Lambda简化(多行代码)
new Thread(() -> {
System.out.println("Lambda线程1:" + Thread.currentThread().getName());
System.out.println("执行多步逻辑");
}).start();
// Lambda极简(单行代码)
new Thread(() -> System.out.println("Lambda线程2:" + Thread.currentThread().getName())).start();
}
}
五、Lambda 常见坑点与避坑指南
1. 局部变量捕获的限制
Lambda 中引用的局部变量必须是 final 或 “事实上的 final”(即变量值从未被修改),否则编译报错:
java
运行
public class LambdaVariablePitfall {
public static void main(String[] args) {
int num = 10; // 事实上的final(未修改)
// num = 20; // 若修改,下面Lambda会报错
Consumer<Integer> printNum = (a) -> {
// Lambda中不能修改局部变量,也不能引用非final的局部变量
System.out.println(a + num);
};
printNum.accept(5);
}
}
原因:Lambda 可能在另一个线程执行,局部变量的生命周期与 Lambda 执行周期不一致,final 保证变量值不变。
2. 函数式接口的误判
若接口有多个抽象方法,即使加上 @FunctionalInterface 也会编译报错,需确保仅一个抽象方法:
java
运行
// 错误示例:多个抽象方法,编译报错
// @FunctionalInterface
// public interface MyInterface {
// void method1();
// void method2();
// }
3. 代码可读性权衡
Lambda 简化代码的同时,过度简化会降低可读性,例如:
java
运行
// 不推荐:一行代码逻辑过于复杂,可读性差
Function<Integer, Integer> complexFunc = a -> a > 0 ? a * 2 : (a < 0 ? a + 10 : 0);
// 推荐:复杂逻辑拆分为方法,用方法引用简化
public static int calculate(int a) {
if (a > 0) {
return a * 2;
} else if (a < 0) {
return a + 10;
} else {
return 0;
}
}
Function<Integer, Integer> goodFunc = LambdaVariablePitfall::calculate;
六、核心总结(补充进阶要点)
总结
- Lambda 核心依赖函数式接口:接口仅一个抽象方法(可使用
@FunctionalInterface校验),JDK 内置Consumer/Function等接口可直接复用; - 语法简化有章可循:参数类型、小括号、大括号、return 可按规则省略,方法引用是 Lambda 的终极简化形式;
- 实战场景聚焦简化:集合遍历、排序、线程创建、异步任务是 Lambda 的高频使用场景,可大幅减少模板代码;
- 避坑关键:Lambda 引用的局部变量需为 final / 事实上的 final,复杂逻辑避免过度简化,保证可读性。
更多推荐




所有评论(0)