【问题标题】:Hiding instance variables of a class隐藏类的实例变量
【发布时间】:2011-12-09 07:49:35
【问题描述】:

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

假设我们有以下类定义:

class Parent {
    int var = 1;
}

class Child extends Parent {
    int var = 2;
}

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

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

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

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

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

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

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

【问题讨论】:

  • 我真的很想知道为什么编译器不禁止这样做。我想不出有什么用处。
  • 如果您希望 var 的值对于 Child 与 Parent 不同?

标签: java oop


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

    • @tskuzzy:我认为这只是在讨论在类中的代码,真的。它当然不是试图将隐藏字段变成您为了删除对字段的访问而做的事情。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      这种场景称为变量隐藏,当子类和父类都有同名的变量时,子类的变量会隐藏父类的变量,这个过程称为变量隐藏。 p>

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

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

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

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

      【讨论】:

        【解决方案5】:

        当您进行强制转换时,您实际上是在告诉编译器“我知道得更清楚”——它暂停了正常的强类型推理规则,并为您提供了怀疑的好处。

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

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

        【讨论】:

          【解决方案6】:

          正如你所指出的:

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

          这里的重点是变量不要像方法那样覆盖,所以当你直接调用Child.var时你调用的是一个变量直接从 Child 类调用,当您调用 Parent.var 时,您是从 Parent 类调用一个变量,无论它们是否具有相同的名称。

          作为旁注,我想说这真的很混乱,不应该被允许作为有效的语法。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2013-08-18
            • 1970-01-01
            • 2013-06-15
            • 1970-01-01
            • 1970-01-01
            • 2011-01-07
            • 1970-01-01
            相关资源
            最近更新 更多