接口本质是编译期类型契约,用于声明能力、解耦实现、支撑多态扩展;它描述“谁可以做什么”,而非功能集合,不规定实现细节,强调 can-do 关系与无状态协作。
接口在 Java 里不是为了“定义一组方法”而存在,而是为了**声明契约、解耦实现、支撑多态扩展**——它本质是编译期的类型协议,不是运行时的模板或工具类。
接口是类型契约,不是功能集合
很多人把 interface 当成“功能清单”,比如写个 Logger 接口就堆 logInfo()、logError()、logDebug(),结果一换日志框架就得改接口。这违背了接口本意。
- 接口应描述“谁可以做什么”,而不是“能做哪些事”。例如
Runnable不关心线程怎么调度,只承诺“我能被Thread.start()调用” - 一个类实现多个接口,是因为它同时满足多个独立契约(如
Serializable & Comparable & Cloneable),而非为了凑齐一堆方法 - 接口方法默认是
public abstract,没有实现细节——这是刻意为之:契约不规定怎么做,只规定能不能做、怎么调
接口支撑编译期多态,替代继承传递能力
Java 不支持多继承,但允许实现多个接口。这不是语法妥协,而是设计选择:能力(behavior)应该可叠加,但身份(identity)必须唯一。
- 用
extends表达 is-a 关系(Dog extends Animal),用implements表达 can-do 关系(Dog implements Swimmable, Barkable) - 依赖接口编程(如方法参数用
List而非ArrayList),才能让调用方完全不感知实现类——这才是Collection.sort()能对ArrayList、LinkedList、甚至自定义MyList统一工作的基础 - 接口的默认方法(
default)和静态方法是后来加的补丁,用于向后兼容;它们不能破坏契约性——比如Collection.removeIf()是基于已有iterator()的封装,不是新增能力承诺
接口与抽象类的关键分界点
两者都能定义抽象行为,但语义完全不同。选错会锁死演化路径。