接口本质是编译期类型契约,用于声明能力、解耦实现、支撑多态扩展;它描述“谁可以做什么”,而非功能集合,不规定实现细节,强调 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()的封装,不是新增能力承诺
接口与抽象类的关键分界点
两者都能定义抽象行为,但语义完全不同。选错会锁死演化路径。
立即学习“Java免费学习笔记(深入)”;
- 抽象类表达“共性骨架 + 可选定制”,适合有状态、有构造逻辑、需共享代码的场景(如
AbstractMap封装了size()、isEmpty()的通用实现) - 接口表达“能力声明 + 无状态协作”,适合跨层级、跨领域的能力组合(如
AutoCloseable可被FileInputStream、ZipOutputStream、甚至数据库连接实现,彼此毫无继承关系) - Java 8 后接口可有
default方法,但不能有实例字段——这意味着它依然无法承载“对象状态”,一旦需要保存lastModifiedTime或retryCount,就必须退回到抽象类或委托模式
真正难的不是写接口,是在需求还没稳定时判断:这个“共同点”是暂时的实现巧合,还是长期稳定的协作契约。过早抽取接口,会导致大量空实现或强制实现无关方法;过晚抽取,则各处硬编码 if (obj instanceof Xxx),污染调用逻辑。契约的生命力,取决于它是否经得起“换掉全部实现类,调用方代码仍无需修改”的检验。










