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.