Java expands on the abstract method concept with its interfaces scheme. It’s often desirable to specify the prototypes for a set of methods and provide no implementation. In Java, this is called an interface. An interface defines a set of methods that a class must implement (i.e., some or all of the class’s behavior). A class in Java can declare that it implements an interface and then go about implementing the required methods. A class that implements an interface doesn’t have to inherit from any particular part of the inheritance hierarchy or use a particular implementation.
Interfaces are kind of like Boy Scout or Girl Scout merit badges. A scout who has learned to build a birdhouse can walk around wearing a little sleeve patch with a picture of one. This says to the world, “I know how to build a birdhouse.” Similarly, an interface is a list of methods that define some set of behavior for an object. Any class that implements each of the methods listed in the interface can declare that it implements the interface and wear, as its merit badge, an extra type—the interface’s type.
Interface types act like class types. You can declare variables to be of an interface type, you can declare arguments of methods to accept interface types, and you can even specify that the return type of a method is an interface type. In each of these cases, what is meant is that any object that implements the interface (i.e., wears the right merit badge) can fill that spot. In this sense, interfaces are orthogonal to the class hierarchy. They cut across the boundaries of what kind of object an item is and deal with it only in terms of what it can do. A class can implement as many interfaces as it desires. In this way, interfaces in Java replace the need for C++’s multiple inheritance (and all of its messy side effects).
An interface looks like a purely abstract
class
(i.e., a class with only abstract
methods). You
define an interface with the interface
keyword and
list its methods with no bodies, just prototypes:
interface Driveable { boolean startEngine( ); void stopEngine( ); float accelerate( float acc ); boolean turn( Direction dir ); }
The previous example defines an interface called
Driveable
with four methods. It’s
acceptable, but not necessary, to declare the methods in an interface
with the abstract
modifier; we haven’t done
that here. More importantly, the methods of an interface are always
considered public, and you can optionally declare them as so. Why
public? Well, the user of the interface wouldn’t necessarily be
able to see them otherwise.
Interfaces
define capabilities, so it’s common to name interfaces after
their capabilities. Driveable
,
Runnable
, and Updateable
are
good interface names. Any class that implements all the methods can
then declare it implements the interface by using a special
implements
clause in its class definition. For
example:
class Automobile implements Driveable { ... public boolean startEngine( ) { if ( notTooCold ) engineRunning = true; ... } public void stopEngine( ) { engineRunning = false; } public float accelerate( float acc ) { ... } public boolean turn( Direction dir ) { ... } ... }
Here, the class Automobile
implements the methods
of the Driveable
interface and declares itself
Driveable
using an implements
clause.
As shown in Figure 6.5, another class, such as
Lawnmower
, can also implement the
Driveable
interface. The figure illustrates the
Driveable
interface being implemented by two
different classes. While it’s possible that both
Automobile
and Lawnmower
could
derive from some primitive kind of vehicle, they don’t have to
in this scenario. This is a significant advantage of interfaces over
standard multiple inheritance, as implemented in C++.
After declaring the interface, we have a new type,
Driveable
. We can declare variables of type
Driveable
and assign them any instance of a
Driveable
object:
Automobile auto = new Automobile( ); Lawnmower mower = new Lawnmower( ); Driveable vehicle; vehicle = auto; vehicle.startEngine( ); vehicle.stopEngine( ); vehicle = mower; vehicle.startEngine( ); vehicle.stopEngine( );
Both Automobile
and Lawnmower
implement Driveable
, so they can be considered of
that type.
Interfaces can be used to implement callbacks in Java. An object can, in effect, pass one of its methods to another object. The callback occurs when the other object subsequently invokes the method. In C or C++, this is prime territory for function pointers; Java uses interfaces instead.
Consider two classes: a TickerTape
class that
displays data and a TextSource
class that provides
an information feed. We’d like our
TextSource
to send any new text data. We could
have TextSource
store a reference to a
TickerTape
object, but then we could never use our
TextSource
to send data to any other kind of
object. Instead, we’d have to proliferate subclasses of
TextSource
that dealt with different types. A more
elegant solution is to have TextSource
store a
reference to an interface type, TextUpdateable
:
interface TextUpdateable { void doTextUpdate( String text ); } class TickerTape implements TextUpdateable { public void doTextUpdate( String text ) { System.out.println("TICKER:\n" + text + "\n"); } } class TextSource { TextUpdateable receiver; TextSource( TextUpdateable r ) { receiver = r; } public void sendText( String s ) { receiver.doTextUpdate( s ); } }
The only thing the TextSource
really cares about
is finding the right method to invoke in order to output some text.
Using an interface establishes a “well-known” name,
doTextUpdate
, for that method.
When the TextSource
is constructed, a reference to
the TickerTape
(which implements the interface) is
stored in an instance variable. This “registers” the
TickerTape
as the
TextSource
’s “output device.”
Whenever it needs to output data, the TextSource
calls the output device’s doTextUpdate( )
method.
Although
interfaces mostly allow us to specify behavior without
implementation, there’s one exception. An interface can contain
constants (static
final
variables), which appear in any class that
implements the interface. This feature enables predefined parameters
for use with the methods:
interface Scaleable { static final int BIG = 0, MEDIUM = 1, SMALL = 2; void setScale( int size ); }
The Scaleable
interface defines three integers:
BIG
, MEDIUM
, and
SMALL
. All variables defined in interfaces are
implicitly final
and static
; we
don’t have to use the modifiers, but for clarity, we recommend
you do. A class that implements Scaleable
sees
these variables:
class Box implements Scaleable { void setScale( int size ) { switch( size ) { case BIG: ... case MEDIUM: ... case SMALL: ... } } ... }
Sometimes, interfaces are created just to hold constants; anyone who implements the interfaces can see the constant names, as if they were included by a C/C++ include file. This is a somewhat degenerate, but acceptable, use of interfaces.
Sometimes completely empty interfaces serve as a marker that a class
has a special property. The java.io.Serializeable
interface is a good example. Classes that implement
Serializable
don’t add any methods or
variables. Their additional type simply identifies them to Java as
classes that want to be able to be serialized.
An interface can extend another interface, just as a class can extend another class. Such an interface is called a subinterface. For example:
interface DynamicallyScaleable extends Scaleable { void changeScale( int size ); }
The interface DynamicallyScaleable
extends our
previous Scaleable
interface and adds an
additional method. A class that implements
DynamicallyScaleable
must implement all the
methods of both interfaces.
Note here that we are using the term
"extends” and not
“implements" to subclass the
interface. Interfaces can’t implement anything! But an
interface is allowed to extend as many interfaces as it wants. If you
want to extend two or more interfaces, list them after the
extends
keyword, separated by commas:
interface DynamicallyScaleable extends Scaleable, SomethingElseable { ... }
Keep in mind that although Java supports multiple inheritance of interfaces, each class can extend only a single parent class.
Get Learning Java now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.