Adding a field to a class will not break compatibility with any pre-existing binaries that are not recompiled, even in the case where a class could no longer be recompiled because a field access previously referenced a field of a superclass with an incompatible type. The previously compiled class with such a reference will continue to reference the field declared in a superclass. Thus compiling and executing the code:
class Hyper { String h = "hyper"; } class Super extends Hyper { String s = "super"; } class Test { public static void main(String[] args) { System.out.println(new Super().h); } }
hyper
Changing Super
to be defined as:
class Super extends Hyper { String s = "super"; int h = 0; }
recompiling Hyper
and Super
, and executing the resulting new binaries with the
old binary of Test
produces the output:
hyper
The field h
of Hyper
is output by the original binary of main
no matter what type
field h
is declared in Super
. While this may seem surprising at first, it serves to
reduce the number of incompatibilities that occur at run time. (In an ideal world,
all source files that needed recompilation would be recompiled whenever any one
of them changed, eliminating such surprises. But such a mass recompilation is
often impractical or impossible, especially in the Internet. And, as was previously
noted, such recompilation would sometimes require further changes to the source
code.)
Deleting a field from a class will break compatibility with any pre-existing binaries that reference this field, and a NoSuchFieldError
will be thrown when such a reference from a pre-existing binary is linked. Only private
fields may be safely deleted from a widely distributed class.