隐藏类的实例变量

发布于 2024-12-10 05:22:10 字数 903 浏览 0 评论 0原文

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

假设我们有以下类定义:

class Parent {
    int var = 1;
}

class Child extends Parent {
    int var = 2;
}

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

但是当我们进行强制转换时,这种隐藏机制就会崩溃:

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

这不是完全规避了字段隐藏的全部意义吗?如果真是这样的话,那这个想法岂不是完全没用了?

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

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

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

I'm wondering why Java has this strange behavior regarding a superclass and a subclass having instance variables with the same name.

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. 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.

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. 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. Though I agree that it is a rather obscure concept and would probably bad practice in general.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

挥剑断情 2024-12-17 05:22:10

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

最好的方法实际上取决于您想要实现的目标:

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

In Java, data members are not polymorphic. This means that Parent.var and Child.var are two distinct variables that happen to have the same name. You're not in any sense "overriding" var in the derived class; 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.
  2. If Parent.var and Child.var are two logically distinct variables, give them different names to avoid confusion.
  3. If Parent.var and Child.var are logically the same variable, then use one data member for them.
筑梦 2024-12-17 05:22:10

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

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

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.

淡紫姑娘! 2024-12-17 05:22:10

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

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

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

在子级和父级中声明具有相同名称的变量会造成混乱,我们应该始终避免这种情况,这样就不会造成混乱。这就是为什么我们也应该始终坚持 一般准则创建 POJO 并声明具有私有访问权限的变量,并提供适当的 get/set 方法来访问它们。

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

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

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. 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.

You can read more on my article What is Variable Shadowing and Hiding in Java.

落花浅忆 2024-12-17 05:22:10

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());

Attributes are not polymorphic in Java, and anyway declaring a public attribute is not always a good idea. 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:

Child child = new Child();
Parent parent = (Parent)child;
System.out.println(parent.getVar());
新人笑 2024-12-17 05:22:10

当你进行转换时,你有效地告诉编译器“我更了解”——它暂停了正常的强类型推理规则,并给你带来了怀疑的好处。

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

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

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; 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).

初见你 2024-12-17 05:22:10

正如您所指出的:

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

这里的要点是变量不会像方法那样被重写,所以当你直接调用Child.var时strong> 您直接从 Child 类调用变量,而当您调用 Parent.var 时,您正在从 Parent 类调用变量,无论它们是否具有相同的名称。

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

As you pointed out:

we are supposed to have hidden the superclass's variable 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.

As a side note I would say this is really confusing and shouldn't be allowed as valid syntax.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文