
本文解析当使用 instance::method 引用类中重写的方法来实现含默认方法的函数式接口时,为何实际调用的是接口默认实现而非类中重写版本,并通过代码对比阐明方法引用的目标绑定机制与动态分派规则。
本文解析当使用 `instance::method` 引用类中重写的方法来实现含默认方法的函数式接口时,为何实际调用的是接口默认实现而非类中重写版本,并通过代码对比阐明方法引用的目标绑定机制与动态分派规则。
在 Java 8+ 中,方法引用(如 a::myTest)用于简洁地实现函数式接口。但其行为常被误解——方法引用绑定的不是接口中同名方法,而是函数式接口唯一的抽象方法(即 SAM)。这一点是理解本问题的关键。
回顾示例代码:
@FunctionalInterface
interface MyIF {
void init(); // ← 唯一抽象方法(SAM)
default void myTest() {
System.out.println("myTest interface Method");
}
}
class A implements MyIF {
@Override
public void init() {
myTest(); // 调用的是 this.myTest() → 动态分派到 A.myTest()
}
@Override
public void myTest() {
System.out.println("myTest class Method");
}
}当执行:
A a = new A(); MyIF my = a::myTest; // ✅ 绑定到 init(),而非 myTest() my.myTest(); // → 输出 "myTest interface Method"
此处 a::myTest 并非“将 myTest 方法赋给 myTest 字段”,而是:
? 编译器识别 MyIF 是函数式接口,其唯一未实现的抽象方法是 init();
? 因此 a::myTest 被自动适配为 init() 的实现,等价于:
MyIF my = new MyIF() {
@Override
public void init() {
a.myTest(); // ← 显式调用 a 实例的 myTest()
}
// myTest() 仍由接口默认提供,未被覆盖
};此时调用 my.myTest(),由于 my 是 MyIF 的匿名实现(非 A 类型),且该实现未重写 myTest(),故 JVM 直接执行接口默认方法 MyIF.myTest()。
立即学习“Java免费学习笔记(深入)”;
而对比场景:
MyIF my2 = new A(); // my2 是 A 的实例,类型为 A & MyIF my2.myTest(); // → 输出 "myTest class Method"
此时 my2 是 A 的实例,A 显式重写了 myTest(),根据 Java 的动态方法分派规则(runtime dispatch),myTest() 调用会绑定到 A.myTest()。
✅ 正确理解要点:
- 方法引用 obj::method 总是绑定到函数式接口的 SAM(Single Abstract Method),与方法名是否匹配无关;
- 接口默认方法仅在实现类未提供具体实现时生效;方法引用生成的实现类若未覆盖该默认方法,则默认实现保留;
- 若希望调用子类重写的 myTest(),应直接使用实例(如 new A()),或显式创建覆盖 myTest() 的实现。
? 补充验证:若想让方法引用“真正调用 A.myTest()”,可改写为:
MyIF my3 = new A() {
@Override
public void myTest() {
super.myTest(); // 或 this.myTest() —— 触发动态分派
}
};
my3.myTest(); // 输出 "myTest class Method"总结:方法引用的本质是 SAM 实现的语法糖,而非“方法别名”。开发者需始终明确:引用的目标是哪个抽象方法?该实现类是否覆盖了其他接口方法? 忽略此逻辑将导致意外的默认方法调用行为。










