Parzival
Parzival

Reputation: 623

Explain how variable hiding is working in this Java code

Consider below code

class A
{
    int x = 5;
    void foo()
    {
        System.out.println(this.x);
    }
}
class B extends A
{
    int x = 6;
    // some extra stuff
}
class C
{
    public static void main(String args[])
    {
         B b = new B();
         System.out.println(b.x);
         System.out.println(((A)b).x);
         b.foo();
    }
 }  

Output of the program is

6
5
5

I understand the first two but can't get my head around the last one. How does b.foo() print 5. B class will inherit the foo method. But shouldn't it print what b.x would print? What exactly is happening here?

Upvotes: 21

Views: 1042

Answers (5)

davidxxx
davidxxx

Reputation: 131346

Fields are not overridable in Java and subclasses with same field names as the parent class shadow "only" the fields of the parent class.
So this.x refers to the x defined in the current class : A.
Whereas the result : 5.

To be more precise : the foo() method is inherited by the B subclass but it doesn't mean that the behavior of the inherited method will change about instance fields referenced since as said fields are not overridable : the this.x expression that refers the A.x field in the foo() method goes on referencing A.x.

It is exactly the same thing as for the two previous statements :

 B b = new B();
 System.out.println(b.x); // refers B.x -> 6
 System.out.println(((A)b).x); // refers A.x -> 5
 b.foo(); // refers under the hood A.x -> 5

The very good answer of rgettman shows how you can overcome the field hiding in the subclass.
A alternative to overcome the hiding relies on making the instance field private (which is recommended) and providing a method that returns the value.
In this way you benefit from the overriding mechanism and the field hiding is not an issue any longer for clients of the classes :

class A
{
    private int x = 5;

    int getX(){
        return x;
    }

    void foo()
    {
        System.out.println(this.getX());
    }
}
class B extends A
{
    private int x = 6;

    int getX(){
        return x;
    }
}

Upvotes: 2

Andreas DM
Andreas DM

Reputation: 10998

When you call

b.foo(); 

It checks to see if B has overridden the method foo(), which it has not. It then looks one level up, to the superclass A and invokes that method.

You have then invoked A's version of foo() which then prints out

this.x

Now, A can not see B's version of x.


In order to solve this, you have to override the method in B

class B extends A
{
    int x = 6;

    @Override
    void foo()
    {
        System.out.println(this.x);
    }

}

Now, calling

b.foo();

will call B's version of foo() and you will get the expected result.

Upvotes: 0

Anatolii
Anatolii

Reputation: 14660

In JAVA, methods can be overridden while variables can't. So, as your method foo is not overridden in B, it takes the member variable from A.

Upvotes: 1

Michel_T.
Michel_T.

Reputation: 2821

Well, this is because of static binding.

1) Static binding in Java occurs during Compile time while Dynamic binding occurs during Runtime.

2) private methods, final methods and static methods and variables uses static binding and bonded by compiler while virtual methods are bonded during runtime based upon runtime object.

3) Static binding uses Type(Class in Java) information for binding while Dynamic binding uses Object to resolve binding.

4) Overloaded methods are bonded using static binding while overridden methods are bonded using dynamic binding at runtime.

Upvotes: 2

rgettman
rgettman

Reputation: 178263

Yes, the B class inherits the foo method. But the variable x in B hides the x in A; it doesn't replace it.

This is an issue of scope. The foo method in A sees only the variables that are in scope. The only variable in scope is the instance variable x in A.

The foo method is inherited, but not overridden, in B. If you were to explicitly override foo with the same exact code:

class B extends A
{
    int x = 6;

    @Override
    void foo()
    {
        System.out.println(this.x);
    }
}

Then the variable that would be in scope when referred to by this.x would be B's x, and 6 would be printed. While the text of the method is the same, the reference is different because of scope.

Incidentally, if you really wanted to refer to A's x in the B class, you can use super.x.

Upvotes: 17

Related Questions