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 :
SubCovariantTest
object, and has a valid override
for getObject()
, so use the overridden methodIs it a case of the JVM ignoring the getObject()
part, and always taking x
from c1
as variables are associated at compile time?
In the case of method overriding, overriding methods completely replace the inherited methods but in variable hiding, the child class hides the inherited variables instead of replacing them which basically means that the object of Child class contains both variables but Child's variable hides Parent's variable.
Instance Methods The ability of a subclass to override a method allows a class to inherit from a superclass whose behavior is "close enough" and then to modify behavior as needed. The overriding method has the same name, number and type of parameters, and return type as the method that it overrides.
Because instance variables CANNOT be overridden in Java. In Java, only methods can be overridden. When you declare a field with the same name as an existing field in a superclass, the new field hides the existing field. The existing field from the superclass is still present in the subclass, and can even be used ...
For example, in Java, a method that is declared final in the super class cannot be overridden. Methods that are declared private or static cannot be overridden either because they are implicitly final. It is also impossible for a class that is declared final to become a super class.
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)
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With