我想了解隐藏在Java中是如何工作的。 所以我们假设你有以下代码
public class A{
protected SomeClass member;
public A(SomeClass member){
this.member = member;
}
}
public class B extends A{
protected SomeClass member;
public B(SomeClass member){
super(member);
}
public static void main(String[] args){
SomeClass sc = new SomeClass();
B b = new B(sc);
System.out.println(b.member.toString());
}
}
如果我编译我很荣幸有NullPointerException。我以为这将是sc.toString();
的输出我将此代码更改为
public class A{
protected SomeClass member;
public A(SomeClass member){
setMember(member);
}
public void setMember(SomeClass sc){
this.member = sc;
}
}
public class B extends A{
protected SomeClass member;
public B(SomeClass member){
super(member);
}
public void setMember(SomeClass sc){
this.member = sc;
}
//...main
}
并按预期获得输出... ok setMember of B覆盖A中的那个,所以我可以这样解释。 我玩了一下,从B中删除了setMember,得到了我的NullPointerException。但是如果我将A的代码改为
,它会再次编译并给出输出public class A{
protected SomeClass member;
public A(SomeClass member){
setMember(member);
}
public void setMember(SomeClass sc){
member = sc;
}
}
在我看来,实际上有两个SomeClass成员实例......但是如果有两个实例则意味着什么?隐藏只对第二种情况有用吗?
答案 0 :(得分:3)
我假设您的第一个代码示例的最后一行是b.member.toString()
。
有两个名为“member”的成员变量,在两个示例中,只设置了其中一个变量,因为只调用了一个this.member
赋值。要修复第一个例子,你通常会说
public B(SomeClass member) {
super(member);
this.member = member;
}
但我认为你已经理解了这一点,并且真的在问为什么语言是这样设计的。它与超类实现的封装有关。超类的作者应该能够在不破坏子类的情况下重写它,反之亦然。想象一下,如果B.member
首先出现是因为B的作者虽然是“成员”会是一件好事,后来A的作者也有同样的想法。
然而,这个系统并不完美,你的第二个例子说明了原因。如果B.setMember()
首先出现,然后更高版本的A引入A.setMember()
,那么A的作者可能无法预测覆盖B.setMember()
方法,并按照您显示的方式编写构造函数, A.member
永远不会被初始化的结果。 C#引入了“覆盖”关键字来捕获这种东西,Java将其作为“@overrides”借用,但是这个注释在Java中并不是疯狂的,所以它不那么有效。