Interfaces

Goals

Concepts

Language

Javadoc

Library

Dependencies

Build Plugins

Lesson

Interface versus Implementation

You've learned the importance of specifying a “contract” for your objects using Javadocs and method signatures; this is the Application Programming Interface (API) of your objects. In fact a class, which encapsulates data and functionality, can be thought of as a combination of an interface (the specification of methods; the contract for interacting with the class) and the implementation of that interface (the actual code that fulfills the requirements of the contract).

If the contract or interface of your objects is so important, wouldn't it be nice if you could define that interface separately from any implementation? If you could do that, then others could create modules directly to your interface specification without needing your actual implementation (until it came time to run the program, of course). Furthermore a third party could write a different implementation using the same interface; if the other implementation performed faster or more efficiently, it could be substituted with no change in the code of other modules in your program.

Java allows several facilities to separate an API from the underlying implementation. One of those you've briefly seen before: abstract classes. The other is a tool seemingly made for the job: the Java interface.

Abstract Methods

When studying inheritance, we talked about abstract classes and how they prevent anyone from instantiating them directly. Only concrete subclasses (those that are not abstract) may be instantiated with the new keyword. Abstract classes work sort of like “placeholders” in the inheritance hierarchy.

An abstract vehicle class.
/**
 * Abstract base class for vehicles.
 * Only concrete subclasses can be instantiated.
 */
public abstract class AbstractVehicle
{
}

Abstract classes can specify some of their methods as abstract as well. In our first version of the Vehicle class in the lesson on inheritance, we had assumed that vehicles would move(…) by turning their wheels. As soon as it became clear that airplanes move differently, so we had to override Airplane.move(…) with different logic. But what if we don't know how any of the subclasses will implement certain functionality, but we want to make sure that subclasses do override a method? In other words, how can we make sure that each subclass of Vehicle provides its own implementation of move(…) without providing an implementation of Vehicle.move(…)? The answer in Java is to create an abstract method Vehicle.move() that has no body, but serves only as a “placeholder” for subclasses to override. This is done by using the abstract keyword.

An abstract vehicle class with an abstract method.
public abstract class AbstractVehicle
{

  /**
   * Moves the position of the vehicle.
   * @param distance The distance to move, in example units.
   */
  public abstract void move(int distance);  //abstract method

}

Such abstract methods also function as placeholders—placeholders for a later implementation by a subclass. In fact some class along the chain must implement the abstract method before one of the subclasses can be considered concrete.

Two subclasses overriding an abstract method.
public class Car extends AbstractVehicle
{
  @Override
  public void move(final int distance)
  {
    //TODO turn wheels
  }
}
public class Airplane extends AbstractVehicle
{

  @Override
  public void move(final int distance)
  {
    //TODO turn propellers
  }
}

Therefore abstract methods partly fill our requirement for specifying an interface separate from an implementation. The problem with abstract methods is that they are tied to a class and, as as you have seen, class inheritance in Java is somewhat restrictive. Java does not allow multiple inheritance, so if we use abstract methods for specifying our interface we would have no way to have a class implement abstract methods placed in multiple abstract classes.

Abstract methods are usually best for requiring subclasses to implement internal, protected functionality that is used by some other default functionality. Here for example we use AbstractVehicle as a base class with a default implementation of storeLuggage() that uses two abstract methods openStorage() and closeStorage(), which will be implemented by base classes.

An abstract vehicle class with protected abstract methods.
public abstract class AbstractVehicle  //abstract class
{

  /**
   * Stores luggage in the luggage storage compartment.
   * @param luggage The luggage to store.
   */
  public void storeLuggage(@Nonnull final Luggage luggage)
  {
    openStorage();
    //put luggage inside storage space
    closeStorage();
  }

  /** Opens the luggage storage compartment. */
  protected abstract void openStorage();  //NOTE: protected abstract method

  /** Closes the luggage storage compartment. */
  protected abstract void closeStorage();  //NOTE: protected abstract method

}

Now each concrete subclass can implement openStorage() and closeStorage() the way it sees fit. A Car for example may open and close its trunk, while an Airplane may open an close the luggage hold. However keep these methods as protected so that we don't expose this functionality to callers. Someone using our vehicle simply needs to know that it can pass luggage to the vehicle, and the vehicle (whichever type of vehicle it is) will properly store the luggage in the luggage compartment. The caller need not worry about the details of how this particular vehicle opens and closes the luggage compartment.

Interfaces

We have so far been using “interface” in the general sense, meaning the “contract” that specifies how an implementation will behave. Java provides a construct that is specially designed to encapsulate an API distinct from its implementation; it is named, appropriately, interface. Let's revisit the AbstractVehicle example above, using an interface instead of an abstract method to represent the specification of the move(…) method.

