Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Evolving Java-based APIs"

(declare revision 1.1 done)
m (added quick links to parts 2 and 3 on top of the page)
(17 intermediate revisions by 4 users not shown)
Line 2: Line 2:
  
 
Revision history:
 
Revision history:
 +
 +
: October 1, 2007 - revision 1.2 - Clarified moving methods up and down type hierarchy; added note about Java reflection; split document into 3 parts because it was getting too large to edit:
 +
:* Part 1: What is an API? (this page)
 +
:* [[Evolving_Java-based_APIs_2 | Part 2: Achieving API Binary Compatibility]]
 +
:* [[Evolving_Java-based_APIs_3 | Part 3: Other Notes]]
  
 
: February 14, 2007 - revision 1.1 - Add coverage for JDK 1.5 language features: generics, enums, annotation types, variable arity methods.
 
: February 14, 2007 - revision 1.1 - Add coverage for JDK 1.5 language features: generics, enums, annotation types, variable arity methods.
Line 24: Line 29:
 
those which are not:
 
those which are not:
 
<blockquote>
 
<blockquote>
 +
<ul>
 
<li>
 
<li>
 
<b>API package</b> - a package that contains at least one API class or
 
<b>API package</b> - a package that contains at least one API class or
Line 56: Line 62:
 
declared in, or inherited by, an API class or interface. The names of API
 
declared in, or inherited by, an API class or interface. The names of API
 
fields appear in Client code.</li>
 
fields appear in Client code.</li>
 
+
</ul>
 
</blockquote>
 
</blockquote>
 
The following elements are not considered API:
 
The following elements are not considered API:
Line 393: Line 399:
 
== Achieving API Binary Compatibility ==
 
== Achieving API Binary Compatibility ==
  
<blockquote><i>"[A]n object-oriented model must be carefully designed so
+
See [[Evolving_Java-based_APIs_2 | Part 2]].
that class-library transformations that should not break already compiled
+
applications, indeed, do not break such applications."<br>
+
  </i>---Ira Forman,
+
Michael Conner, Scott Danforth, and Larry Raper, "Release-to-Release Binary
+
Compatibility in SOM", in <i>Proceedings of OOPSLA '95.</i>
+
</blockquote>
+
  
