Skip to content

Instantly share code, notes, and snippets.

@renepanke
Last active October 26, 2023 14:06
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save renepanke/e669ead080865e175db50880ebc94fda to your computer and use it in GitHub Desktop.
Save renepanke/e669ead080865e175db50880ebc94fda to your computer and use it in GitHub Desktop.
Java API Design Guidelines

Java API Design Guidelines

Credits for this Gist go to Eamonn McManus and his blog post.

Evolveability

Once something is in the API it will stay there. The consequence of this is to never remove classes and methods.

Design Goals

The API has to be:

  • absolutely correct,
    • e.g. a URL library has to be URL spec compliant
  • easy to use
    • test this by writing lots of example code:
      1. are there groups of operations that you need to repeat?
      2. do you need to look at your API documentation very often?
      3. are there cases where the API doesn't behave as you might expect?
  • easy to learn
    • the API should be small
    • documentation should include examples
    • (where appropriate, the API should look like familiar APIs)
  • fast enough
    • but first simple and correct
    • if your first implementation is inefficient, change your API to allow a more efficient implementation
    • measure it
  • small enough
    • in terms of:
      1. size of compiled code
      2. amount of memory it needs
    • measure it

Minimalism

Base the API on example code! Think of problems a user might want to solve with the API. Add just enough classes and methods to solve those problems. Code the solutions!

This allows you to check if the API is useful and to have some basic tests and examples.

A note on Interfaces

A type should only be an Interface if you have a good reason for it to be.

Interfaces are overvalued

  • Interfaces can be implemented by anybody
    • imagine String being an Interface, you could never be sure, how it behaves if you use an unknown library
    • in practice Interfaces often end up cheating and casting to the non-public implementation class
  • Interfaces cannot have constructors or static methods
    • you will either need your own implementation
    • or utilize another class and its method like Factory.getInstance() to create an instance of it
  • Interfaces cannot evolve
    • imagine adding a new method to version 2 of your API, implementations of version 1 won't compile because they don't implement the new method
    • you can solve this by using an Abstract Class
  • Interfaces cannot be serialized

Some good reasons/cases to use Interfaces

  • Callbacks
    • if the Interface is intended to be implemented by user code it's often more appropriate than an Abstract Class
  • Multiple Inheritance
    • e.g. Comparable
    • only very deep in the Inheritance Hierarchy (e.g. implemented in Integer but not in Number)
    • implementing Interface in private inner class is usually cleaner
  • Dynamic Proxies
    • it's possible to make an implementation of an Interface at runtime with the java.lang.reflect.Proxy class. Any of the calls of the Interfaces methods results in a call to invoke().

A note by myself (SVYSHE)

Interfaces might be useful while developing, to see how the API would feel and behave. A substition by the actual class while developing will then be necessary.

Packages

In Java, if a class or method is visibile outside its package, then it's visible to all code in all packages. This means you'll easily get forced to declare something public just so that code in the API can access them.

The simplest solution is to put your whole API in one package. For an API with fewer than 30 public classes this is usually the best approach.

If the API is too big for a single package, then you should plan to have private implementation packages. In consequence, some packages will be excluded from JavaDoc and the public API even though their contents are accessible.

E.g. in the JDK there are many sun.* and com.sun.* packages.

A good convention for private packages is to put internal in the name. E.g. the Banana API has public com.example.banana and private packages com.example.banana.internal packages.

"Don't forget that the private packages are accessible. There may be security implications if arbitrary code can access these internals. Various techniques exist to address these. The NetBeans API tutorial describes one. In the JMX API, we use another. There is a class javax.management.JMX which contains only static methods and has no public constructor. This means that user code can never have an instance of this class. So in the private com.sun.jmx packages, we sometimes add a parameter of type JMX to sensitive public methods. If a caller can supply a non-null instance of this class, it must be coming from the javax.management package."

Various other tips

  • if a class can be immutable, then it should be
  • the only public fields should be static and final.
  • don't violate Java Conventions just because you think they're badly designed
  • don't implement Cloneable, rather provide a copy constructor or a static factory method. The advantage of the constructor is that it can be called form its subclasses constructor. The advantage of the static method is that it can return an instance of a subclass or an already existant instance
  • Exceptions should usually be unchecked, a checked exception usually reflects a problem with the underlying infrastructure such as FileSystem, network and so on
  • design for inheritance or don't allow it, every method in a class that hasn't got a clear documentation what happens if you override it, should be final by default. Do this only in case of having useful code examples that do override the method.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment