繁体   English   中英

隐藏类的实例变量

[英]Hiding instance variables of a class

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

假设我们有以下类定义:

class Parent {
    int var = 1;
}

class Child extends Parent {
    int var = 2;
}

通过这样做,我们应该隐藏了超类的变量var 如果我们没有明确指定通过super调用访问Parentvar的方法,那么我们将永远无法从子实例访问var

但是当我们有一个演员表时,这种隐藏机制就会中断:

Child child = new Child();
Parent parent = (Parent)child;
System.out.println(parent.var); // prints out 1, instead of 2

这不是完全绕过了字段隐藏的全部要点吗? 如果是这样,那么这不是使这个想法完全无用吗?

编辑:我特别指的是 Java 教程中的这篇文章 它提到

在子类中,超类中的字段不能通过其简单名称引用。 相反,该字段必须通过超级访问...

从我在那里读到的内容,这似乎暗示 Java 的开发人员在执行此操作时考虑了某种技术。 虽然我同意这是一个相当晦涩的概念,而且一般来说可能是不好的做法。

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

最好的前进方式实际上取决于您要实现的目标:

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

字段隐藏的“点”仅仅是指定代码的行为,该行为确实赋予变量与其超类中的名称相同的名称。

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

这种情况称为变量隐藏,当子类和父类都有同名变量时,子类的变量隐藏父类的变量,这个过程称为变量隐藏。

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

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

在方法覆盖的情况下,被覆盖的方法完全取代了继承的方法,因此当我们尝试通过持有子类的对象从父类的引用访问方法时,会调用子类中的方法。

但是在变量隐藏子类中隐藏了继承的变量而不是替换,所以当我们试图通过持有子对象从父类引用访问变量时,它将从父类访问。

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"
}

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

在 child 和 parent 中声明具有相同名称的变量会造成混淆,我们应该始终避免它,这样就不会有混淆。 这就是为什么我们还应该始终坚持通用指南来创建 POJO并使用私有访问声明我们的变量,并提供适当的 get/set 方法来访问它们。

您可以在我的文章什么是 Java 中的变量阴影和隐藏中阅读更多内容。

属性在 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());

当您进行转换时,您可以有效地告诉编译器“我知道得更好”——它暂停了正常的强类型推理规则,并为您提供了怀疑的好处。

通过说Parent parent = (Parent)child; 您告诉编译器“将此对象视为父对象的实例”。

另一方面,您将 OO 的“信息隐藏”原则(好!)与字段隐藏副作用(通常很糟糕)混淆了。

正如你所指出的:

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

这里的要点是变量不会像方法那样覆盖,因此当您直接调用Child.var 时,您是直接从 Child 类调用变量,而当您调用Parent.var 时,您是从 Parent 类调用变量,不如果它们确实具有相同的名称,这很重要。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM