目录

Java-Lambda-表达式与函数式接口解析

Java — Lambda 表达式与函数式接口解析

在 Java 8 引入 Lambda 表达式函数式接口 之后,Java 语言在简洁性与表达力上有了质的提升。本文将逐步讲解相关的核心知识点,包括 函数式接口定义、Lambda 使用、方法引用 以及 常见函数式接口


一、函数式接口(Functional Interface)

定义:接口中只允许存在一个抽象方法(可以有多个 defaultstatic 方法),这样的接口就可以作为 Lambda 表达式 的目标类型。

示例:

@FunctionalInterface
interface MyInterface {
    int sum(int i, int j);
}

这里的 MyInterface 只有一个抽象方法 sum,因此是一个函数式接口。
Java 提供了 @FunctionalInterface 注解来保证语义明确,编译器会强制检查该接口是否满足“只有一个抽象方法”的条件。

还可以有 default 方法:

@FunctionalInterface
interface MyInterface1 {
    int haha();

    default int hello() {
        return 2;
    }
}

即便 hellodefaulthaha 仍然是唯一抽象方法,所以依然是函数式接口。


二、Lambda 表达式的语法与应用

Lambda 表达式本质上是对 匿名类 的简化,它能让代码更简洁、可读性更强。

示例:

MyInterface myInterface = (i, j) -> i + j;
System.out.println(myInterface.sum(1, 2)); // 输出 3

对应的传统写法需要创建一个匿名类:

MyInterface myInterface = new MyInterface() {
    @Override
    public int sum(int i, int j) {
        return i + j;
    }
};

Lambda 大幅简化了开发。


三、方法引用(Method Reference)

Lambda 有时只是调用已有方法,可以进一步简化为 方法引用

示例:

Collections.sort(list, String::compareTo);

等价于:

Collections.sort(list, (s1, s2) -> s1.compareTo(s2));

方法引用有四类:

  1. 类名::静态方法
  2. 对象::实例方法
  3. 类名::实例方法
  4. 类名::new(构造器引用)

四、常见函数式接口

java.util.function 包预定义了大量常用函数式接口。

1. Consumer<T> / BiConsumer<T, U> —— 有入参,无返回值

BiConsumer<String, String> consumer = (a, b) -> System.out.println(a + b);
consumer.accept("Hello ", "World");

2. Function<T, R> / BiFunction<T, U, R> —— 有入参,有返回值

Function<String, Integer> function = s -> s.length();
System.out.println(function.apply("Hello World"));  // 11

BiFunction<String, Integer, Integer> biFunction = (s, i) -> s.substring(i).length();
System.out.println(biFunction.apply("Hello World, Mario", 5));

3. Supplier<T> —— 无入参,有返回值

Supplier<String> supplier = () -> UUID.randomUUID().toString();
System.out.println(supplier.get());

4. Runnable —— 无入参,无返回值

Runnable runnable = () -> System.out.println("Hello World");
new Thread(runnable).start();

