Fields
The rules are a lot less complicated for fields than for methods because methods have more complicated signatures and the potential for overriding and overloading. For fields, the name and signature are determined at compile time. Let's add some fields to these classes:
class Wine { float proof = 22; } class WhiteWine { float proof = 18; } class RedWine { float proof = 25; }
If you ask for this:
Wine wine = sommelier.recommend("duck"); System.out.println(wine.proof);
it's always going to print 22, no matter what wine is recommended. That's because the wine.proof expression is compiled as follows:
getfield Wine/proof F
The other proof fields are ignored; it fetches the proof field for the Wine class. The others are actually different fields; a RedWine class has two different proof fields. You can find out its value with a cast:
(RedWine) wine.proof
which compiles to:
getfield RedWine/proof F
which returns 25.
The implications for binary compatibility are easy to understand: A field cannot be moved, overridden, renamed, or retyped without breaking binary compatibility with other classes that use it. The virtual invocation mechanism doesn't apply, and the JVM won't seek out another field if you delete the one that's requested, even if one exists with a compatible type that would have been used at runtime.