Reputation: 71008
Studying JVM internals and am a bit confused about this example, which features both an overriding method in a subclass and an instance variable being shadowed:
class CA extends Object {
public int ivar;
public void two() {
ivar = 20;
this.three();
}
public void three() {
System.out.println("three called in CA");
System.out.println(ivar);
}
}
class CB extends CA {
public int ivar;
public void one() {
ivar = 10;
two();
}
public void three() {
System.out.println("three called in CB");
System.out.println(ivar);
super.three();
}
public static void main(String[] args) {
CB cb = new CB();
cb.one();
}
}
This produces the following output:
three called in CB
10
three called in CA
20
The method calls are entirely expected but I observe that the instance variable behaves differently-- when it's set and accessed within the implementation of the superclass, it's accessing the "shadowed" value.
I can't seem to find the discussion of how these behaviors work in the JVM spec, sections 5.4.3.2 and 5.4.3.3 (fields and methods, respectively). Both sections have very similar wording:
If
C
declares a field with the name and descriptor specified by the field reference, field lookup succeeds. [...]
and
[...] if
C
declares a method with the name and descriptor specified by the method reference, method lookup succeeds.
This suggests to me that the ivar behavior in the example above is sensible-- the class file for CA
doesn't know about CB
, so its field ref points to its own version of ivar
and the JVM respects that when it sets and gets the value as it executes that class's method.
But the same would seem true for the overriding method, and I don't find the resolution algorithm to talk about "current object class" vs "implementing object class", etc. Am I looking in the wrong place? Can someone illuminate for me the design/implementation? Thanks!
Upvotes: 1
Views: 116
Reputation: 638
I don't see your confusion -- what it outputted is exactly what I expected.
main calls
CB.one which sets CB.ivar to 10, then calls
CA.two which sets CA.ivar to 20, then calls
CB.three which prints CB.ivar (10), then calls
CA.three which prints CA.ivar (20)
Upvotes: 0
Reputation: 81074
This is documented in 15.12 of the JLS, Method Invocation Expressions:
Resolving a method name at compile time is more complicated than resolving a field name because of the possibility of method overloading. Invoking a method at run time is also more complicated than accessing a field because of the possibility of instance method overriding.
This is just a note, the actual rules for method invocation are very verbose and the part you want is 15.12.4.4. Locate Method to Invoke.
If the method m of class or interface C is private, then it is the method to be invoked.
Otherwise, overriding may occur. A dynamic method lookup, specified below, is used to locate the method to invoke. The lookup procedure starts from class R, the actual run-time class of the target object.
https://docs.oracle.com/javase/specs/jls/se15/html/jls-15.html#jls-15.12
Upvotes: 1