5. Predicate<T> —— 断言型接口(返回 boolean

Predicate<Integer> even = i -> i % 2 == 0;
System.out.println(even.test(2));            // true
System.out.println(even.negate().test(2));   // false

这里的 negate() 会返回一个新的 Predicate,逻辑是:

(t) -> !even.test(t)

下面用一个例子来展示如何使用 Supplier(供给者)、Predicate(断言)、Function(转换器)、Consumer(消费者) 这四类核心接口,并将它们自然地串联在一起,构建一条“数据处理流水线”。

https://i-blog.csdnimg.cn/direct/b9a20cde4db94745a4e3a7ebf21806c4.png

private static void myMethod(Predicate<String> isNumber,
                             Supplier<String> supplier,
                             Consumer<Integer> consumer,
                             Function<String, Integer> change) {
    if (isNumber.test(supplier.get())) {
        consumer.accept(change.apply(supplier.get()));
    } else {
        System.out.println("not a number");
    }
}

流程可以分解为:

  1. Supplier 负责“提供数据”
  2. Predicate 验证数据是否合法(是否为数字)
  3. Function 将字符串转换为整数 Integer
  4. Consumer 消费结果

这就是一种典型的 函数组合 思路。完整代码如下:

import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

public class FunctionDemo {
    public static void main(String[] args) {
        // 1. 定义数据提供者函数
        Supplier<String> supplier = () -> "47";
        // 2. 断言:验证是否为数字
        Predicate<String> isNumber = str -> str.matches("-?\\d+(\\.\\d+)?");
        // 3. Function转换器:把字符串变成数字
        Function<String, Integer> change = str -> Integer.parseInt(str);
        // 4. 消费者:打印数字
        Consumer<Integer> consumer = integer -> {
            if (integer % 2 == 0) System.out.println("even: " + integer);
            else System.out.println("odd: " + integer);
        };

        // 第一次调用
        myMethod(isNumber, supplier, consumer, change);

        // 使用 Lambda 与方法引用直接传参
        myMethod(str -> str.matches("-?\\d+(\\.\\d+)?"),
                () -> "777",
                integer -> {
                    if (integer % 2 == 0) System.out.println("even: " + integer);
                    else System.out.println("odd: " + integer);
                },
                Integer::parseInt);
    }

    private static void myMethod(Predicate<String> isNumber,
                                 Supplier<String> supplier,
                                 Consumer<Integer> consumer,
                                 Function<String, Integer> change) {
        // 将上述函数串在一起,判断42的奇偶性
        if (isNumber.test(supplier.get())) {
            consumer.accept(change.apply(supplier.get()));
        } else {
            System.out.println("not a number");
        }
    }
}

程序输出为:

odd: 47
odd: 777

五、测试程序(仅为练习使用)

import java.util.ArrayList;
import java.util.Collections;
import java.util.UUID;
import java.util.function.*;

// 函数式接口:接口中只有一个(有且仅有一个)未实现的方法,这个接口就叫函数式接口,只要是函数式接口就可以用Lambda表达式简化
interface MyInterface {
    int sum(int i, int j);
}

// 这个接口也符合有且仅有一个未实现的方法 这个标准,因此也是函数式接口
// 如果以后检查某个接口是否为函数式接口,可以用 @FunctionalInterface 注解
// 如果没有报错,说明是函数式接口
@FunctionalInterface
interface MyInterface1 {
    int haha();

    default int hello() {
        return 2;
    }
}

public class Lambda {
    public static void main(String[] args) {
        MyInterface myInterface = (i, j) -> i + j;
        System.out.println(myInterface.sum(1, 2));

        MyInterface1 myInterface1 = () -> 1;
        System.out.println(myInterface1.haha());


        var list = new ArrayList<String>();
        list.add("Alice");
        list.add("Bob");
        list.add("Charlie");
        list.add("David");
        Collections.sort(list, (s1, s2) -> s2.compareTo(s1));
        System.out.println(list);

        // 类::方法  引用类中的实例方法(或静态方法);忽略lambda的完整写法
        Collections.sort(list, String::compareTo);
        System.out.println(list);


//        new Thread(() -> System.out.println("Hello World")).start();

        // 1. 有入参,无出参
        BiConsumer<String, String> consumer = (a, b) -> System.out.println(a + b);
        consumer.accept("Hello ", "World");

        // 2. 有入参,有出参
        Function<String, Integer> function = s -> s.length();
        System.out.println(function.apply("Hello World"));


        BiFunction<String, Integer, Integer> biFunction = (s, i) -> s.substring(i).length();
        System.out.println(biFunction.apply("Hello World, Mario", 5));

        // 3. 无入参,无出参
        Runnable runnable = () -> System.out.println("Hello World");
        new Thread(runnable).start();

        // 4. 无入参,有出参
        Supplier<String> supplier = () -> UUID.randomUUID().toString();
        System.out.println(supplier.get());

        // 断言
        Predicate<Integer> even = i -> i % 2 == 0;
        System.out.println(even.test(2));  // 正向判断
        System.out.println(even.negate().test(2));  // 反向判断
        /**
         even.negate()
             even 是 i -> i % 2 == 0
             negate() 会返回一个新的 Predicate:
             (t) -> !even.test(t)

         .test(2)
             调用这个新的 Predicate 的 test(2)
             它内部会先调用 even.test(2) → 结果是 true
             再取反 → false
         */
    }
}

程序运行结果为:

3
1
[David, Charlie, Bob, Alice]
[Alice, Bob, Charlie, David]
Hello World
11
13
Hello World
8209a7dd-8a1e-4c2e-bf1b-669fedda6b46
true
false

六、总结

  1. 函数式接口 是 Lambda 的基础,一个接口只能有一个抽象方法。
  2. Lambda 表达式 极大简化了匿名类写法,使 Java 更接近函数式编程。
  3. 方法引用 可以在 Lambda 仅仅调用现有方法时使用,更加简洁。
  4. Java 8 内置的 ConsumerFunctionSupplierPredicate 等接口满足了大多数常见场景。

得益于 Lambda 与函数式接口,Java 在流式 API(Stream API)、并发编程、事件驱动开发等领域变得更强大、更现代化。