3.9. Interfaces
Like a class, an interface
defines a new reference type. Unlike classes, however, interfaces
provide no implementation for the types they define. As its name
implies, an interface specifies only an API: all of its methods are
abstract and have no bodies. It is not possible to
directly instantiate an interface and create a member of the
interface type. Instead, a class must implement
the interface to provide the necessary method bodies. Any instances
of that class are members of both the type defined by the class and
the type defined by the interface. Interfaces provide a limited but
very powerful
alternative
to multiple inheritance .[9] Classes in Java can inherit members from only a single
superclass, but they can implement any number of interfaces. Objects
that do not share the same class or superclass may still be members
of the same type by virtue of implementing the same interface.
[9] C++ supports
multiple inheritance, but the ability of a class to have more than
one superclass adds a lot of complexity to the language.
3.9.1. Defining an Interface
An
interface definition is much like a class definition in which all the
methods are abstract and the keyword class has
been replaced with interface. For example, the
following code shows the definition of an interface named
Centered. A Shape class, such
as those defined earlier in the chapter, might implement this
interface if it wants to allow the coordinates of its center to be
set and queried:
public interface Centered {A number of restrictions apply to the members of an interface:
void setCenter(double x, double y);
double getCenterX();
double getCenterY();
}
- An
interface contains no implementation whatsoever. All methods of an
interface are implicitly
abstract and
must have a semicolon in place of a method body. The
abstract modifier is allowed but, by convention,
is usually omitted. Since static methods may not be abstract, the
methods of an interface may not be declared
static. - An interface defines a public API. All members of an
interface are implicitly public, and it is
conventional to omit the unnecessary public
modifier. It is an error to define a protected or
private method in an interface. - An interface may not define any instance fields. Fields are an
implementation detail, and an interface is a pure specification
without any implementation. The only fields allowed in an interface
definition are constants that are
declared both static and final. - An interface cannot be instantiated, so it does not define a
constructor. - Interfaces may contain nested types. Any such types are implicitly
public and static. See Section 3.10 later in this chapter.
3.9.1.1 Extending interfaces
Interfaces
may extend other interfaces, and, like a class definition, an
interface definition may include an extends
clause. When one interface extends another, it
inherits all the abstract methods
and constants of its superinterface and can define new abstract
methods and constants. Unlike classes, however, the extends clause of
an interface definition may include more than one superinterface. For
example, here are some interfaces that extend other interfaces:
public interface Positionable extends Centered {An interface that extends more than one interface inherits all the
void setUpperRightCorner(double x, double y);
double getUpperRightX();
double getUpperRightY();
}
public interface Transformable extends Scalable, Translatable, Rotatable {}
public interface SuperShape extends Positionable, Transformable {}
abstract methods and constants from each of those interfaces and can
define its own additional abstract methods and constants. A class
that implements such an interface must implement the abstract methods
defined directly by the interface, as well as all the abstract
methods inherited from all the superinterfaces.
3.9.2. Implementing an Interface
Just as a class uses
extends to specify its superclass, it can use
implements
to name one or more interfaces it supports.
implements is a Java keyword that can appear in a
class declaration following the extends clause.
implements should be followed by a comma-separated
list of interfaces that the class implements.When a class declares an interface in its
implements clause, it is saying that it provides
an implementation (i.e., a body) for each method of that interface.
If a class implements an interface but does not provide an
implementation for every interface method, it inherits those
unimplemented abstract methods from the interface
and must itself be declared abstract. If a class
implements more than one interface, it must implement every method of
each interface it implements (or be declared
abstract).The following code shows how we can define a
CenteredRectangle class that extends the
Rectangle class from earlier in the chapter and
implements our Centered interface.
public class CenteredRectangle extends Rectangle implements Centered {
// New instance fields
private double cx, cy;
// A constructor
public CenteredRectangle(double cx, double cy, double w, double h) {
super(w, h);
this.cx = cx;
this.cy = cy;
}
// We inherit all the methods of Rectangle but must
// provide implementations of all the Centered methods.
public void setCenter(double x, double y) { cx = x; cy = y; }
public double getCenterX() { return cx; }
public double getCenterY() { return cy; }
}
Suppose we implement
CenteredCircle and
CenteredSquare just as we have implemented this
CenteredRectangle class. Since each class extends
Shape, instances of the classes can be treated as
instances of the Shape class, as we saw earlier.
Since each class implements the Centered
interface, instances can also be treated as instances of that type.
The following code demonstrates how objects can be members of both a
class type and an interface type:
Shape[] shapes = new Shape[3]; // Create an array to hold shapesThis example demonstrates that interfaces are data types in Java,
// Create some centered shapes, and store them in the Shape[]
// No cast necessary: these are all widening conversions
shapes[0] = new CenteredCircle(1.0, 1.0, 1.0);
shapes[1] = new CenteredSquare(2.5, 2, 3);
shapes[2] = new CenteredRectangle(2.3, 4.5, 3, 4);
// Compute average area of the shapes and average distance from the origin
double totalArea = 0;
double totalDistance = 0;
for(int i = 0; i < shapes.length; i++) {
totalArea += shapes[i].area(); // Compute the area of the shapes
if (shapes[i] instanceof Centered) { // The shape is a Centered shape
// Note the required cast from Shape to Centered (no cast would
// be required to go from CenteredSquare to Centered, however).
Centered c = (Centered) shapes[i]; // Assign it to a Centered variable
double cx = c.getCenterX(); // Get coordinates of the center
double cy = c.getCenterY(); // Compute distance from origin
totalDistance += Math.sqrt(cx*cx + cy*cy);
}
}
System.out.println("Average area: " + totalArea/shapes.length);
System.out.println("Average distance: " + totalDistance/shapes.length);
just like classes. When a class implements an interface, instances of
that class can be assigned to variables of the interface type.
Don't interpret this example to imply that you must
assign a CenteredRectangle object to a
Centered variable before you can invoke the
setCenter( ) method or to a
Shape variable before you can invoke the
area() method.
CenteredRectangle defines setCenter(
) and inherits area() from its
Rectangle superclass, so you can always invoke
these methods.
3.9.2.1 Implementing multiple interfaces
Suppose we want shape
objects that can be positioned in terms of not only their center
points but also their upper-right corners. And suppose we also want
shapes that can be scaled larger and smaller. Remember that although
a class can extend only a single superclass, it can implement any
number of interfaces. Assuming we have defined appropriate
UpperRightCornered and Scalable
interfaces, we can declare a class as follows:
public class SuperDuperSquare extends ShapeWhen a class implements more than one interface, it simply means that
implements Centered, UpperRightCornered, Scalable {
// Class members omitted here
}
it must provide implementations for all abstract methods in all its
interfaces.
3.9.3. Interfaces vs. Abstract Classes
When defining an abstract type (e.g.,
Shape) that you expect to have many subtypes
(e.g., Circle, Rectangle,
Square), you are often faced with a choice between
interfaces and abstract classes. Since they have similar features, it
is not always clear which to use.An interface is useful because any class can implement it, even if
that class extends some entirely unrelated superclass. But an
interface is a pure API specification and contains no implementation.
If an interface has numerous methods, it can become tedious to
implement the methods over and over, especially when much of the
implementation is duplicated by each implementing class.An abstract class does not need to be entirely abstract; it can
contain a partial implementation that subclasses can take advantage
of. In some cases, numerous subclasses can rely on default method
implementations provided by an abstract class. But a class that
extends an abstract class cannot extend any other class, which can
cause design difficulties in some situations.
Another important difference between interfaces and abstract classes
has to do with compatibility. If you define an interface as part of a
public API and then later add a new method to the interface, you
break any classes that implemented the previous version of the
interface. If you use an abstract class, however, you can safely add
nonabstract methods to that class without requiring modifications to
existing classes that extend the abstract class.In some situations, it is clear that an interface or an abstract
class is the right design choice. In other cases, a common design
pattern is to use both. Define the type as a totally abstract
interface, then create an abstract class that implements the
interface and provides useful default implementations that subclasses
can take advantage of. For example:
// Here is a basic interface. It represents a shape that fits inside
// of a rectangular bounding box. Any class that wants to serve as a
// RectangularShape can implement these methods from scratch.
public interface RectangularShape {
void setSize(double width, double height);
void setPosition(double x, double y);
void translate(double dx, double dy);
double area();
boolean isInside();
}
// Here is a partial implementation of that interface. Many
// implementations may find this a useful starting point.
public abstract class AbstractRectangularShape implements RectangularShape {
// The position and size of the shape
protected double x, y, w, h;
// Default implementations of some of the interface methods
public void setSize(double width, double height) { w = width; h = height; }
public void setPosition(double x, double y) { this.x = x; this.y = y; }
public void translate (double dx, double dy) { x += dx; y += dy; }
}
3.9.4. Marker Interfaces
Sometimes it
is useful to define an interface that is entirely empty. A class can
implement this interface simply by naming it in its
implements clause without having to implement any
methods. In this case, any instances of the class become valid
instances of the interface. Java code can check whether an object is
an instance of the interface using the instanceof operator,
so this technique is a useful way to provide additional information
about an object.The java.io.Serializable
interface is a marker interface of this sort. A
class implements Serializable interface to tell
ObjectOutputStream that its instances may safely
be serialized.
java.util.RandomAccess is another example:
java.util.List implementations implement this
interface to advertise that they provide fast random access to the
elements of the list. ArrayList implements
RandomAccess, for example, while
LinkedList does not. Algorithms that care about
the performance of random-access operations can test for
RandomAccess like this:
// Before sorting the elements of a long arbitrary list, we may want to make
// sure that the list allows fast random access. If not, it may be quicker
// make a random-access copy of the list before sorting it.
// Note that this is not necessary when using java.util.Collections.sort().
List l = ...; // Some arbitrary list we're given
if (l.size() > 2 && !(l instanceof RandomAccess)) l = new ArrayList(l);
sortListInPlace(l);
3.9.5. Interfaces and Constants
As noted
earlier, constants can appear in an
interface definition. Any class that implements an interface inherits
the constants it defines and can use them as if they were defined
directly in the class itself. Importantly, there is no need to prefix
the constants with the name of the interface or provide any kind of
implementation of the constants.When a set of constants is used by more than one class, it is
tempting to define the constants once in an interface and then have
any classes that require the constants implement the interface. This
situation might arise, for example, when client and server classes
implement a network protocol whose details (such as the port number
to connect to and listen on) are captured in a set of symbolic
constants. As a concrete example, consider the
java.io.ObjectStreamConstants interface, which
defines constants for the object serialization protocol and is
implemented by both ObjectInputStream and
ObjectOutputStream.The primary benefit of inheriting constant definitions from an
interface is that it saves typing: you don't need to
specify the type that defines the constants. Despite its use with
ObjectStreamConstants, this is not a recommended
technique. The use of constants is an implementation detail that is
not appropriate to declare in the implements
clause of a class signature.A better approach is to define constants in a class and use the
constants by typing the full class name and the constant name. In
Java 5.0 and later, you can save typing by importing the constants
from their defining class with the import static
declaration. See Section 2.10 in
Chapter 2 for details.