Skip to content

@FunctionalInterface 函数式接口详解

@FunctionalInterface 是 Java 8 引入的注解,用于标记只有一个抽象方法的接口。这种接口可以直接用 Lambda 表达式方法引用来实现,是函数式编程的核心特性。


核心特性

  1. 单抽象方法
    接口中只能有一个抽象方法(但可以有默认方法或静态方法)。
  2. Lambda 支持
    可以用 Lambda 表达式快速实现接口。
  3. 编译检查
    添加 @FunctionalInterface 后,编译器会强制检查是否满足函数式接口的条件。

经典案例

1. Java 内置的函数式接口

java
// Runnable(无参数、无返回值)
Runnable task = () -> System.out.println("Hello, Lambda!");
task.run();

// Predicate<T>(输入T,返回boolean)
Predicate<String> isEmpty = s -> s.isEmpty();
System.out.println(isEmpty.test("")); // true

// Function<T, R>(输入T,返回R)
Function<String, Integer> strToLength = String::length;
System.out.println(strToLength.apply("Java")); // 4
// Runnable(无参数、无返回值)
Runnable task = () -> System.out.println("Hello, Lambda!");
task.run();

// Predicate<T>(输入T,返回boolean)
Predicate<String> isEmpty = s -> s.isEmpty();
System.out.println(isEmpty.test("")); // true

// Function<T, R>(输入T,返回R)
Function<String, Integer> strToLength = String::length;
System.out.println(strToLength.apply("Java")); // 4

2. 自定义函数式接口

java
@FunctionalInterface
interface Calculator {
    int calculate(int a, int b); // 唯一抽象方法

    // 默认方法(不影响函数式接口性质)
    default void log() {
        System.out.println("Calculating...");
    }
}

public class Main {
    public static void main(String[] args) {
        // 用Lambda实现
        Calculator add = (a, b) -> a + b;
        System.out.println(add.calculate(3, 5)); // 8

        // 用方法引用实现(假设有静态方法)
        Calculator multiply = Main::staticMultiply;
        System.out.println(multiply.calculate(3, 5)); // 15
    }

    static int staticMultiply(int a, int b) {
        return a * b;
    }
}
@FunctionalInterface
interface Calculator {
    int calculate(int a, int b); // 唯一抽象方法

    // 默认方法(不影响函数式接口性质)
    default void log() {
        System.out.println("Calculating...");
    }
}

public class Main {
    public static void main(String[] args) {
        // 用Lambda实现
        Calculator add = (a, b) -> a + b;
        System.out.println(add.calculate(3, 5)); // 8

        // 用方法引用实现(假设有静态方法)
        Calculator multiply = Main::staticMultiply;
        System.out.println(multiply.calculate(3, 5)); // 15
    }

    static int staticMultiply(int a, int b) {
        return a * b;
    }
}

实际应用场景

1. 简化回调逻辑

java
@FunctionalInterface
interface Callback {
    void onComplete(String result);
}

void fetchData(Callback callback) {
    // 模拟异步操作
    new Thread(() -> {
        String data = "Data fetched";
        callback.onComplete(data);
    }).start();
}

// 调用
fetchData(result -> System.out.println("Result: " + result));
@FunctionalInterface
interface Callback {
    void onComplete(String result);
}

void fetchData(Callback callback) {
    // 模拟异步操作
    new Thread(() -> {
        String data = "Data fetched";
        callback.onComplete(data);
    }).start();
}

// 调用
fetchData(result -> System.out.println("Result: " + result));

2. 策略模式

java
@FunctionalInterface
interface PaymentStrategy {
    void pay(double amount);
}

class PaymentService {
    private PaymentStrategy strategy;

    public void setStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void executePayment(double amount) {
        strategy.pay(amount);
    }
}

// 使用
PaymentService service = new PaymentService();
service.setStrategy(amount -> System.out.println("Paid via Credit Card: $" + amount));
service.executePayment(100.0);
@FunctionalInterface
interface PaymentStrategy {
    void pay(double amount);
}

class PaymentService {
    private PaymentStrategy strategy;

    public void setStrategy(PaymentStrategy strategy) {
        this.strategy = strategy;
    }

    public void executePayment(double amount) {
        strategy.pay(amount);
    }
}

// 使用
PaymentService service = new PaymentService();
service.setStrategy(amount -> System.out.println("Paid via Credit Card: $" + amount));
service.executePayment(100.0);

常见问题

  1. 为什么要有 @FunctionalInterface 注解?

    • 明确告知开发者该接口的用途(如 Runnable 一看就知道是任务执行)。
    • 编译器会检查是否真的只有一个抽象方法。
  2. 没有这个注解的接口能用 Lambda 吗?

    • 可以!只要实际满足函数式接口的条件(单一抽象方法),但加上注解更规范。
  3. 抽象方法能是 Object 类中的方法吗?

    • 不算!例如 equals()Object 的方法,不会影响函数式接口的性质。
    java
    @FunctionalInterface
    interface Example {
        void doSomething();
        boolean equals(Object obj); // 不计数
    }
    @FunctionalInterface
    interface Example {
        void doSomething();
        boolean equals(Object obj); // 不计数
    }

总结

  • 本质:函数式接口是 Lambda 的“目标类型”。
  • 用途:简化代码(替代匿名类)、实现策略模式、事件回调等。
  • 关键点:记住 单一抽象方法 规则,其他都是锦上添花。