17

My code is:

class Foo {
  public int a=3;
  public void addFive() {
    a+=5;
    System.out.print("f ");
  }
}

class Bar extends Foo {
  public int a=8;
  public void addFive() {
    this.a += 5;
    System.out.print("b ");
  }
}

public class TestClass {
  public static void main(String[]args) {
    Foo f = new Bar();
    f.addFive();
    System.out.println(f.a);
  }
}

Output:

b 3

Please explain to me, why is the output for this question "b 3" and not "b 13" since the method has been overridden?

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
Himanshu Aggarwal
  • 1,803
  • 2
  • 24
  • 36

4 Answers4

19

F is a reference of type Foo, and variables aren't polymorphic so f.a refers to the variable from Foo which is 3

How to verify it?

To test this you can remove a variable from Foo , it will give you compile time error

Note: Make member variable private and use accessors to access them


Also See

Community
  • 1
  • 1
jmj
  • 237,923
  • 42
  • 401
  • 438
  • 10
    To extend this answer a bit, this is one reason why it is often safer to use getters instead of accessing variables directly. – Dave Newton Aug 15 '12 at 17:55
  • @DaveNewton please tell me something about getters? i haven't heard this before. – Himanshu Aggarwal Aug 15 '12 at 17:59
  • 2
    This issue could be quietly avoided if used a common programming practice in Java: variables must be private (only the own class can use it). After that, as said above, using getters and setters methods to access/modify it. – axcdnt Aug 15 '12 at 18:00
  • 2
    Dave is saying to make private fields and use `getA()` `setA()` accessor methods to access the fields – jmj Aug 15 '12 at 18:01
16

You cannot override variables in Java, hence you actually have two a variables - one in Foo and one in Bar. On the other hand addFive() method is polymorphic, thus it modifies Bar.a (Bar.addFive() is called, despite static type of f being Foo).

But in the end you access f.a and this reference is resolved during compilation using known type of f, which is Foo. And therefore Foo.a was never touched.

BTW non-final variables in Java should never be public.

Dheeru Mundluru
  • 373
  • 3
  • 10
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • 4
    Well, except if you have `GridBagConstraints` or similar objects. But for a beginner, **never** is an appropriate advice :) – Marko Topolnik Aug 15 '12 at 18:37
12

With such a question, the SCJP exam is assessing your knowledge of what is known as hiding. The examiner deliberately complicated things to try to make you believe that the behavior of the program depends only on polymorphism, wich it doesn't.

Let's try to make things a bit clearer as we remove the addFive() method.

class Foo {
  public int a = 3;
}

class Bar extends Foo {
  public int a = 8;
}

public class TestClass {
  public static void main(String[]args) {
    Foo f = new Bar();
    System.out.println(f.a);
  }
}

Now things are a bit less confusing. The main method declares a variable of type Foo, which is assigned an object of type Bar at runtime. This is possible since Bar inherits from Foo.The program then refers to the public field a of the variable of type Foo.

The error here would be to believe that the same kind of concept known as overriding applies to class fields. But there is no such a concept for fields: the public field a of class Bar is not overriding the public field a of class Foo but it does what is called hiding. As the name implies, it means that in the scope of the class Bar, a will refer to Bar's own field which has nothing to do with Foo's one. (JLS 8.4.8 - Inheritance, Overriding, and Hiding)

So, when we are writing f.a, which a are we referring to? Recall that resolution of the field a is done at compile time using the declaring type of the object f, which is Foo. As a consequence, the program prints '3'.

Now, lets add an addFive() method in class Foo and override it in class Bar as in the exam question. Here polymorphism applies, therefore the call f.addFive() is resolved using not the compile time but the runtime type of object f, which is Bar, and thus is printed 'b '.

But there is still something we must understand: why the field a, which was incremented by 5 units, still sticks to the value '3'? Here hiding is playing around. Because this is the method of class Bar which is called, and because in class Bar, every a refers to Bar's public field a, this is actually the Bar field which is incremented.

1) Now, one subsidiary question: how could we access the Bar's public field a from the main method? We can do that with something like:

System.out.println( ((Bar)f).a );

which force the compiler to resolve the field member a of f as Bar's a field.

This would print 'b 13' in our example.

2) Yet another question: how could we work around hiding in addFive() method of class Bar to refer not to the Bar's a field but to its superclass eponimous field ? Just adding the super keyword in front of the field reference does the trick:

public void addFive() {
  super.a += 5;
  System.out.print("b ");
}

This would print 'b 8' in our example.

Note that the initial statement

public void addFive() {
  this.a += 5;
  System.out.print("b ");
}

could be refined to

public void addFive() {
  a += 5;
  System.out.print("b ");
}

because when the compiler is resolving the field a, it will start to look in the closest enclosing scope from within the method addFive(), and find the Bar class instance, eliminating the need to use explicitely this.

But, well, this was probably a clue for the examinee to solve this exam question !

Alexandre Dupriez
  • 3,026
  • 20
  • 25
1

Since you are doing f.a you will get the value of a from the class Foo. if you had called a method to get the value, e.g getA() then you would have gotten the value from the class Bar.

fastcodejava
  • 39,895
  • 28
  • 133
  • 186