1

我想了解隐藏在 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
}

它得到了预期的输出......好的 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 成员有两个实例......但是如果有两个实例,那么阴影意味着什么?隐藏仅对第二种情况有用吗?

4

1 回答 1

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# 引入了“overrides”关键字来捕捉这类事情,Java 将其借用为“@overrides”,但该注解在 Java 中并不是强制性的,因此它没有那么有效。

于 2011-06-24T21:26:22.477 回答