Achieving API binary compatibility depends in part on the Java language's notion of binary compatibility:
+
== Other Notes ==
<blockquote>"A change to a type is <i>binary compatible with </i>(equivalently, does not <i>break binary compatibility </i>with) preexisting binaries if preexisting binaries that previously linked without error will continue to link without error." ([http://java.sun.com/docs/books/jls/third_edition/html/binaryComp.html#13.2 JLS3, 13.2])</blockquote>
+
Reference: [http://java.sun.com/docs/books/jls/third_edition/html/binaryComp.html Gosling, Joy, Steele, and Bracha, <i>The Java Language Specification</i>, Third Edition, Addison-Wesley, 2005; chapter 13 Binary Compatibility].
+
  
The tables in the following sections summarize which kinds of changes break API binary compatibility.
+
See [[Evolving_Java-based_APIs_3 | Part 3]].
 
+
Bear in mind that many changes will have effects in several places. For example, defining a new public interface with one public method and adding that interface as the superinterface of an existing interface has the following ramifications:
+
 
+
<ul>
+
<li>
+
a new public API interface is added to an API package</li>
+
 
+
<li>
+
the superinterface set of the existing API interface has expanded</li>
+
 
+
<li>
+
a new public API method is added to the existing API interface</li>
+
</ul>
+
Each of these individual net effects could break binary compatibility.
+
Use the tables to determine whether the <i>net effects </i>preserve or
+
break compatibility.
+
 
+
=== Evolving API packages ===
+
 
+
It is always possible to evolve the Component API to include a new API
+
package. However, once introduced in a release, an API package cannot easily
+
be withdrawn from service. When an API package becomes obsolete, its API
+
classes and API interfaces should continue to work but be marked as deprecated.
+
After a couple of releases, it may be possible to phase out an obsolete
+
API package.
+
 
+
The names of non-public (non-API) types in API packages
+
do not appear in Client source code or binaries. Non-API types
+
can be added or deleted without jeopardizing binary compatibility. However,
+
once made public in a release, these types are part of
+
the API and cannot easily be withdrawn from service without breaking existing
+
Clients. When an API type becomes obsolete, it should continue
+
to work but be marked as deprecated.
+
<br>&nbsp;
+
<table BORDER COLS=3 WIDTH="100%" >
+
<tr>
+
<td>Add API package</td>
+
 
+
<td width="20%">-</td>
+
 
+
<td width="20%">Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete API package</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Add API type to API package</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete API type from API package</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
 
+
<tr>
+
<td>Add non-<code>public </code>(non-API) type to API package</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete non-<code>public</code> (non-API) type from API package</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change non-<code>public</code> (non-API) type in API package
+
to make public (API)</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change <code>public</code> type in API package to make non-<code>public</code></td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Change kind of API type (class, interface, enum, or annotation type)</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (1)</td>
+
</tr>
+
 
+
</table>
+
 
+
(1) API class-interface gender changes break binary compatibility, even
+
in cases where the class/interface is used by, but not implemented by,
+
Clients. This is because the Java VM bytecodes for invoking a method declared
+
in an interface are different from the ones used for invoking a method
+
declared in a class. More generally, all gender changes involving classes,
+
enums, interfaces, and annotation types break binary compatibility for
+
one reason or another.
+
 
+
=== Evolving API Interfaces ===
+
 
+
Evolving API interfaces is somewhat more straightforward than API classes
+
since all methods are <code>public</code> and <code>abstract</code>, all fields
+
are <code>public</code> <code>static</code> and <code>final</code>, all type members
+
are <code>public</code> and <code>static</code>, and there are no constructors.
+
Annotation types (<code>@interface</code>) , which are a form of interface, are also covered.
+
 
+
<table BORDER COLS=3 WIDTH="97%" >
+
<tr>
+
<td ROWSPAN="2">Add API method</td>
+
 
+
    <td width="35%">If method need not be implemented by Client</td>
+
 
+
    <td width="25%">Binary compatible (0)</td>
+
</tr>
+
 
+
<tr>
+
<td>If method must be implemented by Client</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (1)</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete API method&nbsp;</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td ROWSPAN="2">Add API field</td>
+
 
+
<td>If interface not implementable by Clients</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>If interface implementable by Clients</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (2)</td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>Delete API field</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Expand superinterface set (direct or inherited)</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Contract superinterface set (direct or inherited)</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (3)</td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>Add, delete, or change static initializers</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td ROWSPAN="2">Add API type member</td>
+
 
+
<td>If interface not implementable by Clients</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>If interface implementable by Clients</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (2)</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete API type member</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Re-order field, method, and type member declarations</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
<tr>
+
<td ROWSPAN="2">Add type parameter</td>
+
 
+
<td>If interface has no type parameters</td>
+
 
+
<td>Binary compatible (4)</td>
+
</tr>
+
 
+
<tr>
+
<td>If interface has type parameters</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
 
+
<tr>
+
<td>Delete type parameter</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
      <td>Re-order type parameters</td>
+
      <td>-</td>
+
      <td><b><font color=#ff0000>Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
      <td>Rename type parameter</td>
+
      <td>-</td>
+
      <td>Binary compatible</td>
+
</tr>
+
<tr>
+
      <td>Add, delete, or change type bounds of type parameter</td>
+
      <td>-</td>
+
      <td><B><FONT color=#ff0000>Breaks compatibility</FONT></B></td>
+
</tr>
+
 
+
<tr>
+
  <td rowspan="2">Add element to annotation type</td>
+
  <td width="35%">If element has a default value</td>
+
  <td width="25%">Binary compatible</td>
+
</tr>
+
<tr>
+
  <td>If element has no default value</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b> (5)</td>
+
</tr>
+
<tr>
+
  <td>Delete element from annotation type </td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b> (6)</td>
+
</tr>
+
 
+
</table>
+
 
+
(0) Although adding a new method to an API interface which need not be reimplemented by Clients does not break binary compatibility, a pre-existing Client subclass of an existing implementation might still provide a pre-existing implementation of a method by this name. See [[#Example 4 - Adding an API method]] in the preceding section for why this breaks API contract compatibility.
+
 
+
(1) Adding a new method to an API interface that is implemented by Clients (e.g., a callback, listener, or visitor interface) breaks compatibility because hypothetical pre-existing implementations do not implement the new method.
+
 
+
(2) Adding an API field to an API interface that is implemented by Clients (e.g., a callback, listener, or visitor interface) breaks binary compatibility in a different way. A field added to a superinterface of C may hide an instance field inherited from a superclass of C, causing linking errors to be detected. Because of this fact, it is important to distinguish between API interfaces that Clients should implement from those that Clients should merely use. API interfaces that Clients should implement should not include fields.
+
 
+
(3) Shrinking the set of API interfaces that a given API interfaces extends (either directly or inherited) breaks compatibility because some casts between API interfaces in hypothetical pre-existing Client code between will no longer work. However, non-API superinterfaces can be removed without breaking binary compatibility.
+
 
+
(4) Altering the type parameters of a parameterized type breaks compatibility. However, adding type parameters to a previously unparameterized type retains compatibility because of Java's special treatment of legacy references (raw types).
+
 
+
(5) Existing annotations would not have a value for the new element, causing an exception (IncompleteAnnotationException) to be thrown when the annotation is read.
+
 
+
(6) Existing annotations that mention the deleted element will cause an exception (AnnotationTypeMismatchException) to be thrown when the annotation is read.
+
 
+
==== Evolving API interfaces - API methods ====
+
 
+
All methods in an API interface are implicitly <code>public</code> and <code>abstract</code>, and are therefore all considered API methods. The same is true for method declarations defining the elements of an API annotation type.
+
 
+
<table BORDER COLS=3 WIDTH="100%" >
+
<tr>
+
<td>Change formal parameter name</td>
+
 
+
<td width="20%" height="0">-</td>
+
 
+
<td width="20%">Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change method name</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Add or delete formal parameter</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Change type of a formal parameter</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Change result type (including <code>void</code>)</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Add checked exceptions thrown</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (1)</td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>Add unchecked exceptions thrown</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete checked exceptions thrown</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (1)</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete unchecked exceptions thrown</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>Re-order list of exceptions thrown</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
  <td rowspan="2">Add type parameter </td>
+
  <td>If method has no type parameters</td>
+
  <td>Binary compatible (2) </td>
+
</tr>
+
<tr>
+
  <td>If method has type parameters</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Delete type parameter </td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Re-order type parameters </td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Rename type parameter</td>
+
  <td>-</td>
+
  <td>Binary compatible</td>
+
</tr>
+
<tr>
+
  <td>Add, delete, or change type bounds of type parameter</td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Change last parameter from array type <code>T[]</code> to variable
+
arity <code>T...</code></td>
+
  <td>-</td>
+
  <td>Binary compatible (3) </td>
+
</tr>
+
<tr>
+
  <td>Change last parameter from variable arity <code>T...</code> to array
+
type <code>T[]</code></td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b> (4)</td>
+
</tr>
+
 
+
<tr>
+
  <td>Add default clause to annotation type element</td>
+
  <td>-</td>
+
  <td>Binary compatible</td>
+
</tr>
+
<tr>
+
  <td>Changing  default clause on annotation type element</td>
+
  <td>-</td>
+
  <td>Binary compatible (5)</td>
+
</tr>
+
<tr>
+
  <td>Delete default clause from annotation type element</td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
</table>
+
 
+
(1) Adding and deleting checked exceptions declared as thrown by an API method does not break binary compatibility; however, it breaks contract compatibility (and source code compatibility).
+
 
+
(2) Adding type parameters to an unparameterized method is a compatible change owing to Java's story for interfacing with non-generic legacy code.
+
 
+
(3) A variable arity method declaration such as &quot;<code>void foo(String... y)</code>&quot; is compiled as if it had been written &quot;<code>void foo(String[] y)</code>&quot;.
+
 
+
(4) Although existing binaries will continue to work, existing invocations in source code may not compile because the compiler no longer automatically bundles up the extra arguments into an array.
+
 
+
(5) Defaults are applied dynamically at the time annotations are read. Changing the default value may affect annotations in all classes, including ones compiled before the change was made.
+
 
+
==== Evolving API interfaces - API fields ====
+
 
+
All fields in an API interface are implicitly <code>public</code>, <code>static</code>,
+
and <code>final</code>; they are therefore all considered API fields.
+
 
+
Because of binary compatibility problems with fields, the Java Language Specification recommends against using API fields. However, this is not always possible; in particular, enumeration constants to be used in <code>switch</code> statements must be defined as API fields.
+
 
+
<table BORDER COLS=3 WIDTH="100%" >
+
<tr>
+
<td>Change type of API field</td>
+
 
+
    <td width="35%">-</td>
+
 
+
    <td width="20%"><b><font color="#FF0000">Breaks compatibility</font></b> (1)</td>
+
</tr>
+
 
+
<tr>
+
<td ROWSPAN="2">Change value of API field</td>
+
 
+
<td>If field is compile-time constant value</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (2)</td>
+
</tr>
+
 
+
<tr>
+
<td>If field is not compile-time constant value</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
</table>
+
 
+
(1) All field type changes break binary compatibility, even seemingly innocuous primitive type widenings like turning a <code>short</code> into an <code>int</code>.
+
 
+
(2) Java compilers always inline the value of constant fields (ones with compile-time computable values, whether primitive or <code>String</code> type). As a consequence, changing the value of an API constant field does not affect pre-existing Clients. Invariably, this fails to meet the objective for changing the API field's value in the first place.
+
 
+
==== Evolving API interfaces - API type members ====
+
 
+
All type members in an API interface are implicitly <code>public</code> and
+
<code>static</code>;
+
they are therefore considered API type members. The rules for evolving
+
an API type member are basically the same as for API classes and interfaces
+
declared at the package level.
+
 
+
=== Evolving API Classes ===
+
 
+
Evolving API classes is somewhat more complex than API interfaces due to
+
the wider variety of modifiers, including <code>protected</code> API members.
+
Enums, which are a form of class, are also covered.
+
 
+
<table BORDER COLS=3 WIDTH="97%" >
+
<tr>
+
    <td ROWSPAN="2">Add API method</td>
+
 
+
    <td width="40%">If method need not be reimplemented by Client</td>
+
 
+
    <td width="25%">Binary compatible (0)</td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>If method must be reimplemented by Client</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (1)</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete API method&nbsp;</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td ROWSPAN="2">Add API constructor</td>
+
 
+
<td>If there are other constructors</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
 
+
<td>If this is only constructor</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (2)</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete API constructor</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
 
+
</tr>
+
 
+
<tr>
+
<td ROWSPAN="2">Add API field</td>
+
 
+
<td>If class is not subclassable by Client</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>If class is subclassable by Client</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (3)</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete API field</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Expand superinterface set (direct or inherited)</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Contract superinterface set (direct or inherited)</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (4)</td>
+
</tr>
+
 
+
<tr>
+
<td>Expand superclass set (direct or inherited)</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Contract superclass set (direct or inherited)</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (4)</td>
+
</tr>
+
 
+
<tr>
+
<td>Add, delete, or change static or instance initializers</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td ROWSPAN="2">Add API type member</td>
+
 
+
<td>If class is not subclassable by Client</td>
+
 
+
<td>Binary compatible</td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>If class is subclassable by Client</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (3)</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete API type member</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Re-order field, method, constructor, and type member declarations</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
 
+
<td>Add or delete non-API members; that is, <code>private</code> or default
+
access fields, methods, constructors, and type members</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change <code>abstract</code> to non-<code>abstract</code></td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change non-<code>abstract</code> to <code>abstract</code></td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (5)</td>
+
</tr>
+
 
+
<tr>
+
<td>Change <code>final</code> to non-<code>final</code></td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change non-<code>final</code> to <code>final</code></td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (6)</td>
+
 
+
</tr>
+
 
+
<tr>
+
  <td rowspan="2">Add type parameter </td>
+
  <td>If class has no type parameters</td>
+
  <td>Binary compatible (7) </td>
+
</tr>
+
<tr>
+
  <td>If class has type parameters</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Delete type parameter </td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Re-order type parameters </td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Rename type parameter</td>
+
  <td>-</td>
+
  <td>Binary compatible</td>
+
</tr>
+
<tr>
+
  <td>Add, delete, or change type bounds of type parameter</td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
  <td>Rename enum constant</td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Add, change, or delete enum constant arguments</td>
+
  <td>-</td>
+
  <td>Binary compatible</td>
+
</tr>
+
<tr>
+
  <td>Add, change, or delete enum constant class body</td>
+
  <td>-</td>
+
  <td>Binary compatible</td>
+
</tr>
+
<tr>
+
  <td>Add enum constant</td>
+
  <td>-</td>
+
  <td>Binary compatible (8) </td>
+
</tr>
+
<tr>
+
  <td>Delete enum constant</td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Re-order enum constants</td>
+
  <td>-</td>
+
  <td>Binary compatible (8) </td>
+
</tr>
+
 
+
</table>
+
 
+
(0) Although adding a new method to an API class which need not be reimplemented by Clients does not break binary compatibility, a pre-existing subclass might still provide a pre-existing implementation of a method by this name. See [[#Example 4 - Adding an API method]] in the preceding section for why this breaks API contract compatibility.
+
 
+
(1) Adding a new method to an API class that must be reimplemented by Clients breaks compatibility because pre-existing subclasses would not provide any such implementation.
+
 
+
(2) Adding the first constructor to an API class causes the compiler to no longer generate a default (public, 0 argument) constructor, thereby breaking compatibility with pre-existing code that invoked this API constructor. To avoid this pitfall, API classes should always explicitly declare at least one constructor.
+
 
+
(3) Adding a new field to an API class that is subclassed by Clients breaks binary compatibility. A field in a superinterface of C may hide an added field inherited from a superclass of C, causing linking errors to be detected when a static field hides an instance field. Apart from the binary compatibility issues, it is generally good software engineering practice that API classes should not expose any fields.
+
 
+
(4) Shrinking an API class's set of API superclasses and superinterfaces (either directly or inherited) breaks compatibility because some casts in pre-existing Client code will now longer work. However, non-API superclasses and superinterfaces can be removed without breaking binary compatibility.
+
 
+
(5) Pre-existing binaries that attempt to create new instances of the API class will fail with a link-time or runtime error.
+
 
+
(6) Pre-existing binaries that subclass the API class will fail with a link-time error.
+
 
+
(7) Adding type parameters to an unparameterized type is a compatible change owing to Java's story for interfacing with non-generic legacy code.
+
 
+
(8) Client code can use the values() method to determine the ordinal positions of the enum constants. So although this is a binary compatible change, it may break contractual compatibility.
+
 
+
==== Evolving API classes - API methods and constructors ====
+
 
+
<table BORDER COLS=3 WIDTH="96%" >
+
<tr>
+
<td>Change body of method or constructor</td>
+
 
+
    <td width="20%">-</td>
+
 
+
    <td width="25%">Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change formal parameter name</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change method name</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>Add or delete formal parameter</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Change type of a formal parameter</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Change result type (including <code>void</code>)</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>Add checked exceptions thrown</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (1)</td>
+
</tr>
+
 
+
<tr>
+
<td>Add unchecked exceptions thrown</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete checked exceptions thrown</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (1)</td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>Delete unchecked exceptions thrown</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Re-order list of exceptions thrown</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Decrease access; that is, from <code>protected</code> access to default or <code>private</code> access; or from <code>public</code> access to <code>protected</code>, default, or <code>private</code> access</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Increase access; that is, from <code>protected</code> access to <code>public</code> access</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible (2)</td>
+
</tr>
+
 
+
<tr>
+
<td>Change <code>abstract</code> to non-<code>abstract</code></td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change non-<code>abstract</code> to <code>abstract</code></td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (3)</td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>Change <code>final</code> to non-<code>final</code></td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td ROWSPAN="2">Change non-<code>final</code> to <code>final</code></td>
+
 
+
<td>If method not reimplementable by Clients</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>If method reimplementable by Clients</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (4)</td>
+
</tr>
+
 
+
<tr>
+
<td>Change <code>static</code> to non-<code>static</code></td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Change non-<code>static</code> to <code>static</code></td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>Change <code>native</code> to non-<code>native</code></td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change non-<code>native</code> to <code>native</code></td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change <code>synchronized</code> to non-<code>synchronized</code></td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible (5)</td>
+
</tr>
+
 
+
<tr>
+
<td>Change non-<code>synchronized</code> to <code>synchronized</code></td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible (5)</td>
+
</tr>
+
 
+
<tr>
+
  <td rowspan="2">Add type parameter </td>
+
  <td>If method has no type parameters</td>
+
  <td>Binary compatible (6) </td>
+
</tr>
+
<tr>
+
  <td>If method has type parameters</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Delete type parameter </td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Re-order type parameters </td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Rename type parameter</td>
+
  <td>-</td>
+
  <td>Binary compatible</td>
+
</tr>
+
<tr>
+
  <td>Add, delete, or change type bounds of type parameter</td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b></td>
+
</tr>
+
<tr>
+
  <td>Change last parameter from array type <code>T[]</code> to variable
+
arity <code>T...</code></td>
+
  <td>-</td>
+
  <td>Binary compatible (7) </td>
+
</tr>
+
<tr>
+
  <td>Change last parameter from variable arity <code>T...</code> to array
+
type <code>T[]</code></td>
+
  <td>-</td>
+
  <td><b><font color="#ff0000">Breaks compatibility</font></b> (8)</td>
+
</tr>
+
 
+
</table>
+
 
+
(1) Adding and deleting checked exceptions declared as thrown by an API method does not break binary compatibility; however, it breaks contract compatibility (and source code compatibility).
+
 
+
(2) Perhaps surprisingly, the binary format is defined so that changing a member or constructor to be more accessible does not cause a linkage error when a subclass (already) defines a method to have less access.
+
 
+
(3) Pre-existing binaries that invoke the method will fail with a runtime error.
+
 
+
(4) Pre-existing binaries that reimplement the method will fail with a link-time error.
+
 
+
(5) Adding or removing the <code>synchronized</code> modifier also has a bearing on the method's behavior in a multi-threaded world, and may therefore raise a question of contract compatibility.
+
 
+
(6) Adding type parameters to an unparameterized type is a compatible change owing to Java's story for interfacing with non-generic legacy code.
+
 
+
(7) A variable arity method declaration such as "void foo(int x, String... y)" is compiled as if it had been written "void foo(int x, String[] y)".
+
 
+
(8) Although existing binaries will continue to work, existing invocations in source code may not compile because the compiler no longer automatically bundles up the extra arguments into an array.
+
 
+
==== Evolving API classes - API fields ====
+
 
+
Because of binary compatibility problems with fields, the Java Language
+
Specification recommends against using API fields. However, this is not
+
always possible; in particular, enumeration constants to be used in <code>switch</code>
+
statements must be defined as API constant fields.
+
<br>&nbsp;
+
<table BORDER COLS=3 WIDTH="100%" >
+
<tr>
+
<td>Change type of API field</td>
+
 
+
    <td width="30%">-</td>
+
 
+
    <td width="25%"><b><font color="#FF0000">Breaks compatibility</font></b> (1)</td>
+
 
+
</tr>
+
 
+
<tr>
+
<td ROWSPAN="2">Change value of API field</td>
+
 
+
<td>If field is compile-time constant</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (2)</td>
+
</tr>
+
 
+
<tr>
+
<td>If field is not compile-time constant</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Decrease access; that is, from <code>protected</code> access to default or <code>private</code> access; or from <code>public</code> access to <code>protected</code>, default, or <code>private</code> access</td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Increase access; that is, from <code>protected</code> access to <code>public</code>
+
access</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td ROWSPAN="3">Change <code>final</code> to non-<code>final</code></td>
+
 
+
<td>If field is non-static</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>If field is static with compile-time constant value</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (3)</td>
+
</tr>
+
 
+
<tr>
+
<td>If field is static with non-compile-time constant value</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change non-<code>final</code> to <code>final</code></td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (4)</td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>Change <code>static</code> to non-<code>static</code></td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (5)</td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>Change non-<code>static</code> to <code>static</code></td>
+
 
+
<td>-</td>
+
 
+
<td><b><font color="#FF0000">Breaks compatibility</font></b> (5)</td>
+
 
+
</tr>
+
 
+
<tr>
+
<td>Change <code>transient</code> to non-<code>transient</code></td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change non-<code>transient</code> to <code>transient</code></td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
</table>
+
 
+
(1) All field type changes break binary compatibility, even seemingly innocuous primitive type widenings link turning a <code>short</code> into an <code>int</code>.
+
 
+
(2) Java compilers always inline the value of constant fields (ones with compile-time computable values, whether primitive or <code>String</code> type). As a consequence, changing the value of an API constant field does not affect pre-existing Clients. Invariably, this does not meet the objective for changing the API field's value.
+
 
+
(3) Java compilers always inline the value of constant fields (ones with a compile-time computable values, whether primitive or <code>String</code>type). As a consequence, changing an API constant field into a non-<code>final</code> one does not propagate to pre-existing Clients. Invariably, this does not meet the objective for making the API field non-<code>final</code>.
+
 
+
(4) Making an API field final breaks compatibility with pre-existing binaries that attempt to assign new values to the field.
+
 
+
(5) Changing whether an API field is declared static or not results in link-time errors where the field is used by a pre-existing binary which expected a field of the other kind.
+
 
+
==== Evolving API classes - API type members ====
+
 
+
The rules for evolving an API type member are basically the same as for
+
API classes and interfaces declared at the package level, with these additional
+
rules for changing access modifiers:
+
<br>&nbsp;
+
<table BORDER COLS=3 WIDTH="100%" >
+
<tr>
+
<td>Decrease access; that is, from <code>protected</code> access to default or <code>private</code> access; or from <code>public</code> access to <code>protected</code>, default, or <code>private</code> access</td>
+
 
+
<td width="20%">-</td>
+
 
+
<td width="20%"><b><font color="#FF0000">Breaks compatibility</font></b></td>
+
</tr>
+
 
+
<tr>
+
<td>Increase access; that is, from <code>protected</code> access to <code>public</code>
+
access</td>
+
 
+
<td>-</td>
+
 
+
<td>Binary compatible</td>
+
</tr>
+
</table>
+
 
+
=== Evolving non-API packages ===
+
 
+
The names of non-API packages, classes, and interfaces do not appear in Client source code or binaries. Consequently, non-API packages, classes, and interfaces can be added or deleted without jeopardizing binary compatibility. However, when non-API classes and interfaces containing <code>public</code> or <code>protected</code> members are among the superclass or superinterface sets of API classes and interfaces, non-API changes may have ramifications to API methods, fields, and constructors.
+
 
+
<table BORDER COLS=3 WIDTH="100%" >
+
<tr>
+
<td>Add non-API package</td>
+
<td width="20%">-</td>
+
<td width="20%">Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete non-API package</td>
+
<td>-</td>
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Add class or interface to non-API package</td>
+
<td>-</td>
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Delete class or interface in a non-API package</td>
+
<td>-</td>
+
<td>Binary compatible</td>
+
</tr>
+
 
+
<tr>
+
<td>Change existing class or interface in non-API package</td>
+
<td>-</td>
+
<td>Binary compatible</td>
+
</tr>
+
</table>
+
 
+
=== Turning non-generic types and methods into generic ones ===
+
 
+
Generic types and methods were added to the Java language in Java SE 5 (aka JDK 1.5) along with a special story for how legacy non-generic code can continue to use types and methods that have been &quot;generified&quot;. The prime example of this is the java.util Collections Framework, which was upgraded to make use of generics while remaining compatible with code that uses collections in the old way.
+
 
+
The key concepts behind Java's special compatibility mechanism are raw types and erasures.
+
 
+
A <em>raw type</em> is a use of a generic type without the normal type arguments.
+
For example, &quot;<code>List</code>&quot; in the declaration statement &quot;<code>List
+
x = null;</code>&quot; is a raw type since List is a generic type declared &quot;<code>public
+
interface List&lt;E&gt; </code>...&quot;
+
in JDK 1.5. Contrast this to a normal use of List which looks like &quot;<code>List&lt;String&gt; x
+
= null;</code>&quot; or &quot;<code>List&lt;?&gt;
+
x = null;</code>&quot; where a type augument (&quot;<code>String</code>&quot;)
+
or wildcard is specified.
+
 
+
The term <em>erasure</em> is suggestive. Imagine going through your code and
+
literally erasing the type parameters from the generic type declaration (e.g.,
+
erasing the &quot;<code>&lt;E&gt;</code>&quot; in
+
&quot;<code>public interface List&lt;E&gt; </code>...&quot;) to get a non-generic
+
type declaration, and replacing all occurrence of the deleted type variable
+
with <code>Object</code>. For type parameters with type bounds (e.g., &quot;<code>&lt;E
+
extends T1 &amp; T2 &amp; T3
+
&amp; ...&gt;</code>&quot;), the leftmost type bound (<code>&quot;T1</code>&quot;),
+
rather than <code>Object</code>, is substituted for the type variable. The
+
resulting declaration is known as the erasure.
+
 
+
According to the special compatibility story, the Java compiler treats a raw
+
type as a reference to the type's erasure. An existing type can be evolved
+
into a generic type by adding type parameters to the type declaration and judiciously
+
introducing uses of the type variables into the signatures of its existing
+
methods and fields. As long as the erasure looks like the corresponding declaration
+
prior to generification, the change is binary compatible with existing code.
+
 
+
As a case study of how to generify an existing API, carefully compare the Java 1.4 Collections Framework[http://java.sun.com/j2se/1.4.2/docs/api/java/util/Collection.html] classes with their counterparts in the Java 1.5 Collections Framework[http://java.sun.com/j2se/1.5.0/docs/api/java/util/Collection.html]. You will see that the erasures of the 1.5 versions looks just like the 1.4 versions.
+
 
+
Variable arity methods were also introduced in 1.5, also with a special story
+
for how legacy code can continue to invoke or override a method that has been
+
upgraded. A variable arity method declaration such as &quot;<code>void foo(int
+
x, String... y)</code>&quot; is compiled as if it had been written &quot;<code>void
+
foo(int x, String[] y)</code>&quot;. This provides a consistent interpretation
+
for old-style invocations of the form
+
&quot;<code>foo(int 5, new String[]{&quot;a&quot;,&quot;b&quot;})</code>&quot;.
+
The class Arrays.asList[http://java.sun.com/j2se/1.5.0/docs/api/java/util/Arrays.html#asList(T...)] is
+
an example of a method that became both generic and variable arity in 1.5.
+
 
+
Two final notes. Regarding whether existing APIs ought to embrace generics,
+
the Java Language Specification says only this:</p>
+
<blockquote>
+
  <p>&quot;The use of raw types is allowed only as a concession to compatibility
+
    of legacy code. The use of raw types in code written after the introduction
+
    of genericity into the Java programming language is strongly discouraged. <em><strong>It
+
    is possible that future versions of the Java programming language will disallow
+
    the use of raw types.</strong></em>&quot; (JLS3,
+
    4.8[http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.8])</p>
+
</blockquote>
+
 
+
The implication is that code that uses the Collections Framework should use
+
types like &quot;<code>List&lt;?&gt;</code>&quot;  instead of the raw type &quot;<code>List</code>&quot;.
+
 
+
But, also bear in mind that there are severe constraints on how a type or method
+
that already is generic can be compatibly evolved with respect to its type
+
parameters (see the tables above). So if you plan to generify an API, remember
+
that you only get one chance (release), to get it right. In particular, if
+
you change a type in an API signature from the raw type &quot;<code>List</code>&quot; to &quot;<code>List&lt;?&gt;</code>&quot; or &quot;<code>List&lt;Object&gt;</code>&quot;,
+
you will be locked into that decision. The moral is that generifying an existing
+
API is something that should be considered from the perspective of the API
+
as a whole rather than piecemeal on a method-by-method or class-by-class basis.
+
 
+
=== Evolving annotations on API elements ===
+
 
+
Annotations were added to the Java language in Java SE 5 (aka JDK 1.5). Annotation can be used on most any named Java element, including packages, types, methods, and fields. It's a natural question to ask whether adding, deleting, or changing an annotation on a API element is a compatible or a breaking change.
+
 
+
On one hand, adding or removing annotations has no effect on the correct linkage of class files by the Java virtual machine. On the other hand, annotations exist to be read via reflective APIs for manipulating annotations. So there is no uniform answer as to what will happen if a given annotation is or is not present on an API element (or non-API element, for that matter). It depends entirely on the specifics of the annotation and the mechanisms that are processing those annotations.
+
 
+
Parties that declare annotation types should try to provide helpful guidance for their customers.
+
 
+
== Data Compatibility ==
+
The Component implementation may need to store and retrieve its internal
+
data from a file. For example, Microsoft Word stores a document in a file. When one
+
of these files may live from release to release, clients would break if
+
the format or interpretation of that data changed in an incompatible way.
+
<br><b>Data compatibility</b> is an additional issue for components with
+
persistent data.
+
<p>The standard technique is to tag all stored data with its format version
+
number. The format version number is increased when the format is changed
+
from one release to the next. The Component implementation contains readers
+
for the current format version and for all past versions, but usually only
+
the writer for the current format version (unless for some reason there
+
is an ongoing need to write older versions).
+
 
+
== Standard Workarounds ==
+
 
+
When evolving APIs, the prime directive places serious constraints on how
+
this can be done.
+
 
+
Here are some standard techniques that come in handy when you're caught
+
between a rock and a hard place. They're not necessarily pretty, but they
+
get the job done.
+
 
+
=== Deprecate and Forward ===
+
 
+
When some part of the Component API is made obsolete by some new and improved
+
Component API, the old API should be marked as deprecated using the <code>@deprecated</code>
+
Javadoc tag (the comment directing the reader attention to the replacement
+
API). When feasible, the implementation of the old API should forward the
+
message to the corresponding method in the replacement API; doing so will
+
mean that any performance improvements or bug fixes made to the implementation
+
of the new API will automatically be of benefit to clients of the old API.
+
 
+
=== Start over in a New Package ===
+
 
+
Even simpler than Deprecate and Forward, the Component API and implementation
+
can be redone in new packages. The old API and implementation are left
+
in the old location untouched, except to mark them as deprecated. Old and
+
new API and implementations co-exist independent of one another.
+
 
+
=== Adding an argument ===
+
 
+
Here is a simple technique for adding an argument to a method that is intended to be overridden by subclasses. For example the <code>Viewer.inputChanged(Object input)</code> method should get an additional argument <code>Object oldInput</code>. Adding the argument results in pre-existing clients overridding the wrong method. The workaround is to call the old method as the default implementation
+
of the new method:
+
 
+
<pre>
+
public void inputChanged(Object input, Object oldInput) {
+
  inputChanged(input);
+
}
+
</pre>
+
 
+
Pre-existing clients which override the old method continue to work; and all calls to the old method continue to work New or upgraded clients will override the new method; and all calls to the new method will work, even if they happen to invoke an old implementation.
+
 
+
=== "2" Convention ===
+
The first release of an API callback-style interface didn't work as well as hoped. For example, the first release contained:
+
 
+
<pre>
+
public interface IProgressMonitor {
+
  void start();
+
  void stop();
+
}
+
</pre>
+
 
+
You now wish you had something like:
+
 
+
<pre>
+
public interface IProgressMonitor {
+
  void start(int total);
+
  void worked(int units);
+
  void stop();
+
}
+
</pre>
+
 
+
But it's too late to change <code>IProgressMonitor</code> to be that API. So you mark <code>IProgressMonitor</code> as deprecated and introduce the new and improved one under the name <code>IProgressMonitor2</code> (a name everyone recognizes as the second attempt):
+
 
+
<pre>
+
public interface IProgressMonitor2 extends IProgressMonitor {
+
  void start(int total);
+
  void worked(int units);
+
  void stop();
+
}
+
</pre>
+
 
+
By declaring the new interface to extend the old one, any object of type <code>IProgressMonitor2</code> can be passed to a method expecting an old <code>IProgressMonitor</code>.
+
 
+
=== COM Style ===
+
 
+
The "COM style" is to not implement interfaces directly but to ask for an interface
+
by using <code>getAdapter(someInterfaceID)</code>. This allows adding new interfaces
+
in the implementation without breaking existing classes.
+
 
+
=== Making Obsolete Hook Methods Final ===
+
 
+
As a framework evolves, it may sometimes be necessary to break compatibility.
+
When compatibility is being broken knowingly, there are some tricks that
+
make it easier for broken clients to find and fix the breakage.
+
 
+
A common situation occurs when the signature of a framework hook method
+
is changed. Overridding a hook method that is no longer called by the base
+
class can be tough to track down, especially if the base class contains
+
a default implementation of the hook method. In order to make this jump
+
out, the obsolete method should be marked as <code>final</code> in addition
+
to being deprecated. This ensures that existing subclasses which override
+
the obsolete method will no longer compile or link.
+
 
+
== Defective API Specifications ==
+
 
+
As hard as one might try, achieving perfect APIs is difficult. The harsh
+
reality is that some parts of large Component API will be specified better
+
than others.
+
 
+
One problem is specification bugs---when the API spec actually says
+
the wrong thing. Every effort should be made to catch these prior to release.
+
 
+
Another problem is underspecification---when the API spec does not specify
+
enough. In some cases, the implementor will notice this before the API
+
is ever released. In other cases, the specification will be adequate for
+
the implementor's needs but inadequate for clients. When an API is released
+
in advance of serious usage from real clients, it may be discovered too
+
late that the specification should have been tighter or, even worse, that
+
the API should have been designed differently.
+
 
+
When you find out that you're saddled with a defective API specification,
+
these points are worth bearing in mind:
+
<ul>
+
<li>
+
APIs are not sacrosanct; it's just that breaking compatibility is usually
+
very costly. For a truly unusable feature, the cost is likely much lower.</li>
+
 
+
<li>
+
Tightening up a seriously weak specification can often be achieved without
+
breaking compatibility by changing the specification in a way consistent
+
with the existing implementation. That is, codify more of how it actually
+
works to ensure that clients that currently work continue to work in subsequent
+
releases.</li>
+
 
+
<li>
+
Breaking compatibility in a limited way may be cheaper in the long run
+
that leaving a bad patch of API as it is.</li>
+
 
+
<li>
+
If you break compatibility between releases, do it in a controlled way
+
that only breaks those Clients that actually utilize of the bad parts of
+
the API. This localizes the pain to affected Clients (and their downstream
+
customers), rather than foisting a "Big Bang" release on everyone.</li>
+
 
+
<li>
+
Document all breaking API changes in the release notes. Clients appreciate this
+
much more than discovering for themselves that you knowingly broke them.</li>
+
</ul>
+
 
+
== A Word about Source Code Incompatibilities ==
+
 
+
While the idea that the Java source code for existing Clients should continue to compile without errors against the revised Component API, this is not strictly necessary (and not always achievable). API contract and binary compatibility are the only hard requirements. Source code incompatibilities are not worth losing sleep over because the Client's owner can easily correct these problems if they do arise with only localized editing of the source code.
+
 
+
The following is a list of known kinds of Java source code incompatibilities that can arise as APIs evolve:
+
<ul>
+
<li>
+
Ambiguities involving type-import-on-demand declarations.</li>
+
 
+
<ul>
+
 
+
<li>
+
Triggered by: adding an API class or interface.</li>
+
 
+
<li>
+
Remedy: add single type import declaration to disambiguate.</li>
+
 
+
<li>
+
Avoidance strategy: use at most one type-import-on-demand declaration per
+
compilation unit.</li>
+
</ul>
+
 
+
<li>
+
Ambiguities involving overloaded methods.</li>
+
 
+
<ul>
+
<li>
+
Triggered by: adding an overloaded API method or constructor.</li>
+
 
+
<li>
+
Remedy: add casts to disambiguate ambiguously typed arguments.</li>
+
 
+
<li>
+
Avoidance strategy: put casts on null arguments.</li>
+
</ul>
+
 
+
<li>
+
Ambiguities involving field and type member hiding.</li>
+
 
+
<ul>
+
<li>
+
Triggered by: adding an API field.</li>
+
 
+
<li>
+
Remedy: add qualification to disambiguate ambiguous field references.</li>
+
 
+
<li>
+
Avoidance strategy: none.</li>
+
</ul>
+
 
+
<li>
+
Ambiguities involving fields and local variables.</li>
+
 
+
<ul>
+
<li>
+
Triggered by: adding an API field.</li>
+
 
+
<li>
+
Remedy: rename conflicting local variables to avoid new field name.</li>
+
 
+
<li>
+
Avoidance strategy: don't declared API fields in classes and interfaces
+
that Clients implement.</li>
+
</ul>
+
 
+
<li>
+
Problems involving checked exceptions thrown by methods.</li>
+
<ul>
+
<li>
+
Triggered by: removing checked exceptions from a method's <code>throws</code>
+
clause.</li>
+
<li>
+
Remedy: add or remove exception handlers as required.</li>
+
<li>
+
Avoidance strategy: none.</li>
+
</ul>
+
</ul>
+
  
Copyright © 2000, 2007 IBM Corporation
+
Copyright © 2000, 2009 IBM Corporation
  
 
[[Category:API]]
 
[[Category:API]]
 +
[[Category:Best Practices]]

Revision as of 07:46, 22 October 2014

By Jim des Rivières, IBM

Revision history:

October 1, 2007 - revision 1.2 - Clarified moving methods up and down type hierarchy; added note about Java reflection; split document into 3 parts because it was getting too large to edit:
February 14, 2007 - revision 1.1 - Add coverage for JDK 1.5 language features: generics, enums, annotation types, variable arity methods.
June 8, 2001 - revision 1.02 - Added note about breakage due to adding API method to classes that may be subclassed.
January 15, 2001 - revision 1.01 - Added suggestion about making obsolete hook methods final.
October 6, 2000 - revision 1.0

This document is about how to evolve Java-based APIs while maintaining compatibility with existing client code. Without loss of generality, we'll assume that there is a generic Component with a Component API, with one party providing the Component and controlling its API. The other party, or parties, write Client code that use the Component's services through its API. This is a very typical arrangement.

API Java Elements

All parties need to understand which Java elements (packages, interfaces, classes, methods, constructors, and fields) are part of the Component API, which Clients may use, and those which are part of the internal Component implementation and are off limits to Clients. Having a clearly-defined and marked boundary between API and non-API is therefore very important. The following convention uses the visibility-limiting features of the Java language to distinguish those Java elements which are considered API from those which are not:

  • API package - a package that contains at least one API class or API interface. The names of API packages are advertised in the Component documentation. These names will appear in Client code; the names of non-API packages should never appear in Client code. Note that Clients must be prohibited from declaring their code in Component packages (API or otherwise).
  • API class - a public class in an API package, or a public or protected class member declared in, or inherited by, some other API class or interface. The names of API classes appear in Client code.
  • API interface - a public interface in an API package, or a public or protected interface member declared in, or inherited by, some other API class or interface. The names of API interfaces appear in Client code.
  • API method - a public or protected method either declared in, or inherited by, an API class or interface. The names of API methods appear in Client code.
  • API constructor - a public or protected constructor of an API class. The names of API constructors appear in Client code.
  • API field - a public or protected field either declared in, or inherited by, an API class or interface. The names of API fields appear in Client code.

The following elements are not considered API:

  • Any package that is not advertised in the Component documentation as an API package.
  • All classes and interfaces declared in non-API packages. However, when API classes and interface extend or implement non-API classes, the non-API classes and interface may contribute API elements nevertheless.
  • Non-public classes and interfaces in API packages.
  • Default access and private methods, constructors, fields, and type members declared in, or inherited by, API classes and interfaces.
  • protected methods, constructors, fields, and type members declared in, or inherited by, final API classes, including enums (which are implicitly final). With no ability to declare subclasses, these cannot be referenced by Client code.

API Prime Directive

As the Component evolves from release to release, there is an absolute requirement to not break existing Clients that were written in conformance to Component APIs in an earlier release.

API Prime Directive: When evolving the Component API from release to release, do not break existing Clients.

Changing an API in a way that is incompatible with existing Clients would mean that all Clients would need to be revised (and even in instances where no actual changes are required, the Client code would still need to be reviewed to ensure that it still works with the revised API). Customers upgrading to a new release of the Component would need to upgrade all their Clients at the same time. Since the overall cost of invalidating existing Client code is usually very high, the more realistic approach is to only change the API in ways that do not invalidate existing Clients.

As the Component API evolves, all pre-existing Clients are expected to continue to work, both in principle and in practice.

Suppose a Client was written to a given release of the Component and abided by the contracts spelled out in the Component API specification.
The first requirement is that when the Component API evolves to follow-on releases, all pre-existing Client must still be legal according to the contracts spelled out in the revised Component API specification, without having to change the Clients source code. This is what is meant by continuing to work in principle.

API Contract Compatibility: API changes must not invalidate formerly legal Client code.

Since the set of Clients is open-ended, and we have no way of knowing exactly which aspects of the API are being counted on, the only safe assumption to make when evolving APIs is that every aspect of the API matters to some hypothetical Client, and that any incompatible change to the API contract will cause that hypothetical Client to fail.

API Usage Assumption: Every aspect of the API matters to some Client.

Under this assumption, deleting something in the API, or backtracking on some promise made in the API, will certainly break some Client. For this reason, obsolete API elements are notoriously difficult to get rid of. Obsolete API elements should be marked as deprecated and point new customers at the new API that replaces it, but need to continue working as advertised for a couple more releases until the expense of breakage is low enough that it can be deleted.

Clients are generally written in Java and are compiled to standard Java binary class files. A Client's class files are typically stored in a JAR file on the Client's library path. It would be unsatisfactory if a Client's class files, which were compiled against one release of the Component, do not successfully link and execute correctly with all later releases of the Component. This is what is meant by continuing to work in practice.

API Binary Compatibility: Pre-existing Client binaries must link and run with new releases of the Component without recompiling.

Achieving API binary compatibility requires being sensitive to the Java language's notion of binary compatibility JLS3, chapter 13.

While the idea that the Java source code for existing Clients should continue to compile without errors against the revised Component API, this is not strictly necessary (and not always achievable). For instance, adding a new public interface to an existing API package may introduce an ambiguous package reference into source code containing multiple on-demand type (".*") imports. Similarly, removing a method throws declaration for a checked exception may cause the compiler to detect dead code in a try-catch block. Happily, the kinds of problems that could be introduced into Client source code can always be easily corrected. The notion of API source compatibility is not a requirement. (Note: Problems detected by a Java compiler are therefore not necessarily indicators of any kind of API compatibility that we care about.)

The following sections discuss how API contract and binary compatibility can be achieved.

Achieving API Contract Compatibility

"How could I have broken anything? All I did was change a comment."

Since API contracts are captured by the API specification, any change to the API specification risks making code written against the old specification incompatible with the revised specification.

The most confining situation is an API that is specified by one party, implemented by a separate second party, and used by yet a different third party. For example, a standards body promulgates a pure specification (such as the HTTP protocol) but leaves it up to others to write browsers and servers. In such cases, making any changes to the existing specification will almost certainly break client code, implementations, or both.

Fortunately, the case is typically lop-sided. Most commonly, the party responsible for specifying the API also provides the sole implementation. Indeed, this is our earlier assumption about the Component. In this situation, the API owner can unilaterally decide to change the API specification and fix up the implementation to match. However, since they can't do anything about the client code already using the API, the changed API must be contract compatible with the old API: all existing contractual obligations must be honored. Contracts can be tightened to allow users to assume more (and require the implementation to do more); this does not invalidate existing code which would have been written assuming less. Conversely, contracts cannot be loosened to require users to assume less, as this could break existing uses.

Note that in some cases, the contractual roles are reversed. The party responsible for specifying the API provides the uses, whereas other parties provide the implementations. Callback interfaces are a prime example of this situation. Contracts can be loosened to require implementors to provide less (and allow the client to assume less); this does not invalidate existing implementations which would have been written under more stringent rules. Conversely, contracts cannot be tightened to require implementors to provide more, as this could break existing implementations.

When contemplating changing an existing API contract, the key questions to ask are:

  • What roles does the API contract involve?
    For a method contract, there is the caller and the implementor. In the case of frameworks, there is also an additional contract between superclass and subclass regarding default behavior, extending, and overriding.
  • Which role or roles will each party play?
    For many Component API methods, the Component plays the role of exclusive implementor and the Client plays the role of caller. In the case of Component callbacks, the Component plays the caller role and the Client plays the implementor role. In some cases, the Client might play more than one role.
  • Is a role played exclusively by the Component?
    Component API changes coincide with Component releases, making it feasible to change Component code to accommodate the changed APIs.
  • For roles played by Clients, would the contemplated API change render invalid a hypothetical Client making legal usage of the existing API?

The following examples illustrate how this analysis is done.

Example 1 - Changing a method postcondition

Standard method contracts have two roles: caller and implementor. Method postconditions are those things that an implementor must arrange to be true before returning from the method, and that a caller may presume to be true after the return. This first example involves a change to a postcondition.

Consider the following API method specification:

/** Returns the list of children of this widget.
 * @return a non-empty list of widgets
 */
Widget[] getChildren();

The contemplated API change is to allow the empty list of widgets to be returned as well, as captured by this revised specification:

/** Returns the list of children of this widget.
 * @return a list of widgets
 */
Widget[] getChildren();

Would this change break compatibility with existing Clients? It depends on the role played by the Client.

Looking at the caller role, this change would break a hypothetical pre-existing caller that legitimately counts on the result being non-empty. The relevant snippet from this hypothetical caller might read:

Widget[] children = widget.getChildren();
Widget firstChild = children[0];

Under the revised contract, this code would be seen to be in error because it assumes that the result of invoking getChildren is non-empty; under the previous contract, this assumption was just fine. This API change weakens a postcondition for the caller, and is not contract compatible for the caller role. The contemplated change would break Clients playing the caller role.

Looking at the implementor role, this change would not break a hypothetical pre-existing implementor which never return empty results anyway. Weakening a method postcondition is contract compatible for the implementor role. The contemplated change would not break Clients playing the implementor role.

So the answer as to whether this change breaks compatibility with existing Clients hinges on which role(s) the Client plays.

Another form of postcondition change is changing the set of checked exceptions that a method throws.

Example 2 - Changing a method precondition

Method preconditions are those things that a caller must arrange to be true before calling the method, and that an implementor may presume to be true on entry. This second example involves a change to a precondition.

Consider the following API method specification:

/** Removes the given widgets from this widget's list of children.
 * @param widgets a non-empty list of widgets
 */
void remove(Widget[] widgets);

The contemplated API change is to allow empty lists of widgets to be passed in as well:

/** Removes the given widgets from this widget's list of children.
 * @param widgets a list of widgets
 */
void remove(Widget[] widgets);

Would this change break compatibility with existing Clients? Again, it hinges on the role played by the Client.

Looking at the caller role, this change would not break hypothetical pre-existing callers since they pass in non-empty lists. However, this change would break a hypothetical pre-existing implementations that legitimately assumed that the argument is not empty.

The relevant snippet from this hypothetical implementor might read:

Widget firstChild = widgets[0];

Under the revised contract, this code would be seen to be in error because it assumes that the argument is non-empty; under the previous contract, this assumption was just fine. This API change weakens a method precondition, and is not contract compatible for the implementor role. The contemplated change would break Clients that implement this method.

Example 3 - Changing a field invariant

Fields can be analyzed as having two roles: a getter and a setter. The Java language does not separate these roles particularly, but it does have the notion of final fields which eliminates setters from the equation. (Perhaps a better way to divvy this up is to say that there is a getter role and a getter/setter role.)  The API specification for a field is usually in the form of an invariant that holds for the lifetime of the field.

Consider the following API field specification:

/** This widget's list of children, or <code>null</code>.
 */
Widget[] children;

The contemplated API change is to get rid of the possibility of the null value:

/** This widget's list of children.
 */
Widget[] children;

Would this change break compatibility with existing Clients?

This change would break a hypothetical pre-existing setter that legitimately sets the field to null. On the other hand, it would not break a hypothetical pre-existing getter that legitimately had to assume that the field could be null. This API change weakens a field invariant, and is not contract compatible for the setter role.

Example 4 - Adding an API method

Can adding an API method to a class or interface break compatibility with existing Clients?

If the method is added to an interface which Clients may implement, then it is definitely a breaking change.

If the method is added to a class (interface) which Clients are not allowed to subclass (to implement), then it is not a breaking change.

However, if the method is added to a class which Clients may subclass, then the change should ordinarily be viewed as a breaking change. The reason for this harsh conclusion is because of the possibility that a Client's subclass already has its own implementation of a method by that name. Adding the API method to the superclass undercuts the Client's code since it would be sheer coincidence if the Client's existing method met the API contract of the newly added method. In practice, if the likelihood of this kind of name coincidence is sufficiently low, this kind of change is often treated as if it were non-breaking.

General Rules for Contract Compatibility

Whether a particular Component API change breaks or maintains contract compatibility with hypothetical pre-existing Clients hinges on which role, or roles, the Client plays in the API contract(s) being changed. The following table summarizes the pattern seen in the above examples:
 

Method preconditions Strengthen Breaks compatibility for callers Contract compatible for implementors
Weaken Contract compatible for callers Breaks compatibility for implementors
Method postconditions Strengthen Contract compatible for callers Breaks compatibility for implementors
Weaken Breaks compatibility for callers Contract compatible for implementors
Field invariants Strengthen Contract compatible for getters Breaks compatibility for setters
Weaken Breaks compatibility for getters Contract compatible for setters

Achieving API Binary Compatibility

See Part 2.

Other Notes

See Part 3.

Copyright © 2000, 2009 IBM Corporation

Back to the top