Interfaces
Goals
- Understand and use abstract methods.
- Understand and use interfaces.
- Use default methods.
- Know the differences between abstract methods and interfaces and know when to use each.
- Recognize patterns of interfaces, abstract classes, and default concrete classes.
- Program to interfaces.
Concepts
- abstract method
- Application Programming Interface (API)
- implementation
- informative
- interface
- loosely-coupled
- marker interface
- mixin
- normative
- “program to interfaces”
- semantics
Language
abstract
default
interface
Javadoc
@apiNote
@implNote
@implSpec
Library
java.io.Closeable
java.io.PrintStream
java.io.Serializable
java.lang.Appendable
java.lang.Cloneable
java.lang.Runnable
java.lang.System.out
java.lang.StringBuilder
java.lang.Thread
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.
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.
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.
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.
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.
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.
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.
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:
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.
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:
- They make it easier for a developer to implement the interface, there are now fewer methods that require implementing. (The default methods can of course be overridden to provide more efficient versions.)
- They serve as further documentation of what the method does conceptually.
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
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.
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.
Implementation Patterns
Abstract classes, abstract methods, interfaces, and default methods are similar; but used correctly they address different needs:
- Interfaces define the public API.
- Interface default methods provide default implementations of the public API in relation to the public API.
- Abstract methods define the internal API for the implementation hierarchy.
- Abstract classes can provide default implementation of the public or internal API in relation to the non-public implementation.
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 |
| Vehicle | |
Abstract… | abstract class |
| 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
- Interfaces define the public API.
- Interface default methods provide default implementations of the public API in relation to the public API.
- Abstract methods define the internal API for the implementation hierarchy
- Abstract classes can provide default implementation of the public or internal API in relation to the non-public implementation.
Gotchas
- Don't choose an interface merely for the methods it contains; make sure the overall interface semantics are appropriate.
- It could be problematic if your interface extends other interfaces and exists merely as a convenience for implementing other interfaces, rather than having some particular semantics on its own. A
WingedAnimal
interface as a convenience for implementingWinged
andAnimal
could cause confusion, because some programs might check forinstanceof WingedAnimal
and miss anAnimal
that merely implementedWinged
. - Default methods can only call other interface methods; they cannot call class methods, abstract or otherwise.
In the Real World
- Nowadays the use of annotations is often a better choice than using a marker interface for “flagging” a class with special semantics.
- Interface default methods are an extremely useful tool to add functionality to an existing hierarchy of classes without rewriting the classes.
Think About It
- Make sure the interface you are implementing matches the semantics of what you're trying to represent.
- The interface I'm implementing provide the actual semantics I'm wanting to implement, or does it simply a have a method with a similar-sounding name?
- Is the interface I'm creating too general? Make sure you can be specific enough with the interface contract so that the interface doesn't lose all its semantics.
- Does my interface provide semantics, or is it merely a convenience interface for grouping other interfaces? The latter could cause problems.
- Will I ever want to create another implementation of my class? Should I provide a separate interface for it; or at least be prepared factor out an interface in the future?
Self Evaluation
- If a class has an abstract method must that class be marked
abstract
as well? May a class be marked asabstract
without any abstract methods? - What happens if you leave off the access modifier (e.g.
public
) from an interface method declaration? - For what purpose would an abstract method be more useful than an interface method?
- What's a typical suffix added to the names of interfaces designated the ability to perform some function?
- Of what value is an interface with no methods?
- What is a “mixin”, and what Java feature provides essentially this functionality?
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.
- Based upon your research, you decide that books, magazines, and journals are undeniably something called “publication”, and you decide to make an interface for that.
- The “publication” interface allows access to some common things such as “name” and “first publication date”, so you decided to create an abstract implementation of the “publication” interface that stores and returns this common information.
- The class representing “book” can now be modified slightly to extend the abstract publication thing you created; otherwise it shouldn't have to change much. Maybe bring back the “number of copies sold” as something distinct from circulation.
- Ah, ha—journals and magazines are both “periodicals”. You decide to create a distinct interface to represent this commonality. You figure that a periodical counts as a publication—just a narrower category of periodical.
- After a little more research, you realize that magazines and journals really aren't that different—about the only distinction is whether they are peer reviewed and whether they have an academic readership. You therefore decide to create a base periodical class that covers everything that magazines and journals require, so that all you have to do in order to create concrete “magazine” and “journal” classes is to subclass this base periodical class.
- To show the marketing manager how well this is working, you decide to add to your growing database a list of the top eight general and multidisciplinary scientific journals as reported by Wikipedia on 2015-07-24. You may add data points as you see fit, incorporating them into the appropriate classes and interfaces.
- Create another method, analogous to the two you already have for books and magazines, to print out journals.
- The method for getting the difference in publication years doesn't seem as important now, and you decide to remove it, considering that you can always look at Git history and bring it back later or put it somewhere else.
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
- Interfaces (Oracle - The Java™ Tutorials)
- Default Methods (Oracle - The Java™ Tutorials)
- What about the diamond problem? (Maurice Naftalin's Lambda FAQ)
- New Javadoc Tags @apiNote, @implSpec and @implNote (CodeFX)
- Configuring Custom Javadoc Tags (Apache Maven Javadoc Plugin)
- Design Principles from Design Patterns - A Conversation with Erich Gamma, Part III (artima developer)
References
- Application Programming Interface (Wikipedia)
- Mixin (Wikipedia)
- Multiple inheritance: The diamond problem (Wikipedia)
- Interface-based Programming (Wikipedia)
- The Java® Language Specification, Java SE 11 Edition: Chapter 8. Classes (Oracle)
- The Java® Language Specification, Java SE 11 Edition: Chapter 9. Interfaces (Oracle)
- JDK-8068562: javadoc tags to distinguish API, implementation, specification, and notes