接口默认方法与静态方法
Java 8 之前,接口只能有抽象方法,不能有实现。所有的实现都要靠实现类来完成。
但这样有个问题:如果要给接口加一个新方法,所有实现类都要跟着改。在 JDK 8 之前,List 接口加 sort 方法,Java 团队不可能去修改所有实现了 List 的类。
所以他们引入了默认方法。今天我们就来彻底搞懂这个特性。
一、为什么需要默认方法
1.1 JDK 8 之前的问题
如果 JDK 要给 List 加一个新方法,比如 sort,所有实现类都要改代码。
1.2 JDK 8 的解决方案:默认方法
1.3 default 方法的作用
- 接口演进:给接口加新方法不用修改所有实现类
- 代码复用:提供通用实现,子类可以复用
- Lambda 支持:
forEach、stream等方法都是默认方法
二、默认方法的语法
2.1 基本语法
2.2 实现类使用默认方法
三、静态方法
3.1 接口静态方法
3.2 为什么需要接口静态方法
以前 utility 方法放在工具类里:
3.3 静态方法 vs 默认方法
四、菱形继承问题
4.1 多重继承的冲突
4.2 解决规则
解决优先级:类 > 子接口 > 父接口
4.3 显式指定调用父接口
五、【直观类比】
【直观类比】
默认方法就像给接口提供了一个"模板":
菱形继承问题就像两个爸爸都教了你同一句话,你得自己决定听谁的。
六、生产避坑
6.1 ❌ 错误示范:把业务逻辑放在默认方法里
更好的做法:默认方法只放简单的、通用的逻辑。
6.2 ❌ 错误示范:在默认方法里使用未实现的抽象方法
6.3 ❌ 错误示范:默认方法中使用 this 导致歧义
七、接口默认方法的应用
7.1 List.forEach 的实现
7.2 Comparator 的默认方法
7.3 Collection 的默认方法
八、面试追问链
第一层:基础概念
面试官问:"接口的默认方法是什么?"
用 default 关键字修饰的方法,可以有实现。实现类可以直接使用,也可以覆盖。目的是让接口可以演进,不用修改所有实现类。
第二层:菱形继承
面试官追问:"如果两个接口都有同名的默认方法,实现类会怎么样?"
会编译错误。实现类必须显式覆盖这个方法,并通过 InterfaceName.super.hello() 指定调用哪个父接口的实现。
第三层:设计意图
面试官追问:"为什么要引入默认方法?"
主要是为了 JDK 8 的 Lambda 支持。比如给 Iterable 加 forEach、给 Collection 加 stream 等。如果不用默认方法,所有实现类都要修改,会破坏兼容性。
【学习小结】
- 默认方法用
default关键字修饰,可以有实现 - 实现类可以直接使用,也可以覆盖
- 静态方法用
static关键字修饰,通过InterfaceName.method()调用 - 菱形继承时,实现类必须显式指定调用哪个父接口的默认方法
- 解决优先级:类 > 子接口 > 父接口