Eclipse platform
API rules of engagement
Version 0.15 - Last revised 12:00 May 30, 2001
Here are the rules of engagement for clients of the Eclipse platform
API (and other components).
What it means to be API
The Eclipse platform defines API elements for use by its clients, namely
ISVs writing plug-ins. These plug-ins may in turn define API elements for
their clients, and so on. API elements are the public face: they carry
a specification about what they are supposed to do, and about how they
are intended to be used. API elements are supported: the Eclipse platform
team will fix implementation bugs where there is a deviation from the specified
behavior. Since there is often a high cost associated with breaking API
changes, the Eclipse platform team will also try to evolve API elements
gracefully through successive major releases.
How to tell API from non-API
By their very nature, API elements are documented and have a specification,
in contrast to non-API elements which are internal implementation details
usually without published documentation or specifications. So if you cannot
find the documentation for something, that's usually a good indicator that
it's not API.
To try to draw the line more starkly, the code base for the platform
is separated into API and non-API packages, with all API elements being
declared in designated API packages.
-
API package - a Java package that contains at least one API class
or API interface. The names of API packages are advertised in the documentation
for that component; where feasible, all other packages containing only
implementation details have "internal" in the package name. The names of
API packages may legitimately appear in client code. For the Eclipse platform
proper, these are:
-
org.eclipse.foo.* - for example, org.eclipse.swt.widgets,
org.eclipse.ui,
or org.eclipse.core.runtime
-
org.eclipse.foo.internal.* - not API; internal implementation
packages
-
org.eclipse.foo.examples.* - not API; these are examples
-
org.eclipse.foo.tests.* - not API; these are test suites
-
API class or interface - a public class or interface in
an API package, or a public or protected class or interface
member declared in, or inherited by, some other API class or interface.
The names of API classes and interfaces may legitimately appear in client
code.
-
API method or constructor - a public or protected
method or constructor either declared in, or inherited by, an API class
or interface. The names of API methods may legitimately 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 may legitimately appear in client code.
Everything else is considered internal implementation detail and off limits
to all clients. Legitimate client code must never reference the names of
non-API elements (not even using Java reflection). In some cases, the Java
language's name accessibility rules are used to disallow illegal references.
However, there are many cases where this is simply not possible. Observing
this one simple rule avoids the problem completely:
-
Stick to officially documented APIs. Only reference packages that
are documented in the published API Javadoc for the component.
Never reference a package belonging to another component that has "internal"
in its name---these are never API. Never reference a package for which
there is no published API Javadoc---these are not API either.
General rules
The specification of API elements is generated from Javadoc comments in
the element's Java source code. For some types of elements, the specification
is in the form of a contract. For example, in the case of methods, the
contract is between two parties, the caller of the method and the implementor
of the method. The fundamental ground rule is:
-
Honor all contracts. The contracts are described in the published
Javadoc for the API elements you are using.
The term "must", when used in an API contract, means that it is incumbent
on the party to ensure that the condition would always be met; any failure
to do so would be considered a programming error with unspecified (and
perhaps unpredictable) consequences.
-
You must honor "must". Pay especially close heed to conditions where
"must" is used.
Other common sense rules:
-
Do not rely on incidental behavior. Incidental behavior is behavior
observed by experiment or in practice, but which is not guaranteed by any
API specification.
-
Do not treat null as an object. Null is more the lack of an object.
Assume everything is non-null unless the API specification says otherwise.
-
Do not try to cheat with Java reflection. Using Java reflection
to circumvent Java compiler checking buys you nothing more. There are no
additional API contracts for uses of reflection; reflection simply increases
the likelihood of relying on unspecified behavior and internal implementation
detail.
-
Use your own packages. Do not declare code in a package belonging
to another component. Always declare your own code in your own packages.
Calling public API methods
For most clients, the bulk of the Eclipse API takes the form of public
methods on API interfaces or classes, provided for the client to call when
appropriate.
-
Ensure preconditions. Do ensure that an API method's preconditions
are met before calling the method. Conversely, the caller may safely assume
that the method's postconditions will have been achieved immediately upon
return from the call.
-
Null parameters. Do not pass null as a parameter to an API method
unless the parameter is explicitly documented as allowing null. This is
perhaps the most frequently made programming error.
-
Restricted callers. Do not call an API method that is documented
as available only to certain callers unless you're one of them. In some
situations, methods need to be part of the public API for the benefit of
a certain class of callers (often internal); calling one of these methods
at the wrong time has unspecified (and perhaps unpredictable) consequences.
-
Debugging methods. Do not call an API method labelled "for debugging
purposes only". For example, most toString() methods are in this
category.
-
Parameter capture. Do not pass an array, collection, or other mutable
object as a parameter to an API method and then modify the object passed
in. This is just asking for trouble.
Instantiating platform API classes
Not all concrete API classes are intended to be instantiated by just anyone.
API classes have an instantiation contract indicating the terms under which
instances may be created. The contract may also cover things like residual
initialization responsibilities (for example, configuring a certain property
before the instance is fully active) and associated lifecycle responsibilities
(for example, calling dispose() to free up OS resources hung on
to by the instance). Classes that are designed to be instantiated by clients
are explicitly flagged in the Javadoc class comment (with words like "Clients
may instantiate.").
-
Restricted instantiators. Do not instantiate an API class that is
documented as available only to certain parties unless you're one of them.
In some situations, classes need to be part of the public API for the benefit
of a certain party (often internal); instantiating one of these classes
incorrectly has unspecified (and perhaps unpredictable) consequences.
Subclassing platform API classes
Only a subset of the API classes were designed to be subclassed. API classes
have a subclass contract indicating the terms under which subclasses may
be declared. This contract also covers initialization responsibilities
and lifecycle responsibilities. Classes that are designed to be subclassed
by clients are explicitly flagged in the Javadoc class comment (with words
like "Clients may subclass.").
-
Restricted subclassers. Do not subclass an API class that is not
intended to be subclassed. Treat these classes as if they had been declared
final. (These are sometimes referred to as "soft final" classes).
Calling protected API methods
Calling inherited protected and public methods from within a subclass is
generally allowed; however, this often requires more care to correctly
call than to call public methods from outside the hierarchy.
Overriding API methods
Only a subset of the public and protected API methods were designed to
be overridden. Each API method has a subclass contract indicating the terms
under which a subclass may override it. By default, overriding is not permitted.
It is important to check the subclass contract on the actual method implementation
being overridden; the terms of subclass contracts are not automatically
passed along when that method is overridden.
-
Do not override a public or protected API method unless it is explicitly
allowed. Unless otherwise indicated, treat all methods as if they had
been declared final. (These are sometimes known as "soft final" methods).
If the kind of overriding allowed is:
- "implement" - the abstract method declared on the subclass must
be implemented by a concrete subclass
- "extend" - the method declared on the subclass must invoke the
method on the superclass (exactly once)
- "re-implement" - the method declared on the subclass must not
invoke the method on the superclass
- "override" - the method declared on the subclass is free to
invoke the method on the superclass as it sees fit
-
Ensure postconditions. Do ensure that any postconditions specified
for the API method are met by the implementation upon return.
-
Proactively check preconditions. Do not presume that preconditions
specified for the API method have necessarily been met upon entry. Although
the method implementation would be within its rights to not check specified
preconditions, it is usually a good idea to check preconditions (when feasible
and reasonably inexpensive) in order to blow the whistle on misbehaving
callers.
-
Null result. Do not return null as a result from an API method unless
the result is explicitly documented (on the specifying interface or superclass)
as allowing null.
-
Return copies. Do not return an irreplaceable array, collection,
or other mutable object as the result from an API method. Always return
a copy to avoid trouble from callers that might modify the object.
Implementing platform API interfaces
Only a subset of the API interfaces were designed to be implemented by
clients. API interfaces have a contract indicating the terms under which
it may be implemented. Interfaces that are designed to be implemented by
clients are explicitly flagged in the Javadoc class comment (with words
like "Clients may implement."). A client may declare a subinterface of
an API interface if and only if they are allowed to implement it.
-
Restricted implementors. Do not implement an API interface that
is documented as available only to certain parties unless you're one of
them. In many situations, interfaces are used to hide internal implementation
details from view.
Implementing public API methods
See "Overriding API methods".
Accessing fields in API classes and interfaces
Clients may read API fields, most of which are final. Certain struct-like
objects may have non-final public fields, which clients may read and write
unless otherwise indicated.
-
Null fields. Do not set an API field to null unless this is explicitly
allowed.
Casting objects of a known API type
An object of a known API type may only be cast to a different API type
(or conditionally cast using instanceof) if this is explicitly allowed
in the API.
-
Cast and instanceof. Do not use instanceof and cast expressions
to increase what is known about an object beyond what the API supports.
Improper use exposes incidental implementation details not guaranteed by
the API.
And, of course, casting any object to a non-API class or interface is always
inappropriate.
Not following the rules
Whether done knowingly or unwittingly, there are consequences for transgressing
the rules. It might be easier for all involved if there were API police
that would bust you for breaking the rules. However, that is not the case.
For the most part, API conformance operates as an honor system, with each
client responsible for knowing the rules and adhering to them.
The contracts on the API elements delimit the behavior that is supported
and sustained. As the Eclipse platform matures and evolves, it will be
the API contracts that guide how this evolution happens. Outside of these
contracts, everything is unsupported and subject to change, without notice,
and at any time (even mid-release or between different OS platforms). Client
code that oversteps the above rules might fail on different versions and
patch levels of the platform; or when run on different underlying OSes;
or when run with a different mix of co-resident plug-ins; or when run with
a different workbench perspective; and so on. Indeed, no one is even particularly
interested in speculating exactly how any particular transgression might
come back to bite you. To those who choose to ignore the rules, don't say
that you weren't warned. And don't expect much more than a sympathetic
"Told you so."
On the other hand, client plug-in code that lives by the above rules
should continue to work across different versions and patch levels of the
platform, across different underlying OSes, and should peacefully co-exist
with other plug-ins. If everyone plays by the rules, the Eclipse platform
will provide a stable and supported base on which to build exciting new
products.