隐藏类的实例变量

时间:2011-10-17 13:40:35

标签: java oop

我想知道为什么Java对于超类和具有相同名称的实例变量的子类有这种奇怪的行为。

假设我们有以下类定义:

class Parent {
    int var = 1;
}

class Child extends Parent {
    int var = 2;
}

通过这样做,我们应该隐藏超类的变量var。如果我们没有明确指定通过Parent调用访问var的{​​{1}}的方法,那么我们永远不能从一个实例访问super子。

但是当我们有一个演员表时,这个隐藏机制会破坏:

var

这不完全绕过整个隐藏点吗?如果是这种情况,那么这不会使这个想法完全没用吗?

编辑:我指的是Java教程中的this article。它提到

  

在子类中,超类中的字段不能被引用   以其简单的名称。相反,字段必须才能通过super ...

进行访问

从我在那里读到的内容,似乎暗示Java的开发人员在这方面考虑了某种技术。虽然我同意这是一个相当模糊的概念,但一般来说可能是不好的做法。

6 个答案:

答案 0 :(得分:19)

在Java中,数据成员不是多态的。这意味着Parent.varChild.var是碰巧具有相同名称的两个不同变量。你在派生类中没有任何意义上“覆盖”var;正如您自己发现的那样,两个变量可以彼此独立地访问。

前进的最佳方式取决于你想要达到的目标:

  1. 如果Parent.var无法看到Child,请将其设为private
  2. 如果Parent.varChild.var是两个逻辑上不同的变量,请为它们指定不同的名称以避免混淆。
  3. 如果Parent.varChild.var在逻辑上是同一个变量,那么请为它们使用一个数据成员。

答案 1 :(得分:6)

字段隐藏的“要点”仅仅是指定代码的行为, 为变量提供与其超类中的变量相同的名称。

它并不意味着用作技术来真正隐藏信息。这是通过将变量设为私有来完成的......我强烈建议在几乎所有情况下都使用私有变量。字段是一个实现细节,应该对所有其他代码隐藏。

答案 2 :(得分:2)

Java中的属性不是多态的,无论如何声明公共属性并不总是一个好主意。对于您正在寻找的行为,最好使用私有属性和访问器方法,如下所示:

class Parent {

    private int var = 1;

    public int getVar() {
        return var;
    }

    public void setVar(int var) {
        this.var = var;
    }

}

class Child extends Parent {

    private int var = 2;

    public int getVar() {
        return var;
    }

    public void setVar(int var) {
        this.var = var;
    }

}

现在,在测试时,我们得到了理想的结果,2:

Child child = new Child();
Parent parent = (Parent)child;
System.out.println(parent.getVar());

答案 3 :(得分:2)

此场景称为变量隐藏,当子类和父类都具有相同名称的变量时,子类的变量隐藏父类的变量,此过程称为变量隐藏。

在Java中,变量不是多态的,变量隐藏与方法覆盖不一样

虽然变量隐藏看起来像覆盖一个类似于方法重写的变量,但事实并非如此,Overriding仅适用于隐藏的方法是适用的变量。

在方法覆盖的情况下,重写方法完全替换了继承的方法,因此当我们尝试通过保存子对象从父级引用访问该方法时,将调用子类中的方法。

但是在变量隐藏中,子类隐藏了继承的变量而不是替换,因此当我们尝试通过保存子对象从父级引用访问变量时,它将从父类访问。

public static void main(String[] args) throws Exception {

    Parent parent = new Parent();
    parent.printInstanceVariable(); // Output - "Parent`s Instance Variable"
    System.out.println(parent.x); // Output - "Parent`s Instance Variable"

    Child child = new Child();
    child.printInstanceVariable();// Output - "Child`s Instance Variable, Parent`s Instance Variable"
    System.out.println(child.x);// Output - "Child`s Instance Variable"

    parent = child; // Or parent = new Child();
    parent.printInstanceVariable();// Output - "Child`s Instance Variable, Parent`s Instance Variable"
    System.out.println(parent.x);// Output - Parent`s Instance Variable

    // Accessing child's variable from parent's reference by type casting
    System.out.println(((Child) parent).x);// Output - "Child`s Instance Variable"
}

正如我们在上面看到的,当子类中的实例变量与超类中的实例变量具有相同的名称时,则从引用类型中选择实例变量。

在子级和父级中声明具有相同名称的变量会造成混淆,我们应该始终避免它,这样就不会产生混淆。这就是为什么我们也应该始终坚持General Guidelines to create POJOs并使用私有访问声明我们的变量,并提供适当的get / set方法来访问它们。

您可以在我的文章What is Variable Shadowing and Hiding in Java上阅读更多内容。

答案 4 :(得分:0)

当你进行投射时,你会有效地告诉编译器“我知道更好” - 它会暂停正常的强类型推理规则并给你带来疑问。

通过说Parent parent = (Parent)child;,你告诉编译器“把这个对象视为父的一个实例”。

另一方面,你将OO(好!)的“信息隐藏”原则与字段隐藏副作用(通常是坏的)混淆。

答案 5 :(得分:0)

正如你所指出的那样:

  

我们应该隐藏超类的变量var

此处的主要观点是变量覆盖方法,因此当您直接调用 Child.var 时,您正在调用变量直接来自Child类,当你调用 Parent.var 时,你正在调用Parent类中的变量,无论它们是否具有相同的名称。

作为旁注,我会说这实在令人困惑,不应该被允许作为有效的语法。