java函数式接口 @FunctionalInterface用法

1. 什么是 @FunctionalInterface?

@FunctionalInterface 是一个标记注解 (Marker Annotation),用于指示一个接口是函数式接口(Functional Interface)。它是 Java 8 引入的,与 Lambda 表达式紧密相关。

2. 什么是函数式接口?

函数式接口是一个有且仅有一个抽象方法的接口(但可以有多个默认方法或静态方法)。它代表了一种单一功能的契约,是 Lambda 表达式和方法引用的目标类型。

例如,Java 标准库中的:

  • Runnable (抽象方法: run())
  • Callable<V> (抽象方法: call())
  • Comparator<T> (抽象方法: compare(T o1, T o2))

3. @FunctionalInterface 的作用

这个注解主要有两个目的:

  1. 编译器检查 :

    当你在一个接口上使用了 @FunctionalInterface,编译器会强制检查该接口是否确实只有一个抽象方法。如果不是,编译器会报错。这有助于防止意外地添加第二个抽象方法,从而破坏其作为函数式接口的用途。

  2. 文档化 :

    它明确地告诉代码的阅读者:"这个接口被设计为函数式接口,目的是为了与 Lambda 表达式一起使用"。

4. 基本语法

java 复制代码
@FunctionalInterface
interface MyFunctionalInterface {
    // 单个抽象方法
    void myMethod(String msg);

    // 允许有默认方法
    default void defaultMethod() {
        System.out.println("This is a default method.");
    }

    // 允许有静态方法
    static void staticMethod() {
        System.out.println("This is a static method.");
    }

    // 允许覆盖 java.lang.Object 中的方法 (这不算是抽象方法)
    @Override
    boolean equals(Object obj);
}

关键规则:

  • 必须只有一个抽象方法。
  • 可以有任意数量的 default 方法。
  • 可以有任意数量的 static 方法。
  • 覆盖 Object 类的方法(如 toString(), equals(), hashCode())不计入抽象方法的数量。

5. 使用示例

1. 定义函数式接口:

java 复制代码
@FunctionalInterface
interface Greeting {
    void sayHello(String name);
    // void anotherMethod(); // 如果取消注释,编译器会报错
}

2. 使用 Lambda 表达式实现:

java 复制代码
public class Main {
    public static void main(String[] args) {
        // 使用 Lambda 表达式实现 Greeting 接口
        Greeting greet = (name) -> {
            System.out.println("Hello, " + name);
        };

        // 调用方法
        greet.sayHello("Alice"); // 输出: Hello, Alice

        // 更简洁的写法
        Greeting greet2 = name -> System.out.println("Hi there, " + name);
        greet2.sayHello("Bob"); // 输出: Hi there, Bob
    }
}

3. 作为方法参数传递(非常常见):

Lambda 表达式的强大之处在于可以像数据一样传递行为。

java 复制代码
import java.util.Arrays;
import java.util.List;

public class Main {
    // 一个接受函数式接口作为参数的方法
    public static void processNames(List<String> names, Greeting greeting) {
        for (String name : names) {
            greeting.sayHello(name);
        }
    }

    public static void main(String[] args) {
        List<String> nameList = Arrays.asList("Alice", "Bob", "Charlie");

        // 传递不同的 Lambda 实现来表达不同的"问候"行为
        System.out.println("Formal greeting:");
        processNames(nameList, name -> System.out.println("Good day, " + name));

        System.out.println("\nCasual greeting:");
        processNames(nameList, name -> System.out.println("Hey, " + name + "!"));
    }
}

6. 总结

特性 说明
目的 定义并强制检查一个接口为函数式接口,以便与 Lambda 表达式协同工作。
强制规则 接口必须有且仅有一个抽象方法。
灵活性 允许存在多个 default 方法和 static 方法。
最佳实践 如果你设计的接口明确希望被用作 Lambda 表达式的目标,就应该加上 @FunctionalInterface 注解。即使不加,只要符合单一抽象方法的规则,它依然是函数式接口,但加上注解可以让意图更清晰并获得编译器的保护。

简单来说,@FunctionalInterface 是 Java 为支持函数式编程范式而提供的一个关键工具,它让使用 Lambda 表达式变得安全、清晰和高效。

相关推荐
aiks_yuan1 小时前
Java CPU 100% 怎么排查:JDK 11 起 jstack 自带线程 CPU 时间,不用再 top -H 换算十六进制(JDK 8、11、17 实测)
java
Flynn Liang2 小时前
AiServices 源码级拆解:一个接口如何变成 RAG Agent
java·langchain·源码分析·rag·langchain4j·aiservices
Flynn Liang2 小时前
LangChain4j 1.23 架构全景:Java 大模型应用的 6 层抽象
java·langchain·大模型·rag·ai agent·langchain4j
聪明的笨猪猪3 小时前
实际工程问题:AI生成Redis分布式锁的原子性缺陷导致库存超卖
java·经验分享·面试·ai编程
软件技术NINI4 小时前
基于SpringBoot和Vue的新能源汽车销售推荐系统
java·vue.js·spring boot·后端·汽车·毕业设计·毕设
Flynn Liang4 小时前
LangChain4j 生产级工程骨架:12 个模块 + Docker 一键启动
java·spring boot·langchain·rag·langchain4j
噢,我明白了5 小时前
JAVA中的一些基础应用知识点
java·开发语言
SL_staff6 小时前
私有化低代码不是妥协,而是企业级数字化的自主可控主航道
java
装不满的克莱因瓶6 小时前
【SpringAI Alibaba】ReactAgent结构化输出OutputType与OutputSchema实战
java·人工智能·ai·springai·springaialibaba
秋天的怀念_David7 小时前
算法一千零一夜 · 第二十八夜 注册
java·程序人生·算法