Explain how variable hiding is working in this Jav

2019-02-11 19:47发布

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?

5条回答
一纸荒年 Trace。
2楼-- · 2019-02-11 19:47

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.

查看更多
你好瞎i
3楼-- · 2019-02-11 19:54

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.

查看更多
Fickle 薄情
4楼-- · 2019-02-11 19:57

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.

查看更多
趁早两清
5楼-- · 2019-02-11 20:01

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;
    }
}
查看更多
做个烂人
6楼-- · 2019-02-11 20:05

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.

查看更多
登录 后发表回答