Skip to content

函数式接口

Lambda 不是孤立存在的。它需要一个"目标类型"——在 Java 里,这个目标类型就是函数式接口

什么是函数式接口

只有一条抽象方法的接口。不信?翻开 JDK 源码看看 Runnable

java
@FunctionalInterface
public interface Runnable {
    void run();
}

就这一个方法。Lambda 之所以能表示 Runnable,是因为它符合"单一抽象方法"的规则。

@FunctionalInterface 注解是给你的 IDE 看的——加上它,编译器会帮你检查这个接口是否真的只有一条抽象方法。漏掉了会报错,多于一条也会报错。

内置函数式接口一览

JDK 在 java.util.function 包里预置了最常用的类型:

Consumer<T> — 消费,不返回

java
Consumer<String> printer = s -> System.out.println(s);
printer.accept("Hello");  // 打印 Hello,无返回值

// 变体:BiConsumer<T, U>
BiConsumer<String, Integer> printPair = (s, i) -> System.out.println(s + i);
printPair.accept("次数:", 5);  // 次数: 5

Supplier<T> — 不消费,只产出

java
Supplier<LocalDateTime> now = LocalDateTime::now;
LocalDateTime time = now.get();

// 等价于
LocalDateTime time = LocalDateTime.now();

Function<T, R> — 吃进去 T,吐出来 R

java
Function<String, Integer> toLength = String::length;
toLength.apply("hello");  // 5

// 链式:andThen
Function<String, String> pipeline = 
    String::toUpperCase
    .andThen(s -> s + "!");
pipeline.apply("hello");  // HELLO!

Predicate<T> — 判断真假

java
Predicate<String> isEmpty = String::isEmpty;
isEmpty.test("");     // true
isEmpty.test("abc");  // false

// 组合:与、或、非
Predicate<String> notEmpty = isEmpty.negate();
Predicate<String> longEnough = s -> s.length() > 3;

Predicate<String> combined = notEmpty.and(longEnough);
combined.test("hello");  // true
combined.test("hi");     // false
combined.test("");       // false

Operator 系列 — 特殊 Function

类型方法签名说明
UnaryOperator&lt;T&gt;T apply(T t)一元操作
BinaryOperator&lt;T&gt;T apply(T t1, T t2)二元操作
IntUnaryOperatorint applyAsInt(int x)原始类型版本
IntBinaryOperatorint applyAsInt(int a, int b)原始类型版本
java
// UnaryOperator — 一元运算
UnaryOperator<Integer> doubleValue = n -> n * 2;
doubleValue.apply(5);  // 10

// BinaryOperator — 二元运算
BinaryOperator<Integer> max = Math::max;
max.apply(3, 7);  // 7

// 更常见的用法:reduce
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
Integer sum = nums.stream()
    .reduce(0, Integer::sum);  // BinaryOperator

自定义函数式接口

当内置接口不够用时,自己写一个。

场景一:带返回值的回调

java
@FunctionalInterface
interface Transformer<T, R> {
    R transform(T input);
}

// 使用
Transformer<String, Integer> toLength = String::length;
toLength.transform("hello");  // 5

场景二:条件判断

java
@FunctionalInterface
interface Condition<T> {
    boolean evaluate(T item);
}

// 用于筛选逻辑
List<String> longNames = names.stream()
    .filter(name -> name.length() > 5)  // 匿名 Lambda
    .collect(Collectors.toList());

// 用自定义接口让代码更清晰
Condition<String> isLong = name -> name.length() > 5;
// 但说实话,这种场景用内置 Predicate 就行
Predicate<String> isLong2 = name -> name.length() > 5;

场景三:带两个参数一个有返回值

java
@FunctionalInterface
interface Operation {
    int execute(int a, int b);
}

Operation add = (a, b) -> a + b;
Operation multiply = (a, b) -> a * b;

System.out.println(add.execute(3, 4));      // 7
System.out.println(multiply.execute(3, 4)); // 12

方法引用与函数式接口

方法引用本质上也是一种 Lambda,只是更简洁的形式。

java
import java.util.function.*;

// 静态方法引用
Function<String, Integer> f1 = Integer::parseInt;    // s -> Integer.parseInt(s)
f1.apply("123");  // 123

// 实例方法引用(任意对象)
Function<String, String> f2 = String::toUpperCase;   // s -> s.toUpperCase()
f2.apply("hello");  // HELLO

// 实例方法引用(特定对象)
String prefix = "Hello, ";
Function<String, String> f3 = prefix::concat;        // s -> prefix.concat(s)
f3.apply("World");  // Hello, World

// 构造方法引用
Supplier<ArrayList<String>> f4 = ArrayList::new;    // () -> new ArrayList<>()
f4.get();  // 一个新的 ArrayList

// 带参数的构造方法
Function<Integer, String[]> f5 = String[]::new;     // n -> new String[n]
f5.apply(3);  // 一个长度为 3 的 String 数组

泛型函数式接口

函数式接口也可以是泛型的:

java
// 泛型函数式接口
@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}

// 使用
Converter<String, Integer> strToInt = Integer::parseInt;
Converter<Integer, String> intToStr = Object::toString;

Converter<List<String>, Set<String>> listToSet = list -> new HashSet<>(list);

组合的艺术

函数式接口最强大的地方在于可以组合

java
Predicate<String> isBlank = String::isBlank;
Predicate<String> isNotBlank = isBlank.negate();
Predicate<String> isLong = s -> s.length() > 5;

// 组合:既不为空,又要够长
Predicate<String> valid = isNotBlank.and(isLong);

valid.test("hello world");  // true
valid.test("hi");           // false
valid.test("");             // false

// Function 组合
Function<Integer, Integer> doubleValue = n -> n * 2;
Function<Integer, Integer> addOne = n -> n + 1;

// 先加倍再加一:addOne(double(3)) → addOne(6) → 7
Function<Integer, Integer> combined = doubleValue.andThen(addOne);
combined.apply(3);  // 7

// compose:先执行参数里的,再执行外层的
// double(addOne(3)) → double(4) → 8
Function<Integer, Integer> composed = doubleValue.compose(addOne);
composed.apply(3);  // 8

什么不算抽象方法

一个接口里可能有其他方法,但它们不影响函数式接口的判定:

java
@FunctionalInterface
interface EnhancedComparator<T> {
    int compare(T o1, T o2);

    // default 方法不算——它有默认实现
    default boolean equals(Object obj) {
        return false;
    }
}
// 这仍然是函数式接口,因为只有一条抽象方法

小结

接口核心方法用途
Consumer&lt;T&gt;void accept(T)执行操作,不返回值
Supplier&lt;T&gt;T get()生产一个值
Function&lt;T,R&gt;R apply(T)转换
Predicate&lt;T&gt;boolean test(T)判断
UnaryOperator&lt;T&gt;T apply(T)一元运算
BinaryOperator&lt;T&gt;T apply(T, T)二元运算

优先使用 JDK 内置的——它们已经覆盖了 90% 的场景。只有在业务逻辑复杂、内置接口无法清晰表达意图时,才自定义函数式接口。

基于 VitePress 构建