Java方法重写特殊场景疑问:super调用为何触发子类方法?
这是个非常经典的Java多态机制问题,我来给你拆解清楚前因后果,顺便解决你的后续需求。
先把你的代码贴出来方便大家参考:
Foo类
public class Foo { public Foo() {} public String who() { return "I'm foo "; } public String info() { return who() + "and i provide foo infos"; } public static void main(String[] args) { Foo bar = new Bar(); System.out.println(bar.info()); } }
Bar类
public class Bar extends Foo { public Bar() { super(); } public String who(){ return "I'm bar "; } public String info(){ return super.info() + " about x"; } }
你的预期输出是"I'm foo and i provide foo infos about x",但实际得到的是"I'm bar and i provide foo infos about x",核心问题出在Java的动态绑定规则和super关键字的作用范围上。
首先搞懂:super到底做了什么?
你以为super.info()会让整个父类方法的执行都“局限在父类对象里”,但其实不是——super只是帮你调用父类版本的info()方法,但执行这个方法的时候,当前对象的实际类型依然是Bar(因为你new的是Bar实例)。
Java里所有非private、非final、非static的实例方法,都是动态绑定的:调用哪个版本的方法,由对象的运行时实际类型决定,而不是当前代码所在的类。
所以当父类Foo的info()方法里调用who()时,JVM会检查当前对象是Bar,而Bar重写了who(),自然就调用了子类的who()方法,这就是为什么输出里会出现"I'm bar"。
那怎么实现调用父类的who()?
当然可行,给你几个实用方案:
方案1:把父类的who()设为private或final
如果父类的who()不需要被子类重写,直接用private或者final修饰:
public class Foo { // private方法不会被重写,也不参与动态绑定 private String who() { return "I'm foo "; } public String info() { return who() + "and i provide foo infos"; } }
这样Foo的info()里调用的who()永远是自己的版本,完全不受子类重写影响。
方案2:在父类info()里强制调用自身的who()逻辑
如果需要保留who()的可重写性,但想让info()固定用父类的who()内容,可以把父类的who()逻辑封装成一个private方法:
public class Foo { public String who() { return "I'm foo "; } public String info() { // 调用仅父类可见的方法,避免动态绑定影响 return getFooWho() + "and i provide foo infos"; } private String getFooWho() { return super.who(); // Foo的父类是Object,所以super.who()就是Foo自己的who() } }
方案3:在子类info()里手动拼接父类的who()
如果不想修改父类,也可以在子类的info()里直接调用super.who()来获取父类的内容,再拼接:
public class Bar extends Foo { public String who(){ return "I'm bar "; } public String info(){ return super.who() + "and i provide foo infos about x"; } }
这样就能得到你预期的输出"I'm foo and i provide foo infos about x"了。
内容的提问来源于stack exchange,提问作者Jacques Martin

