开发者

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.

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 suppos开发者_开发技巧ed 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.


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.


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.


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


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.


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


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