Skip to content

Java 8 中最核心和常用的函数式接口及其使用

Java 8 在 java.util.function 包中引入了众多内置的函数式接口。所谓函数式接口(Functional Interface),就是只包含一个抽象方法的接口。你可以使用 @FunctionalInterface 注解来标记它,编译器会帮你检查是否符合规则。

1. Runnable - 无参无返回值

所在包: java.lang抽象方法: void run()用途: 通常用于定义在线程中执行的任务,或者任何不需要参数和返回值的操作。

使用示例:

java
// 传统匿名内部类方式
Runnable r1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello World!");
    }
};

// Lambda 表达式方式
Runnable r2 = () -> System.out.println("Hello Lambda!");

// 执行
new Thread(r1).start();
new Thread(r2).start();
new Thread(() -> System.out.println("直接传入Lambda")).start();

2. Consumer<T> - 消费型接口

所在包: java.util.function抽象方法: void accept(T t)用途: 接收一个参数,并对其进行某种操作(消费),不返回任何结果。

常用变体:

  • BiConsumer<T, U>: 接收两个参数。
  • IntConsumer: 接收一个 int 参数,避免装箱拆箱。

使用示例:

java
// 定义一个消费字符串的Consumer
Consumer<String> printConsumer = (str) -> System.out.println(str);
printConsumer.accept("Hello Consumer!"); // 输出: Hello Consumer!

// 集合的 forEach 方法就接收一个Consumer
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println("Hello, " + name));
// 使用方法引用更简洁: names.forEach(System.out::println);

// 使用 andThen 进行组合消费
Consumer<String> c1 = s -> System.out.print("Length: ");
Consumer<String> c2 = s -> System.out.println(s.length());
Consumer<String> combined = c1.andThen(c2);
combined.accept("Java"); // 输出: Length: 4

3. Supplier<T> - 供给型接口

所在包: java.util.function抽象方法: T get()用途: 不需要参数,返回一个结果。类似于“工厂”或“提供者”。

常用变体:

  • BooleanSupplier, IntSupplier, LongSupplier, DoubleSupplier

使用示例:

java
// 定义一个生成随机数的Supplier
Supplier<Double> randomSupplier = () -> Math.random();
System.out.println(randomSupplier.get()); // 输出一个随机数

// 延迟执行/懒加载的经典场景
public static String getExpensiveValue() {
    // 假设这是一个耗时操作
    return "Expensive Value";
}

public static String lazyGet(Supplier<String> supplier) {
    if (someCondition) { // 只有在某些条件下才真正调用获取值的方法
        return supplier.get();
    }
    return "Default Value";
}

// 调用 lazyGet 时,并不会立即执行 getExpensiveValue 方法
// 只有当 supplier.get() 被调用时才会执行
String result = lazyGet(() -> getExpensiveValue());

4. Predicate<T> - 断言型接口

所在包: java.util.function抽象方法: boolean test(T t)用途: 接收一个参数,返回一个布尔值。常用于过滤、条件判断。

常用变体:

  • BiPredicate<T, U>: 接收两个参数。
  • IntPredicate: 接收一个 int 参数。

默认方法:

  • and(Predicate other): 与
  • or(Predicate other): 或
  • negate(): 非

使用示例:

java
// 定义一个判断字符串是否为空的Predicate
Predicate<String> isEmpty = String::isEmpty; // s -> s.isEmpty()
System.out.println(isEmpty.test("")); // true
System.out.println(isEmpty.test("Hi")); // false

// 集合的 stream().filter() 方法就接收一个Predicate
List<String> list = Arrays.asList("Java", "Python", "C", "", "JavaScript");
List<String> nonEmpty = list.stream()
                            .filter(str -> !str.isEmpty()) // 过滤掉空字符串
                            .collect(Collectors.toList());
// 输出: [Java, Python, C, JavaScript]

// 组合使用:判断字符串长度是否大于3并且不是空字符串
Predicate<String> longerThan3 = s -> s.length() > 3;
Predicate<String> notEmpty = s -> !s.isEmpty();

Predicate<String> combinedPredicate = notEmpty.and(longerThan3);
List<String> result = list.stream()
                         .filter(combinedPredicate)
                         .collect(Collectors.toList());
// 输出: [Java, Python, JavaScript]

5. Function<T, R> - 函数型接口

