1

I have an understanding of variable hiding and method overriding and virtual method calling in Java. My question is, why does variable hiding fail to take effect in inherited methods? Does it mean that we have to override the methods which access those variables in every child class?

Abstract class

public abstract class ClassA{

    protected int i = 0;

    public void printOurStuff(){
        System.out.println(i);
    }

    public void printMyStuff(){
        System.out.println(this.i);
    }

    public void printSomeStuff(String s){
        System.out.println(s);
    }

}

Concrete class

 public class ClassB extends ClassA{

     protected int i = 1;

     public static main(String[] args){
         ClassB b = new ClassB();
         b.printOurStuff();
         b.printMyStuff();
         b.printSomeStuff(b.i);
     }

 }

Results

0 0 1

EDIT - changed the access modifier of the field from private to protected and added method printOurStuff

  • 2
    It's not clear which result you want to change - the result of calling `printMyStuff` or the result of calling `printSomeStuff(b.i)`. But those are two entirely separate variables, and each variable is only accessible to code within the same class declaration, as they're both private variables. – Jon Skeet Apr 10 '19 at 06:33
  • 2
    No. It means you should avoid hiding fields. If `i` needs to be available in subclasses, make it protected, or add a protected getter method that you can call in subclasses. – JB Nizet Apr 10 '19 at 06:33
  • @JBNizet changing the access modifier does not fix this anyway. However, I am asking for an explanation on why does this happen, not how to do this – Kusal Hettiarachchi Apr 10 '19 at 06:55
  • It does if you stop hiding fields, i.e. if you remove the `i` field in ClassB and thus have a single `i`field. – JB Nizet Apr 10 '19 at 06:58
  • Updated the question to reflect the problem of my interest. – Kusal Hettiarachchi Apr 10 '19 at 07:17

1 Answers1

2

When you declare private fields like

  private int i = 0;

that means that only this concrete class may access this variable. This field is not available to subclasses. If you want this field to be available for subclasses, you should make it protected instead:

  protected int i = 0;

To override the value of this field you can use a static block, f.e.:

public class ClassB extends ClassA {
    {
         i = 1;
    }
}

or assign a new value in the constructor:

public class ClassB extends ClassA {
     public ClassB() {
         i = 1;
     }
}

As to your example, if you inspect ClassB object with a debugger, you'll find that you actually have two i fields: one for ClassA and one for ClassB.

UPDATE:

As to the case where i variable is protected:
Look carefully at your classes definition.
You can not disagree that you declare i field two times: for ClassA and ClassB. JVM will respect this declaration and follow your instructions. If the field is protected or even public, you still have two fields. You can not just override them as you override methods. And when accessing a field like i = ... you're actually accessing the closest field to your scope. For ClassB it's its field i, not the field of its superclass ClassA.

Also, you can still access the field of the superclass as follows:

super.i = ...

super is the reference to the super class.

Pavel Smirnov
  • 4,611
  • 3
  • 18
  • 28
  • Setting access modifier to private was a mistake I did when posting the question. While adding a static block and assigning the variable in the constructor is totally acceptable as solutions to this problem, I am more concerned about knowing why does this happen. Is it because of the scope of the concrete methods in the abstract class are limited to that of the abstract class? – Kusal Hettiarachchi Apr 10 '19 at 06:59
  • @KusalHettiarachchi no. It's because fields are not polymorphic. They can't be overridden. Only methods can be overridden. So, if a variable (`this` here) is of type ClassA, and you access the field `i` of this variable, then the compiler will resolve it statically to `ClassA.i`. – JB Nizet Apr 10 '19 at 07:02
  • @KusalHettiarachchi, provided some explanation in the answer. – Pavel Smirnov Apr 10 '19 at 07:07
  • @JBNizet i dont have any problem with the `b.i` being resolved to the value of 1. I want to know why the method `printOurStuff` evaluates the value of i to the value set in ClassA – Kusal Hettiarachchi Apr 10 '19 at 07:16
  • @PavelSmirnovyour, my question lies in method `printOutStuff`evaluating i to be 0, although it is called on an instance of ClassB. – Kusal Hettiarachchi Apr 10 '19 at 07:18
  • And I answered to that: because, inside printOurStuff, the implicit variable `this` is of type `ClassA`, and thus `i` is statically resolved to the field `i` declared in `ClassA`. Fields are not polymorphic. They are resolved at compile time. – JB Nizet Apr 10 '19 at 07:19
  • @KusalHettiarachchi, `printOutStuff` is declared inside `ClassA`. It does not even know about `ClassB`. The reference `i` in `System.out.println(i);` references `ClassA.i` field. This is the same as `System.out.println(this.i);` where `this` is the reference to `ClassA`. – Pavel Smirnov Apr 10 '19 at 07:21
  • @KusalHettiarachchi, just think about it this way: you do not override `i` field in `ClassB`, you do not override `printOutStuff` method. So why should it use an absolutely new variable `ClassB.i` instead of `ClassA.i`? – Pavel Smirnov Apr 10 '19 at 07:23
  • @PavelSmirnov so variable hiding does not work for concrete methods implemented in abstract methods unless we effectively override the methods in child classes? – Kusal Hettiarachchi Apr 10 '19 at 07:23
  • Then this is why it is necessary to initialize the variable in the constructor or in a static block. Thank you @PavelSmirnov for the guidance – Kusal Hettiarachchi Apr 10 '19 at 07:25
  • 1
    @KusalHettiarachchi, you do not actually have to initialize it in the constructor or in a static block, if you do not have name conflict. It's just the scope issue: two variables with the same name, so the compiler chooses the closest one accroding to the scope. – Pavel Smirnov Apr 10 '19 at 07:29