简体   繁体   English

隐藏类的实例变量

[英]Hiding instance variables of a class

I'm wondering why Java has this strange behavior regarding a superclass and a subclass having instance variables with the same name.我想知道为什么 Java 对具有同名实例变量的超类和子类有这种奇怪的行为。

Let's say we have the following class definitions:假设我们有以下类定义:

class Parent {
    int var = 1;
}

class Child extends Parent {
    int var = 2;
}

By doing this, we are supposed to have hidden the superclass's variable var .通过这样做,我们应该隐藏了超类的变量var And if we do not explicitly specify a way to access Parent 's var via a super call, then we should never be able to access var from an instance of a child.如果我们没有明确指定通过super调用访问Parentvar的方法,那么我们将永远无法从子实例访问var

But when we have a cast, this hiding mechanism breaks:但是当我们有一个演员表时,这种隐藏机制就会中断:

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

Doesn't this completely circumvent the whole point of field hiding?这不是完全绕过了字段隐藏的全部要点吗? If this is the case, then doesn't that render the the idea completely useless?如果是这样,那么这不是使这个想法完全无用吗?

EDIT : I am referring specifically to this article in the Java Tutorials.编辑:我特别指的是 Java 教程中的这篇文章 It mentions它提到

Within the subclass, the field in the superclass cannot be referenced by its simple name.在子类中,超类中的字段不能通过其简单名称引用。 Instead, the field must be accessed through super...相反,该字段必须通过超级访问...

From what I read there, it seems to imply that the developers of Java had some kind of technique in mind in doing this.从我在那里读到的内容,这似乎暗示 Java 的开发人员在执行此操作时考虑了某种技术。 Though I agree that it is a rather obscure concept and would probably bad practice in general.虽然我同意这是一个相当晦涩的概念,而且一般来说可能是不好的做法。

In Java, data members are not polymorphic.在 Java 中,数据成员不是多态的。 This means that Parent.var and Child.var are two distinct variables that happen to have the same name.这意味着Parent.varChild.var是两个不同的变量,它们碰巧具有相同的名称。 You're not in any sense "overriding" var in the derived class;在派生类中,您在任何意义上都没有“覆盖” var as you have discovered yourself, both variables can be accessed independently of one another.正如您自己发现的那样,这两个变量都可以相互独立地访问。

The best way forward really depends on what you're trying to achieve:最好的前进方式实际上取决于您要实现的目标:

  1. If Parent.var should not be visible to Child , make it private .如果Parent.var不应该对Child可见,请将其设为private
  2. If Parent.var and Child.var are two logically distinct variables, give them different names to avoid confusion.如果Parent.varChild.var是两个逻辑上不同的变量,请为它们指定不同的名称以避免混淆。
  3. If Parent.var and Child.var are logically the same variable, then use one data member for them.如果Parent.varChild.var在逻辑上是同一个变量,则为它们使用一个数据成员。

The "point" of field hiding is merely to specify the behaviour of code which does give a variable the same name as one in its superclass.字段隐藏的“点”仅仅是指定代码的行为,该行为确实赋予变量与其超类中的名称相同的名称。

It's not meant to be used as a technique to genuinely hide information.它并不意味着用作真正隐藏信息的技术 That's done by making the variables private to start with... I would strongly recommend using private variables in virtually all cases.这是通过将变量设为私有来完成的……我强烈建议在几乎所有情况下都使用私有变量。 Fields are an implementation detail which should be hidden from all other code.字段是一个实现细节,应该对所有其他代码隐藏。

This scenario is known as variable hiding , When the child and parent class both have a variable with the same name, child class's variable hides parent class's variable and this process is called variable hiding.这种情况称为变量隐藏,当子类和父类都有同名变量时,子类的变量隐藏父类的变量,这个过程称为变量隐藏。

In Java variables are not polymorphic and Variable Hiding is not same as Method Overriding在 Java 中变量不是多态的,变量隐藏与方法覆盖不同

While variable hiding looks like overriding a variable similar to method overriding but it is not, Overriding is applicable only to methods while hiding is applicable variables.虽然变量隐藏看起来像覆盖类似于方法覆盖的变量,但事实并非如此,覆盖仅适用于方法,而隐藏适用于变量。

In the case of method overriding, overridden methods completely replaces the inherited methods so when we try to access the method from parent's reference by holding child's object, the method from child class gets called.在方法覆盖的情况下,被覆盖的方法完全取代了继承的方法,因此当我们尝试通过持有子类的对象从父类的引用访问方法时,会调用子类中的方法。

But in variable hiding child class hides the inherited variables instead of replacing, so when we try to access the variable from parent's reference by holding child's object, it will be accessed from the parent class.但是在变量隐藏子类中隐藏了继承的变量而不是替换,所以当我们试图通过持有子对象从父类引用访问变量时,它将从父类访问。

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

As we can see in above when an instance variable in a subclass has the same name as an instance variable in a superclass, then the instance variable is chosen from the reference type.正如我们在上面看到的,当子类中的实例变量与超类中的实例变量同名时,实例变量将从引用类型中选择。

Declaring variables with the same name in both child and parent create confusion we should always avoid it so there will be no confusion.在 child 和 parent 中声明具有相同名称的变量会造成混淆,我们应该始终避免它,这样就不会有混淆。 And this is why we should also always stick to General Guidelines to create POJOs and declare our variables with private access and also provide proper get/set methods to access them.这就是为什么我们还应该始终坚持通用指南来创建 POJO并使用私有访问声明我们的变量,并提供适当的 get/set 方法来访问它们。

You can read more on my article What is Variable Shadowing and Hiding in Java .您可以在我的文章什么是 Java 中的变量阴影和隐藏中阅读更多内容。

Attributes are not polymorphic in Java, and anyway declaring a public attribute is not always a good idea.属性在 Java 中不是多态的,无论如何声明一个公共属性并不总是一个好主意。 For the behavior you're looking for, it's better to use private attributes and accessor methods, like this:对于您正在寻找的行为,最好使用私有属性和访问器方法,如下所示:

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

}

And now, when testing it, we get the desired result, 2:现在,在测试时,我们得到了想要的结果,2:

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

When you are casting, you effectively tell the compiler "I know better" - it suspends the normal strong-typing inference rules and gives you the benefit of a doubt.当您进行转换时,您可以有效地告诉编译器“我知道得更好”——它暂停了正常的强类型推理规则,并为您提供了怀疑的好处。

By saying Parent parent = (Parent)child;通过说Parent parent = (Parent)child; you are telling the compiler "treat this object as if it were an instance of Parent".您告诉编译器“将此对象视为父对象的实例”。

On another note, you are confusing "information hiding" principle of OO (good!) with a field-hiding side-effect (usually bad).另一方面,您将 OO 的“信息隐藏”原则(好!)与字段隐藏副作用(通常很糟糕)混淆了。

As you pointed out:正如你所指出的:

we are supposed to have hidden the superclass's variable var我们应该隐藏超类的变量 var

The main point here is Variables do not override as methods do , so when you call directly Child.var you are calling a variable directly from the Child class and when you call Parent.var you're calling a variable from the Parent class, no matter if they do have the same name.这里的要点是变量不会像方法那样覆盖,因此当您直接调用Child.var 时,您是直接从 Child 类调用变量,而当您调用Parent.var 时,您是从 Parent 类调用变量,不如果它们确实具有相同的名称,这很重要。

As a side note I would say this is really confusing and shouldn't be allowed as valid syntax.作为旁注,我会说这真的很令人困惑,不应该被允许作为有效的语法。

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

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