Java继承中子类同名属性获取异常:为何返回父类值及解决方法?
这是个很典型的Java字段隐藏问题,我来给你拆解一下原因和解决方案:
问题原因
Java里和方法不一样,成员字段是不支持重写的,只能被"隐藏"。当你在子类B里定义了和父类A同名的invocations字段时,子类的这个字段并没有覆盖掉父类的版本,只是把它"藏"起来了。
这里的关键是:Java对字段的访问是编译期静态绑定的——编译器会根据你用来访问字段的引用类型来决定用哪个类的字段,而不是看实际运行时对象的类型。举个例子,如果你的代码是这么写的:
A myObj = new B(); System.out.println(myObj.invocations); // 这里会输出["foo"]
因为myObj的引用类型是A,编译器就会绑定到A类的invocations字段,哪怕实际对象是B类型的。
解决方案
想要拿到B类里的{"bar", "baz"},有几种靠谱的方式:
1. 直接用子类类型的引用访问
如果你的代码里直接用B类型的变量来持有实例,访问字段时自然会拿到子类的版本:
B myObj = new B(); System.out.println(Arrays.toString(myObj.invocations)); // 输出 [bar, baz]
2. 改用Getter方法(推荐的面向对象写法)
直接暴露字段不是好的设计,更规范的做法是用方法来返回字段值——因为方法支持重写,是运行期动态绑定的,能自动适配实际对象的类型。
修改父类A:
public class A { protected String invocations[] = {"foo"}; public A() { // do stuff } // 添加Getter方法 public String[] getInvocations() { return invocations; } }
修改子类B,重写Getter方法:
public class B extends A { protected String invocations[] = {"bar", "baz"}; public B() { super(); } @Override public String[] getInvocations() { return invocations; } }
现在不管你用父类还是子类引用,都能拿到正确的结果:
A myObj = new B(); System.out.println(Arrays.toString(myObj.getInvocations())); // 输出 [bar, baz]
3. 向下转型(不推荐,尽量避免)
如果已经有了父类类型的引用,可以先判断类型再向下转型,然后访问子类字段,但这种写法不够安全,万一对象不是B类型会抛出ClassCastException:
A myObj = new B(); if (myObj instanceof B) { System.out.println(Arrays.toString(((B) myObj).invocations)); // 输出 [bar, baz] }
内容的提问来源于stack exchange,提问作者BoxTechy
相关产品推荐
相关产品推荐

