在 Java 中,当子类同时继承抽象类并实现包含默认方法的接口,且两者定义了同名方法时,可通过 super.m1() 调用父类实现,而必须显式使用 InterfaceName.super.m1() 调用接口默认方法——这一差异源于单继承与多实现语义的根本区别。
先说一个常见场景:开发团队经常遇到一个类既要继承抽象类,又要实现某个接口,而这两个“长辈”恰好定义了同名方法。此时,如何分别调用它们?答案其实很清晰——父类的方法用 super.m1(),接口的默认方法必须用 InterfaceName.super.m1()。这背后,反映了 Java 单继承与多实现设计哲学的核心差异。
在 Java 继承体系中,一个类只能有一个直接父类,因此 super.m1() 天然指向唯一的父类,无需额外指定。但接口不同——一个类可以实现多个包含默认方法的接口,如果仅用 super.m1(),编译器无法确定要调用哪个接口的默认方法。于是语法强制要求通过 InterfaceName.super.m1() 明确指定目标接口,彻底消除歧义。
下面是一个规范且可直接运行的示例,一目了然:
interface C {
default void m1() {
System.out.println("C (interface default)");
}
}
abstract class A {
abstract void display();
void m1() {
System.out.println("A (abstract class method)");
}
}
class B extends A implements C {
@Override
void display() {
// ✅ 正确:调用父类 A 的 m1()
super.m1(); // 输出: A (abstract class method)
// ✅ 正确:显式调用接口 C 的默认方法
C.super.m1(); // 输出: C (interface default)
// ❌ 错误写法(编译失败):
// A.super.m1(); // 编译错误:"not an enclosing class"
// 因为 A 是父类而非外围类(enclosing class),不能用 A.super
}
@Override
void m1() {
// 若需重写 m1 并组合两者逻辑,推荐在此处统一调用
super.m1(); // 父类实现
C.super.m1(); // 接口默认实现
}
}
这里有几点需要重点掌握:
super.m1()始终指向直接父类(A)的实例方法,无论该方法是否被子类重写,它都会沿着父类链向上查找。InterfaceName.super.m1()是调用接口默认方法的唯一合法语法,接口名称不可省略——这是强制规则。ClassName.super.m1()这种写法仅在内部类访问外部类同名方法时有效(即“enclosing class”场景),普通继承关系中无法使用。这也是上述代码中A.super.m1()报错“not an enclosing class: A”的原因。- 如果父类的方法是 private 或 static,那么
super也无法调用——super只对可继承的非静态实例成员起作用。
一句话总结:Java 通过语法层面的强制区分,将单继承的确定性(super)与多接口的二义性(Interface.super)彻底分开。理解这一机制后,编写多层抽象结构时才能做到逻辑清晰、易于维护,避免踩坑。
