函数式接口的本质是仅含一个抽象方法的接口,用于定义单一行为契约;它可含默认、静态及Object继承方法,@FunctionalInterface注解用于编译校验与可读性提升。

函数式接口的本质,就是“一个动作的契约”——它不关心你是怎么实现的,只明确约定:你要提供一种特定类型的计算、判断或执行行为。而 SAM(Single Abstract Method)这个概念,正是抓住了这个本质的核心:接口里必须且只能有一个待实现的抽象方法。
什么是 SAM?不是“只有一个方法”,而是“只有一个待重写的抽象方法”
很多人初学时误以为“函数式接口只能写一个方法”,其实不然。关键在“抽象”二字:
- 抽象方法:没有方法体,子类或实现类必须重写(如
apply(T t)、test(T t)); - 默认方法(
default):有实现体,可直接用,也可选择覆盖; - 静态方法(
static):属于接口本身,通过接口名调用,不参与实例行为; -
Object中的toString()、equals()、hashCode()等 public 抽象方法不算数——所有接口天然继承自Object,这些方法已有默认语义,不构成对“单一行为”的干扰。
@FunctionalInterface 注解的作用是“提醒+校验”
它不是必需的,但强烈建议加上:
- 加了之后,编译器会检查:如果接口意外多出第二个抽象方法,立即报错,避免隐性错误;
- Javadoc 会自动标注该接口为函数式接口,提升可读性和团队协作清晰度;
- 它不改变运行行为,只是开发阶段的“安全带”。
为什么 Lambda 只能赋给 SAM 接口?
Lambda 表达式本质上是一个匿名函数实现。Java 需要明确知道:“你这个箭头代码,到底是在实现接口里的哪个方法?”
立即学习“Java免费学习笔记(深入)”;
例如:
Runnable r = () -> System.out.println("hello");
这里编译器一看 Runnable 只有一个抽象方法 run(),就自然把 () -> ... 绑定过去。如果接口有两个抽象方法,Java 就无法确定 Lambda 对应哪一个,语法上就不允许。
常见误区澄清
- “
Comparator有两个抽象方法?”——错。它只有compare(T o1, T o2)是抽象的;equals()来自Object,不算; - “我加了
default方法,就不是函数式接口了?”——不。默认方法越多,接口越实用,只要抽象方法仍唯一; - “自己定义的接口没加
@FunctionalInterface,就不能用 Lambda?”——可以,只要满足 SAM 规则,只是失去编译期保护。


