An interface for a vehicle.
public interface Vehicle
{

  /**
   * Moves the position of the vehicle.
   * @param distance The distance to move, in example units.
   */
  public void move(int distance);

}
Two classes implementing an interface.
public class Car implements Vehicle
{
  @Override
  public void move(final int distance)
  {
    //TODO turn wheels
  }
}
public class Airplane implements Vehicle
{

  @Override
  public void move(final int distance)
  {
    //TODO turn propellers
  }
}

Interface Semantics

Now any class that wants to can implement the Vehicle interface. But the Vehicle interface not only specifies a contract for implementing classes, it also defines semantics, or the meaning of classes that implement the Vehicle interface. Only classes that are in fact vehicles should implement the Vehicle interface.

Perhaps the functionality of “moving” is common to other things besides vehicles. In that case, the move(…) method could be refactored out into a new interface for things that can move.

An interface for things that can move.
public interface Movable
{

  /**
   * Moves the position of the object.
   * @param distance The distance to move, in example units.
   */
  public void move(int distance);

}

Interface Inheritance

Interfaces allow inheritance as well as classes. The Vehicle interface can extend the Movable interface, thereby inheriting the move(…) method (and the semantics that go along with it). Now classes that implement Vehicle must provide a move(…) method, as they are indirectly inheriting Movable as well.

An interface that extends another interface.
public interface Vehicle extends Movable
{

  //we get a move(…) method for free!

}

Multiple Interfaces

Java's interfaces are more flexible than classes; they allow a class to implement multiple interfaces, and for an interface to extend multiple other interfaces! The interfaces must be separated by the comma , character. We could make a LuggageStorable interface, and Vehicle could implement that as well:

Multiple inheritance with interfaces.
public interface LuggageStorable
{

  /**
   * Stores luggage in the luggage storage compartment.
   * @param luggage The luggage to store.
   */
  public void storeLuggage(@Nonnull final Luggage luggage);

}
public interface Vehicle extends Movable, LuggageStorable
{
}

Marker Interfaces

Interfaces define a contract for the methods they contain, but they also denote certain semantics separate from their methods. If a class implements an Animal interface, it means something. Accordingly one can create an interface without any methods at all that is used to merely to flag or “mark” certain semantics; this is called a marker interface.

An interface Winged might not define any methods at all, but a class could implement Winged merely to indicate that something has wings.

A marker interface for things that have wings.
/** Something that has wings. */
public interface Winged
{
}
public class Bird implements Animal, Winged
{
  …
}

Default Methods

Java now allows interfaces to specify default implementations of methods. An implementing class is still free to override them, but that's optional. By implementing interfaces with default methods, classes can “mix in” functionality from multiple sources and get around the inflexibility presented by Java's single-inheritance model.

Default methods are helpful in two ways:

An interface with default methods.
public interface Vehicle extends Movable
{

  /** Honks the horn. */
  public default void honk()
  {
    System.out.println("beep");
  }

  /**
   * Sends out a some warning sound.
   * <p>This implementation sends out a warning sound.</p>
   * @see #honk()
   */
  public default void warn()
  {
    honk();
  }

  /**
   * Moves the vehicle in a figure 8 pattern.
   * <p>This default implementation makes a squarish
   * figure 8.</p>
   */
  public default void moveFigure8()
  {
    turn(90);
    move(1);
    turn(90);
    move(1);
    turn(90);
    move(1);
    turn(90);
    move(2); //crossover
    turn(-90);
    move(1);
    turn(-90);
    move(1);
    turn(-90);
    move(1);
    turn(-90);  //reposition in original direction
  }

  /**
   * Turns the vehicle.
   * @param degrees The degrees to turn;
   *     negative to the left, and positive to the right.
   */
  public void turn(double degrees);

}

Documenting Interfaces and Implementations

Now that we have learned how to separate the interface from the implementation, we need to be clear in the documentation which is which. Moreover when we discuss either of these in the documentation, we need to be clear if our documentation specifies part of the contract that must be followed (the “specification”), or simply extra helpful advice (additional “information”).

You have already seen an example in this lesson and in the lesson on inheritance: the Javadoc comments for Vehicle.move() say, “Moves the position of the vehicle.” This is the specification of the interface. The Javadoc comments for Airplane.move() say, “This version moves by virtue of propellers instead of wheels.” This is the specification of the implementation. For each of these you might provide additional information that isn't part of the specification, such as “You might think of this as the go() method” or “Airplanes may have many propellers” for the interface and implementation, respectively.

Javadoc Interface/Implementation Tags

Javadoc tags for interface/implementation documentation.
Interface Implementation
Specification (“normative”) (default) @implSpec
Information (“informative”) @apiNote @implNote

