一、前言

在 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 核心步骤

  1. 获取类的Class对象
  2. 筛选类的setter方法
  3. 动态创建对象并调用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 函数式编程的核心知识点:

  1. 函数式接口是 Lambda 的基础,四大核心接口覆盖了大部分开发场景
  2. Lambda 表达式和方法引用让代码更简洁、灵活
  3. ComparatorArrays 工具类让数组操作更高效
  4. 反射是 Java 高级特性,实现了动态对象创建和赋值

掌握这些知识,你就能更好地使用 Java 8 + 的现代特性,写出更优雅、高效的代码。


七、结语

函数式编程是 Java 开发的必备技能,它不仅能提升代码质量,还能让你更好地理解现代 Java 框架的底层原理。如果本文对你有帮助,欢迎点赞、收藏、评论~

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