所在包: java.util.function抽象方法: R apply(T t)用途: 接收一个参数(类型 T),返回一个结果(类型 R)。用于类型的转换或映射。

常用变体:

  • BiFunction<T, U, R>: 接收两个参数 (T, U),返回 R。
  • UnaryOperator<T>: Function<T, T> 的特例,接收 T 返回 T。
  • BinaryOperator<T>: BiFunction<T, T, T> 的特例,接收两个 T 返回一个 T。
  • IntFunction<R>, ToIntFunction<T> 等:用于基本类型,提高效率。

默认方法:

  • compose(Function before): 先执行 before 的 apply,再执行当前 apply。
  • andThen(Function after): 先执行当前 apply,再执行 after 的 apply。

使用示例:

java
// 定义一个将字符串转换为整数的Function
Function<String, Integer> parseInt = Integer::parseInt; // s -> Integer.parseInt(s)
Integer number = parseInt.apply("123"); // 返回 123

// 集合的 stream().map() 方法就接收一个Function
List<String> names = Arrays.asList("alice", "bob", "charlie");
List<String> upperCaseNames = names.stream()
                                   .map(name -> name.toUpperCase()) // 将每个元素映射为大写
                                   .collect(Collectors.toList());
// 输出: [ALICE, BOB, CHARLIE]
// 使用方法引用: .map(String::toUpperCase)

// 组合使用:先乘以2,然后再将结果平方
Function<Integer, Integer> timesTwo = x -> x * 2;
Function<Integer, Integer> squared = x -> x * x;

Function<Integer, Integer> composed = timesTwo.andThen(squared);
Integer result = composed.apply(3); // (3 * 2) ^ 2 = 36
System.out.println(result);

// UnaryOperator 示例 (Function<String, String>)
UnaryOperator<String> greet = s -> "Hello, " + s;
System.out.println(greet.apply("World")); // Hello, World

6. Callable<V> - 可调用接口

所在包: java.util.concurrent抽象方法: V call() throws Exception用途: 类似于 Runnable,但它可以返回值并且可以抛出受检异常。通常用于 ExecutorService 提交有返回值的任务。

使用示例:

java
// 传统方式
Callable<String> callable = new Callable<String>() {
    @Override
    public String call() throws Exception {
        // 模拟一些工作
        Thread.sleep(1000);
        return "Result from Callable";
    }
};

// Lambda 表达式方式 (因为只有一个抽象方法,所以可以使用)
Callable<String> callableWithLambda = () -> {
    Thread.sleep(1000);
    return "Result from Lambda Callable";
};

// 配合线程池使用
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(callableWithLambda);

// 获取结果(会阻塞)
String result = future.get();
System.out.println(result); // 输出: Result from Lambda Callable
executor.shutdown();

总结与最佳实践

接口方法用途典型场景
Consumer<T>void accept(T t)消费一个参数forEach, 打印, 数据持久化
Supplier<T>T get()提供一个结果懒加载, 工厂方法, 生成配置
Predicate<T>boolean test(T t)判断条件filter, 数据校验, 条件检查
Function<T, R>R apply(T t)转换/映射一个值map, 类型转换, 计算衍生值
Runnablevoid run()执行一个动作线程任务, 无参无返回的操作
Callable<V>V call()执行并返回结果线程池提交有返回值的任务

最佳实践:

  1. 优先使用内置接口: 在绝大多数情况下,都应优先使用这些内置接口,而不是自己重新定义。
  2. 使用方法引用: 当 Lambda 表达式仅仅是调用一个已有方法时,用方法引用(如 System.out::println, String::length)会使代码更简洁。
  3. 注意组合操作: 利用 Predicate, Function, Consumer 提供的 and, or, negate, andThen, compose 等方法,可以将简单的函数组合成更复杂的逻辑,让代码更具声明性和可读性。
  4. 区分有无异常: RunnableCallable 的主要区别在于后者能抛出受检异常并有返回值。根据你的任务需求选择。
/src/technology/dateblog/2025/08/20250822-java8%E4%B8%AD%E6%9C%80%E6%A0%B8%E5%BF%83%E5%92%8C%E5%B8%B8%E7%94%A8%E7%9A%84%E5%87%BD%E6%95%B0%E5%BC%8F%E6%8E%A5%E5%8F%A3%E5%8F%8A%E5%85%B6%E4%BD%BF%E7%94%A8.html