Java classes are compiled separately, so a library may change after code using it has been compiled. A change is binary compatible if existing class files that linked without error before the change still link without error after it (JLS chapter 13). This page gives what a compiler must put in a class file to make that possible, and then the effect of every kind of change.
Binary compatibility is not source compatibility: a set of compatible binaries can come from sources that no longer compile together, for instance when a new declaration changes what a name means in unchanged source while the old binary keeps the old meaning.
The form of a binary (13.1)
Programs compile to the class file format, or a form a class loader can map to it. A class file must have these properties.
Binary names
| Declaration | Binary name |
|---|---|
| Top-level class or interface | Its canonical name. The host system decides the name of the class implicitly declared by a compact compilation unit (8.1.8) |
| Member class or interface | Enclosing binary name, $, simple name |
| Local class or interface | Enclosing binary name, $, a non-empty sequence of digits, simple name |
| Anonymous class | Enclosing binary name, $, a non-empty sequence of digits |
| Type variable of a generic class or interface | Enclosing binary name, $, simple name of the variable |
| Type variable of a generic method | Binary name of the declaring class or interface, $, the method’s descriptor (JVMS 4.3.3), $, simple name of the variable |
| Type variable of a generic constructor | Binary name of the declaring class, $, the constructor’s descriptor, $, simple name of the variable |
How references are compiled
- A reference to another class or interface is symbolic, by binary name.
- A reference to a constant variable (4.12.4) must be resolved at compile time to
its value. A
staticconstant must not be read through a field reference at all, and an instance constant only in its own class’s code that gives it its value when an object is created, so its default value can never be seen. - A reference to a non-constant field or a method is symbolic: the
qualifying class or interface of the reference, the simple name, and the
erasure of the field’s type or of the method’s signature and return type, or that
it is
void.
| Form of the reference | Qualifying class or interface |
|---|---|
| Simple name (a member of C or of an enclosing declaration) | The innermost lexically enclosing class or interface of which it is a member |
TypeName.f, TypeName.m, ReferenceType::m | The class or interface named (or, for ReferenceType, its erasure) |
ExpressionName.f, Primary.f, ExpressionName.m, Primary::m | The erasure of the expression’s compile-time type, or of the first bound if it is an intersection type |
super.f, TypeName.super.f | The superclass of the class named |
super.m, super::m, TypeName.super.m | The superclass, or, for an interface named in TypeName.super, that interface |
A method declared in Object | Object, whatever the form |
A constructor reference is symbolic: the qualifying class, plus the number of
parameters and a symbolic reference to each parameter type. The qualifying class is
D for new D(...) or D::new, the anonymous class for
new D(...){...}, the direct superclass for super(...), and C for
this(...).
What a class file must contain
- A symbolic reference to the direct superclass (unless it is
Object) and to each direct superinterface. - Each field, by simple name and the erasure of its type.
- Each constructor’s erased signature, and each method’s erased signature and return type (an interface’s implicitly declared methods excepted).
- The code: field initialisers, instance and static initialisers, method bodies and constructors.
- Enough to recover the canonical name, the source access modifier of a member class, the immediately enclosing class of a nested class, and symbolic references to its member classes and to all other nested classes in its body.
- A construct that does not correspond to anything declared must be marked synthetic, unless it is a class initialisation method.
- A construct corresponding to an implicitly declared formal parameter must be
marked mandated: the first parameter of a non-private inner member class’s
constructor, the first parameter of an anonymous constructor whose superclass is an
inner class, the parameter of an enum class’s implicit
valueOf, and a compact constructor’s parameters. - A module’s class file is a class file called
module-infowith no superclass, superinterfaces, fields or methods. It records the module’s name and whether it is open; eachrequires(with transitive and static flags); eachexportsandopens(with theirtomodules); eachusesservice; eachprovidesservice and its providers.
Packages and modules (13.3)
| Change | Effect on existing binaries |
|---|---|
| Add a top-level class or interface to a package | Compatible, provided the name is not already used by an unrelated type; two unrelated classes of the same name cannot be loaded by one class loader |
| Change or delete a package-private top-level type that is not a superclass of a public type | Affects only that package’s binaries, which must be updated together |
| Stop exporting or opening a package, or change its target modules | IllegalAccessError for a binary that needs the access |
| Export or open a package that was not | Compatible, but the program may fail to start if a module reading it also reads another module exporting a package of that name |
Add a requires, or add transitive | Compatible, but start-up may fail for the same reason |
Delete a requires, or delete transitive | May break a binary that relied on the readability; IllegalAccessError when linked |
Add or delete uses or provides | Compatible |
Classes (13.4)
| JLS | Change | Effect on existing binaries |
|---|---|---|
| 13.4.1 | Make a class abstract | Code that creates instances gets InstantiationError at link time, or InstantiationException through reflection. Not recommended for widely distributed classes |
| 13.4.1 | Remove abstract | Compatible |
| 13.4.2.1 | Make an extensible class sealed | IncompatibleClassChangeError when a pre-existing subclass not permitted is loaded |
| 13.4.2.1 | Add a permitted direct subclass | Compatible; but an exhaustive switch compiled earlier throws MatchException if it meets the new subclass |
| 13.4.2.1 | Remove a permitted subclass | IncompatibleClassChangeError when the removed class is loaded |
| 13.4.2.1 | Delete sealed from a class with no sealed direct supertype | Compatible; with a sealed supertype, the class can no longer be recompiled |
| 13.4.2.2 | Change sealed or final to non-sealed | Compatible; deleting non-sealed stops the class recompiling |
| 13.4.2.3 | Make a class final | IncompatibleClassChangeError when a pre-existing subclass is loaded |
| 13.4.2.3 | Remove final | Compatible (without a sealed direct supertype) |
| 13.4.3 | Make a class public | Compatible |
| 13.4.3 | Make a public class non-public | IllegalAccessError for a binary that needs the access |
| 13.4.4 | Change the direct superclass or superinterfaces | Compatible if the full set of superclasses and superinterfaces loses no members. If a class stops being a supertype, linkage errors may follow (VerifyError, ClassCircularityError for a cycle) |
| 13.4.5 | Add, delete or rename a type parameter | No effect in itself. Changing the first bound changes the erasure of what uses it, as at 13.4.13 |
| 13.4.6 | Add a member with the same name, signature and return type as one in a superclass or subclass | No incompatibility; the superclass or subclass member is hidden or overridden as usual. A super.m() call compiled earlier runs the method now found in the direct superclass without being recompiled |
| 13.4.6 | Delete a non-private member or constructor | A linkage error if a pre-existing binary uses it |
| 13.4.7 | Reduce a member’s access | A linkage error when a pre-existing binary is resolved (widening access is compatible) |
| 13.4.8 | Add a field | Compatible unless the new field is less accessible than a field it now hides, or static where that was instance (or the reverse), for a binary that previously referred to it |
| 13.4.8 | Delete a field | NoSuchFieldError for a binary that uses it; only private fields may be safely deleted |
| 13.4.9 | Make a field final | IllegalAccessError for a binary that assigns it |
| 13.4.9 | Remove final, or change a constant’s value | Compatible, but binaries keep the value compiled in until they are recompiled |
| 13.4.10 | Change a non-private field between static and instance | IncompatibleClassChangeError |
| 13.4.11 | Add or delete transient | Compatible |
| 13.4.12 | Add a method or constructor | Compatible unless the new method overrides one less accessibly or changes static/instance for a reference through a subclass |
| 13.4.12 | Delete a method or constructor | NoSuchMethodError for a binary that used it, unless a superclass declares one with the same signature and return type. Adding the first constructor removes the default constructor, so keep one with no parameters and at least the same access |
| 13.4.13 | Add, delete or rename a method’s or constructor’s type parameter | No effect in itself |
| 13.4.13 | Change the first bound of a type parameter | As if the erased field or method were deleted and a new one added |
| 13.4.14 | Rename a parameter | Compatible |
| 13.4.14 | Change a parameter’s type or count, or a method’s name | As deleting the old method and adding a new one |
| 13.4.14 | Change T[] to T..., or back | Compatible |
| 13.4.15 | Change a method’s result type, or to or from void | As deleting and adding |
| 13.4.16 | Make a method abstract | AbstractMethodError when a pre-existing binary calls it |
| 13.4.16 | Remove abstract | Compatible |
| 13.4.17 | Make an instance method final | IncompatibleClassChangeError for a pre-existing override |
| 13.4.17 | Remove final, or make a static method final | Compatible |
| 13.4.18 | Add or delete native | Compatible |
| 13.4.19 | Change a non-private method between static and instance | IncompatibleClassChangeError |
| 13.4.20 | Add or delete synchronized | Compatible |
| 13.4.21 | Change a throws clause | Compatible; it is checked only at compile time |
| 13.4.22 | Change a method or constructor body | Compatible; a compiler cannot inline a method at compile time |
| 13.4.23 | Add an overload | Compatible; overloads are chosen at compile time, so old binaries keep their old choice until recompiled |
| 13.4.24 | Add an overriding instance method | Found by existing binaries, which keep working; a new static method is found only by references whose qualifying class is the class it was added to |
| 13.4.25 | Add, delete or change a static initialiser | Compatible |
| 13.4.26 | Add or reorder enum constants | Compatible, but an exhaustive switch compiled earlier throws MatchException on a new constant |
| 13.4.26 | Delete an enum constant | Deletes the public field; see 13.4.8. Otherwise enums follow the rules for classes |
| 13.4.27 | Add, delete, change or reorder record components | May break binaries that are not recompiled, through the implicit fields, accessors and canonical constructor (13.4.8, 13.4.12). Otherwise records follow the rules for classes |
Interfaces (13.5)
| JLS | Change | Effect on existing binaries |
|---|---|---|
| 13.5.1 | Make an interface public | Compatible |
| 13.5.1 | Make a public interface non-public | IllegalAccessError for a binary that needs it |
| 13.5.2 | Make an extensible interface sealed | IncompatibleClassChangeError when a subtype not permitted is loaded |
| 13.5.2 | Add a permitted subtype | Compatible; an earlier exhaustive switch may throw MatchException |
| 13.5.2 | Remove a permitted subtype | IncompatibleClassChangeError when it is loaded |
| 13.5.2 | Change sealed to non-sealed | Compatible; deleting non-sealed or (with a sealed superinterface) sealed stops the interface recompiling |
| 13.5.3 | Change the superinterfaces | As for classes (13.4.4); a superinterface that stops being one can cause a VerifyError |
| 13.5.4 | Add an abstract, private or static method | Compatible. Calling a new abstract method on an old implementation throws AbstractMethodError only at run time |
| 13.5.4 | Add a field to a superinterface of C | May hide a field inherited from a superclass, giving IncompatibleClassChangeError or IllegalAccessError |
| 13.5.4 | Delete a member | Linkage error if a binary uses it (NoSuchMethodError or NoSuchFieldError) |
| 13.5.5 | Change type parameters | As for classes |
| 13.5.6 | Change a constant | As for static final fields in classes (13.4.8, 13.4.9) |
| 13.5.7 | Change an interface method | As for class methods (13.4.7, 13.4.14, 13.4.15, 13.4.19, 13.4.21 to 13.4.23) |
| 13.5.7 | Add a default method, or change a method from abstract to default | Compatible, but may give IncompatibleClassChangeError at invocation if K, the qualifying interface, inherits unrelated default methods of that signature from two superinterfaces (see below) |
| 13.5.8 | Add or delete elements of an annotation interface | As adding or deleting a method. Adding or deleting annotations has no effect on linkage |
interface Painter { default void draw() { System.out.println("Here's a picture..."); } }
interface Cowboy { }
public class CowboyArtist implements Cowboy, Painter {
public static void main(String... args) { new CowboyArtist().draw(); }
}
// Later, Cowboy gains a default method too:
interface Cowboy { default void draw() { System.out.println("Bang!"); } }
// CowboyArtist, not recompiled, still links; the call to draw() now throws
// IncompatibleClassChangeError at run time.