从入门到实战:Java 函数式编程与高级工具类全解析
一、前言
在 Java 8 之后,函数式编程成为了现代 Java 开发的核心能力之一。它不仅让代码变得更简洁、灵活,还为 Stream 流、集合操作等高级特性提供了底层支撑。本文将结合代码实例,系统梳理函数式接口、Lambda 表达式、Arrays 工具类、反射等核心知识点,帮助你快速掌握 Java 函数式编程的精髓。
二、函数式接口:Lambda 的基石
2.1 什么是函数式接口?
函数式接口是只包含一个抽象方法的接口(可以有默认方法 / 静态方法),它是 Lambda 表达式和方法引用的基础。我们可以通过@FunctionalInterface注解标记,让编译器帮我们检查是否符合规范。
java
运行
@FunctionalInterface
interface MyFunctionalInterface {
void execute(); // 唯一抽象方法
// 可以有默认方法
default void defaultMethod() {
System.out.println("默认方法");
}
// 可以有静态方法
static void staticMethod() {
System.out.println("静态方法");
}
}
2.2 Java 内置四大核心函数式接口
Java 8 在java.util.function包中提供了一套标准函数式接口,覆盖了绝大多数开发场景:
表格
| 接口名 | 抽象方法 | 作用 | 典型场景 |
|---|---|---|---|
Supplier<T> |
T get() |
供给型:无参,返回一个结果 | 生成对象、提供默认值 |
Consumer<T> |
void accept(T t) |
消费型:接收一个参数,无返回 | 遍历集合、处理数据 |
Function<T,R> |
R apply(T t) |
函数型:接收一个参数,返回一个结果 | 数据转换、映射 |
Predicate<T> |
boolean test(T t) |
断言型:接收一个参数,返回布尔值 | 条件判断、数据过滤 |
三、四大核心函数式接口实战
3.1 Supplier:数据的生产者
Supplier 不接收参数,只负责返回结果,常用于延迟创建对象或生成数据。
java
运行
// 1. 基础用法:返回字符串
Supplier<String> supplier = () -> "Hello World";
System.out.println(supplier.get()); // 输出 Hello World
// 2. 构造方法引用:创建对象
Supplier<Student> studentSupplier = Student::new;
Student student = studentSupplier.get();
// 3. 原生类型变体:DoubleSupplier
DoubleSupplier randomSupplier = Math::random;
System.out.println(randomSupplier.getAsDouble()); // 输出随机数
3.2 Consumer:数据的消费者
Consumer 接收参数但不返回结果,常用于处理数据,比如集合遍历。
java
运行
// 1. 基础用法:打印字符串
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("Hello"); // 输出 Hello
// 2. 双参数版本:BiConsumer
BiConsumer<String, Integer> biConsumer = (name, age) -> System.out.println(name + ":" + age);
biConsumer.accept("Tom", 25); // 输出 Tom:25
// 3. 实际应用:List.forEach
List<String> list = Arrays.asList("A", "B", "C");
list.forEach(s -> System.out.println(s)); // 遍历打印
3.3 Function:数据的转换器
Function 接收一个参数,返回一个结果,是数据转换的核心接口,还支持链式操作。
java
运行
// 1. 基础用法:字符串转长度
Function<String, Integer> function = s -> s.length();
System.out.println(function.apply("Hello")); // 输出 5
// 2. 链式操作:andThen
Function<String, String> toUpper = s -> s.toUpperCase();
Function<String, String> addExclaim = s -> s + "!";
String result = toUpper.andThen(addExclaim).apply("hello");
System.out.println(result); // 输出 HELLO!
// 3. 双参数版本:BiFunction
BiFunction<Integer, Integer, Integer> sum = (a, b) -> a + b;
System.out.println(sum.apply(3, 5)); // 输出 8
3.4 Predicate:数据的判断器
Predicate 接收参数返回布尔值,常用于条件判断和数据过滤,还支持逻辑组合。
java
运行
// 1. 基础用法:判断字符串是否为空
Predicate<String> isEmpty = s -> s.isEmpty();
System.out.println(isEmpty.test("")); // 输出 true
// 2. 逻辑组合:and/or/negate
Predicate<String> startsWithA = s -> s.startsWith("A");
Predicate<String> endsWithZ = s -> s.endsWith("Z");
Predicate<String> and = startsWithA.and(endsWithZ);
System.out.println(and.test("AZ")); // 输出 true
// 3. 实际应用:Stream.filter
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0) // 保留偶数
.collect(Collectors.toList());
System.out.println(evenNumbers); // 输出 [2,4,6]
四、进阶:Comparator 与数组操作
4.1 Comparator:灵活的排序规则
当我们需要对对象数组排序时,Comparator 接口可以让我们不修改类代码,临时切换排序规则。
java
运行
// 1. 自然排序:类实现Comparable接口
public class Cust implements Comparable<Cust> {
private int id;
private String name;
private int age;
@Override
public int compareTo(Cust o) {
// 先按id升序,再按age升序,最后按name字典序
int result = Integer.compare(this.id, o.id);
if (result == 0) {
result = Integer.compare(this.age, o.age);
}
if (result == 0) {
result = this.name.compareTo(o.name);
}
return result;
}
}
// 2. 自定义排序:匿名内部类
Arrays.sort(cs, new Comparator<Cust>() {
@Override
public int compare(Cust o1, Cust o2) {
// 自定义排序逻辑
return Integer.compare(o1.getId(), o2.getId());
}
});
// 3. Lambda简化写法
Arrays.sort(cs, (o1, o2) -> Integer.compare(o1.getId(), o2.getId()));
4.2 Arrays 工具类:数组的高效操作
java.util.Arrays 提供了数组排序、查找、填充等便捷方法:
java
运行
// 1. 基础数组排序
int[] x = {4,3,2,8,1};
Arrays.sort(x);
System.out.println(Arrays.toString(x)); // 输出 [1,2,3,4,8]
// 2. 对象数组排序(依赖Comparable/Comparator)
Cust[] cs = genCustArray();
Arrays.sort(cs);
// 3. 二分查找:必须先排序
int index = Arrays.binarySearch(cs, new Cust(100, "张三", 20), comparator);
System.out.println(index); // 输出目标元素索引
五、高级:反射动态生成对象
反射是 Java 的高级特性,允许我们在运行时解析类、创建对象、调用方法,是很多框架(如 Spring、MyBatis)的底层核心。
5.1 核心步骤
- 获取类的
Class对象 - 筛选类的
setter方法 - 动态创建对象并调用
setter赋值
java
运行
// 1. 筛选setter方法
private static Method[] findAllSetMethods(Method[] ms) {
List<Method> list = new ArrayList<>();
for (Method m : ms) {
if (m.getName().startsWith("set")) {
list.add(m);
}
}
return list.toArray(new Method[0]);
}
// 2. 动态生成对象数组
// 方法作用:把字符串转成 Cust 对象数组,抛出异常是因为反射调用会有检查异常
public static Cust[] genCustArray() throws Exception {
// 1. 准备要解析的字符串(数据源)
String str = "100-张三-20,20-李四-22";
// 2. 第一步拆分:按逗号把字符串拆成 ["100-张三-20", "20-李四-22"]
String[] strs = str.split(",");
// 3. 创建一个 Cust 数组,长度和拆分后的字符串数组一样(有2个元素就创建长度为2的数组)
Cust[] cs = new Cust[strs.length];
// 4. 获取 Cust 类的「类对象」(反射的入口:通过这个对象能操作 Cust 类的所有东西)
Class cl = Cust.class;
// 5. 找到 Cust 类中所有以 set 开头的方法(setId、setName、setAge)
Method[] setMethods = findAllSetMethods(cl.getDeclaredMethods());
// 6. 定义一个索引,用来给 Cust 数组赋值
int index = 0;
// 7. 遍历拆分后的每个字符串(先处理 "100-张三-20",再处理 "20-李四-22")
for (String s : strs) {
// 8. 第二步拆分:按横杠拆成 ["100", "张三", "20"]
String[] parts = s.split("-");
// 9. 反射创建 Cust 对象(等价于 new Cust(),但用反射的方式)
Object obj = cl.newInstance();
// 10. 定义一个小索引,用来取 parts 里的数值
int i = 0;
// 11. 遍历所有 set 方法(setId → setName → setAge)
for (Method m : setMethods) {
// 12. 核心:调用 set 方法赋值!
// 比如:调用 setId(obj, "100") → 给 obj 的 id 赋值 100
// 调用 setName(obj, "张三") → 给 obj 的 name 赋值 张三
// 调用 setAge(obj, "20") → 给 obj 的 age 赋值 20
m.invoke(obj, parts[i++]);
}
// 13. 把赋值完成的 Cust 对象放到数组里
cs[index++] = (Cust) obj;
}
// 14. 返回最终的 Cust 数组
return cs;
}
六、总结
本文从函数式接口出发,系统讲解了 Java 8 函数式编程的核心知识点:
- 函数式接口是 Lambda 的基础,四大核心接口覆盖了大部分开发场景
- Lambda 表达式和方法引用让代码更简洁、灵活
Comparator和Arrays工具类让数组操作更高效- 反射是 Java 高级特性,实现了动态对象创建和赋值
掌握这些知识,你就能更好地使用 Java 8 + 的现代特性,写出更优雅、高效的代码。
七、结语
函数式编程是 Java 开发的必备技能,它不仅能提升代码质量,还能让你更好地理解现代 Java 框架的底层原理。如果本文对你有帮助,欢迎点赞、收藏、评论~
更多推荐




所有评论(0)