12

I'm preparing for the SCJP (recently rebranded as OCPJP by Oracle) and one particular question that I got wrong on a mock exam has confused me, the answer description doesn't explain things clear enough.

This is the question :

class A 
{
    int x = 5;
} 
class B extends A 
{
    int x = 6;
} 
public class CovariantTest 
{
    public A getObject() 
    {
       return new A();
    } 
    public static void main(String[]args) 
    {
       CovariantTest c1 = new SubCovariantTest();
       System.out.println(c1.getObject().x);
    }
}

class SubCovariantTest extends CovariantTest 
{
    public B getObject() 
    {
       return new B();
    }
}

The answer is 5, but I chose 6.

I understand that overriding applies to methods at runtime, and not variables, but the way my mind interpreted that println was :

  1. call getObject on c1
  2. c1 is actually a SubCovariantTest object, and has a valid override for getObject(), so use the overridden method
  3. The override returns B, so grab x from B which is 6

Is it a case of the JVM ignoring the getObject() part, and always taking x from c1 as variables are associated at compile time?

Jimmy
  • 16,123
  • 39
  • 133
  • 213

6 Answers6

13

Although the override is done properly for SubCovariantTest the answer is 5 because of how the variable c1 is declared. It is declared as a CovariantTest and not as a SubCovariantTest.

When c1.getObject().x is run, it does not know that it is a SubCovariantTest (no casting was used). This is why 5 is returned from CovariantTest and not 6 from SubCovariantTest.

If you change

System.out.println(c1.getObject().x);

to

System.out.println(((SubCovariantTest) c1).getObject().x);

you will get 6 as you expected.

Edit: As pointed out in the comments

"fields are not polymorphic in Java. Only methods are. The x in the subclass hides the x in the base class. It doesn't override it." (Thanks to JB Nizet)

Scott
  • 12,077
  • 4
  • 27
  • 48
  • 1
    Are you sure? I'm pretty sure the whole point of polymorphism is to be able to do something like this: `Paint[] paint = { new RedPaint(), new BluePaint(), new GreenPaint() };' and then have `for(Paint p : paint) paint.getColor();` where each subclass of `Paint` has a valid override for `getColor()`. Each should perform differently regardless of knowing the subclass or needing to cast to it. – asteri Sep 25 '12 at 18:57
  • 10
    Yes, but fields are not polymorphic in Java. Only methods are. The x in the subclass hides the x in the base class. It doesn't override it. And besides encapsulation, that's another good reason NOT to use public fields. – JB Nizet Sep 25 '12 at 18:58
  • Oh, I see. Interesting! I never knew that. You should add that to the answer. :) – asteri Sep 25 '12 at 18:59
  • Thanks @Scott for explanation. I was just looking for a way to access SuperClass variable through such similar code. Any idea how could we use super in this & get same output? – Shabbir Essaji Feb 23 '18 at 04:49
  • use super.varibaleName – Vindhya Pratap Singh Aug 01 '18 at 05:27
6

Okay I know this is a bit late to reply to this question but I and my friend had the same problem and the answers already here didn't quite clear it for us. So I'll just state what problem I had and how it makes sense now :)

Now I do understand that fields don't get overrided but instead they get hidden as miller.bartek pointed out and I also understand that overriding is for methods and not fields as Scott points out.

The problem I had however was this. According to me,

c1.getObject().x

This must transform into:

new B().x     // and not newA().x since getObject() gets overrided

And that evaluates to 6.

And I couldn't get why the variable of class A (super-class) is being called by an object of class B (sub-class) without having explicitly asked for such a behaviour.

And guessing from the wording of the question, I feel the OP had the same question/doubt in mind.


My Answer:

You get a hint from Elbek's answer. Put the following lines in the main method and try to compile the code:

A a = c1.getObject();    //line 1
B b = c1.getObject();    //line 2

You'll notice that line 1 is completely legal while line 2 gives compilation error.

So when the function getObject() is being called, the CovariantTest (super) function IS getting overrided by SubCovariantTest (sub) function since that is valid overriding in the code and c1.getObject() WILL return new B().

However, since the super-function returns a reference of class-type A, even after getting overrided, it must return a reference of class-type A unless ofcourse we type-cast it. And here, class B is a class A (due to inheritance).

So practically, what we're getting from c1.getObject() is not

new B()

but this:

(A) new B()

That is why the output comes out to be 5 even though an object of class B is returned and class B has value of x as 6.

gaurang847
  • 323
  • 6
  • 11
  • 2
    Right, because for Java version 5.0 and later, covariant overriding became a language feature. This new feature allows the overriding of a method by a derived-class method that has the same signature but a different return type, so long as the derived method's *return* type is derived from the overridden method's return type. So, as explained above, when a variable whose type is CovariantTest is assigned an instance of SubCovariantTest, the value returned by getObject() will indeed be an instance of B, but it will be cast to class A to match the return type of getObject() in CovariantTest. – Carl Sep 10 '18 at 16:54
4

The technical term for what is happening here is "hiding". Variables names in Java are resolved by the reference type, not the object they are referencing.

  • A object has a A.x variable.
  • B object has both A.x and B.x variables.

However instance methods with the same signature are "overridden" not "hidden", and you cannot access the version of a method that is overridden from the outside.

Note that hiding also applies to static methods with the same signature.

Your mock question in a simplified form (without overriding):

class A {
    int x = 5;
}

class B extends A {
    int x = 6;
}

public class CovariantTest {

    public static void main(String[] args) {

        A a = new B();
        B b = new B();
        System.out.println(a.x); // prints 5
        System.out.println(b.x); // prints 6

    }
}
BartoszMiller
  • 1,245
  • 1
  • 15
  • 24
  • So for static methods, can we say my child class B has 2 static methods in it, A.staticMethod() & B.staticMethod() ? As far as i know, static members and variables aren't inherited.. – Shabbir Essaji Feb 23 '18 at 04:53
3

You are calling method from c1: System.out.println(c1.getObject().x);

c1 reference type is:

public class CovariantTest 
{
    public A getObject() 
    {
       return new A();
    } 
    public static void main(String[]args) 
    {
       CovariantTest c1 = new SubCovariantTest();
       System.out.println(c1.getObject().x);
    }
}

so for this: c1.getObject() return type is A. from A you getting directly attribute not method, as you mention java does not override attributes, so it is grabbing x from A

Elbek
  • 3,434
  • 6
  • 37
  • 49
0

When methods are overridden, subclass methods are called, and when variables are overridden the superclass variables are used

0

When the child and parent class both have a variable with same name child class's variable hides parent class's variable and this is called variable hiding.

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.

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.

You can read more on my article What is Variable Shadowing and Hiding in Java.

Naresh Joshi
  • 4,188
  • 35
  • 45