Java 隐藏类的实例变量

Java 隐藏类的实例变量,java,oop,Java,Oop,我想知道为什么Java在超类和子类的实例变量同名时会有这种奇怪的行为 假设我们有以下类定义: class Parent { int var = 1; } class Child extends Parent { int var = 2; } 通过这样做,我们应该隐藏了超类的变量var。如果我们没有明确指定通过super调用访问Parent的var的方法,那么我们就永远不能从子实例访问var 但当我们有一个演员阵容时,这种隐藏机制就会崩溃: Child child = new

我想知道为什么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教程中特别提到的。它提到

在子类中,超类中的字段不能被引用 名字很简单。相反,必须通过超级计算机访问字段

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

字段隐藏的“要点”仅仅是指定代码的行为,它确实给了一个变量与其超类中的变量同名


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

在Java中,数据成员不是多态的。这意味着
Parent.var
Child.var
是两个恰好具有相同名称的不同变量。在任何意义上,您都不会在派生类中“重写”
var
;正如您自己所发现的,这两个变量都可以相互独立地访问

前进的最佳方式实际上取决于你努力实现的目标:

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

  • 当您进行强制转换时,您会有效地告诉编译器“我知道得更好”-它会挂起正常的强类型推理规则,并让您可以放心

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

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

    正如你所指出的:

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

    这里的要点是变量不会像方法那样重写,因此当您直接调用Child.var时,您是直接从子类调用变量,当您调用Parent.var时,您是从父类调用变量,无论它们是否具有相同的名称


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

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

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

    在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"
    }
    
    正如我们在上面所看到的,当子类中的实例变量与超类中的实例变量具有相同的名称时,则从引用类型中选择实例变量

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


    你可以阅读我文章的更多内容。

    我真的很想知道为什么编译器不禁止这样做。我想不出这在什么情况下有用。如果你想让孩子的var值不同于父母?@tskuzzy:我想这只是在谈论类内的代码,真的。它当然不是试图将隐藏字段转换为删除字段访问权限的操作。