You can see that by identifying documentation as interface/implementation and also as specification/information, there are four categories of documentation. In JDK-8068562 Java defined new Javadoc tags, shown in the matrix in the figure. There are only three new tags, because the category of “interface specification” doesn't need a tag—the Javadoc documentation itself without a tag is already considered to be the interface specification. When documenting an implementation, you can still use @inheritdoc to include the interface documentation.

The updated example below shows how you might use these new Javadoc tags in order to clarify the interface specification versus the implementation and additional notes.

Documenting Vehicle and an implementation.
public class Vehicle
{
  /**
   * Moves the position of the vehicle.
   * @apiNote You might think of this method as go().
   */
  public void move();
}
public class Airplane implements Vehicle
{

  /**
   * {@inheritdoc}
   * @implSpec This version moves by virtue of propellers instead of wheels.
   * @implNote Airplanes may have many propellers.
   */
  @Override
  public void move()
  {
    //TODO turn propellers
  }
}

Configuring Javadoc for Interface/Implementation Tags

Unfortunately the javadoc tool doesn't understand these new tags natively, even though these tags are used within the actual Java library source code. Rather than releasing new versions of Javadoc to support new tags, a mechanism was provided to add custom tags in general. You can support custom tags using the javadoc command by adding the -tag command-line option.

However the Apache Maven Javadoc Plugin provides a way to define new tags as part of the plugin configuration in your Maven POM. The jar goal provides a <tags> configuration section for this purpose. This section allows you to identify the custom tags, indicate where they should appear, and provide a label, as shown in the complete example below. You'll also want to include the tags that Javadoc already recognizes, in order to specify the relative order in which the custom tags should appear.

Configuring the Maven Javadoc Plugin for Interface/Implementation Tags.
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-javadoc-plugin</artifactId>
        <version>3.0.1</version>
        <configuration>
          <tags>
            <tag>
              <name>apiNote</name>
              <placement>a</placement>
              <head>API Note:</head>
            </tag>
            <tag>
              <name>implSpec</name>
              <placement>a</placement>
              <head>Implementation Specification:</head>
            </tag>
            <tag>
              <name>implNote</name>
              <placement>a</placement>
              <head>Implementation Note:</head>
            </tag>
            <tag><name>param</name></tag>
            <tag><name>return</name></tag>
            <tag><name>throws</name></tag>
            <tag><name>since</name></tag>
            <tag><name>version</name></tag>
            <tag><name>serialData</name></tag>
            <tag><name>see</name></tag>
          </tags>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>jar</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

Implementation Patterns

Abstract classes, abstract methods, interfaces, and default methods are similar; but used correctly they address different needs:

There has consequently developed a certain pattern of naming convention and usage for these various constructs. A class hierarchy need only use those appropriate for the data being modeled.

Name Entity Purpose Example
interface
  • Defines public API.
  • Provides default implementations in terms of public API
Vehicle
Abstract… abstract class
  • Defines internal API.
  • Provides helpful default implementations in terms of internal API.
AbstractVehicle
Base… abstract class Provides default values and settings to serve as a base for other implementations BaseVehicle
Default… concrete class Provides a class with default settings that can be instantiated with no specialization. DefaultVehicle

Program to Interfaces

Java interfaces are powerful and flexible. As classes encapsulate data and functionality; interfaces encapsulate the public API, the public contract with consumers of your code. When interacting with modules of others, or even your own classes and interfaces, you should write your code in relation to the provided interfaces, not that internal implementations in the classes. Put another way, you should “program to interfaces”, not to implementations.

Programming to interfaces allows your code to be flexible, as module implementations can be substituted without requiring changes to the caller's code. It makes your code testable, as a test module that implements the interface (a “mock” implementation, which you'll learn about in a future lesson) can be temporarily swapped in to provide something to test against without pulling in a full implementation. Lastly programming to interfaces helps you, the developer, get in the right mindset of providing a modularized and well-defined API of loosely-coupled modules rather than brittle code that relies internal “secret sauce” that some implementation just happens to use now but may not in the future.

Review

Summary

Gotchas

In the Real World

Think About It

Self Evaluation

Task

The marketing manager comes to you and tells you that some academic users are wanting to track academic journals as separate from magazine. In the meantime, you have been considering whether some things like “number of copies sold” is really semantically equivalent to “circulation”. You decide to refactor the entire hierarchy, using interfaces to make things even more flexible.

In your new, refactored hierarchy, use the new Javadoc tag @implSpec, combined with {@inheritDoc}, where appropriate. You may also use @apiNote and @implNote as you see fit. Generate your Javadoc documentation and verify that the information from the new tags appears.

In places where this specification is vague or ambiguous (as will often be the case with specifications from your marketing manager), use your best judgement and decide how you think best to model the relationships among the various types of periodicals.

See Also

References

Resources