February 1998
Extensibility and reusability: Abstract classes and interfaces
by Qusay H. Mahmoud
You can use both abstract classes and interfaces to define functionality
shared by a group of objects. However, sometimes we're not sure whether we
should share implementation or maintain independent implementations. Then,
we must choose between an abstract class and an interface. In this
article, we'll introduce you to abstract classes and interfaces, then
we'll show you the differences between them and the benefits of each.
Abstract classes
An abstract class is a basic class that's incomplete. It's called abstract
because it has no direct instances--that is, you can't directly create
(with the new operator) any object from an abstract class. However,
descendant classes (also known as concrete classes) may have direct
instances. Only abstract classes can have abstract methods--that is,
methods that are declared but have no implementation. As an example of an
abstract class, consider the following:
abstract class Shapes {
int x = 0;
int y = 0;
void move (int dx, int dy) {
x += dx;
y += dy;
draw();
}
abstract void draw(Graphics g);
}
In the previous example, we declare the class Shapes as abstract because
it contains a declaration of an abstract method named draw(). Since Shapes
is declared as an abstract class, you can create no direct instances from
this class. Thus, a compile-time error occurs if you try to create an
instance of the abstract class Shapes. So, a statement such as
Shapes s = new Shapes();
would result in a compile-time error. Also, if you attempt to instantiate
an abstract class using the newInstance method, java.lang.Class will cause
an exception (InstantiationException) to be thrown.
It's important not to mix abstract classes with final classes. A class is
declared final if no subclasses are desired. Also note that if a class is
declared final, it can't be declared abstract; if it is, a compile-time
error occurs. The same error occurs if a class attempts to extend a final
class.
Inheritance
In most object-oriented languages, classes can be defined in terms of
other classes, and objects are actually instances of classes. Classes can
be organized in a hierarchy. A subclass inherits attributes from a
superclass at a higher level in the hierarchy. Also, a subclass can add
more variables and methods to the ones inherited from the superclass, as
well as override inherited methods by providing specialized
implementations for those methods.
In Java, classes can have only one superclass; that is to say, Java has
support only for single inheritance. However, note that a class may
implement multiple interfaces. As an example, we'll inherit from the above
abstract class named Shapes.
class Circle extends Shapes {
public void draw(Graphics g) {
g.drawOval(someParameters);
}
}
The class Circle above inherits the move() method from Shapes and provides
implementation for it. The Circle class must provide implementation for
the draw() method. Now, you can create instances from the Circle class. We
mentioned above that you can't create instances from an abstract class.
However, note that you can initialize a Shapes variable with a reference
to any subclass of Shapes. Since the class Circle isn't abstract, the
statement
Shapes s = new Circle();
is correct. This simple statement will execute the default constructor of
the abstract class Shapes and field initializers for x and y of Shapes.
Interfaces
An interface in Java consists of a series of declarations that are subject
to the following two restrictions:
Method declarations must not include implementation code.
Variable declarations can only have constant initializations.
interface Shape{
int x = 0;
int y = 0;
void move(int dx, int dy);
}
In the above example, Shape is the name of the interface, and move is a
method declared inside the Shape interface. Note that every interface is
implicitly abstract, so the use of the abstract modifier is obsolete.
We've also mentioned that variable declarations in an interface can only
have constant initializers, but now you may wonder why the variables x and
y in the interface Shape aren't constant (using the public static final
modifiers). The fact is, every field declared in the body of an interface
is implicitly public static final. Thus, specifying any of these modifiers
for such fields is considered to be redundant and discouraged.
The whole purpose of an interface is to identify a common set of methods
and constants for the group of classes that implements the interface. When
a class implements one or more interfaces, the implemented interface(s) is
(are) identified by the implement clause of the class declaration. For
example, the following Triangle class implements the Shape interface:
class Triangle implements Shape {
void move(int dx, int dy){
// provide implementation here
}
}
When to use what
Interfaces are actually more useful than they might first appear. They
take us from writing one-shot classes to writing extensible packages and
frameworks of classes. If a set of classes happens to offer the same
service, then the classes can share the same interface. As an example,
suppose we write an application for manipulating (drawing, moving,
removing) different shapes on the screen. For simplicity, assume we want
to draw three shapes: circle, rectangle, and triangle.
A one-shot job may be to write an independent class for each shape and
have all the operations that can be performed on that shape encapsulated
within the corresponding class. However, if we decide to add a new shape,
then we must write a new class and modify the main application where we
choose what to draw. Consequently, this approach is neither reusable nor
extensible.
Knowing that the three shapes share some common characteristics (for
instance, they can be drawn, moved, and removed), we can have an interface
that all shapes share. This interface can be as simple as the following:
interface Shapes {
void draw(Graphics g);
void move(int offsetX, int offsetY);
void erase(); // paint with background color
}
The interface Shapes declares three operations: draw, move, and erase.
Now, each shape can implement this interface. For example, the Circle
shape can implement the interface as follows:
class Circle implements Shapes {
public int x, y, r;
public Circle(int x1, int y1, int radius) {
// constructor
x = x1;
y = y1;
radius = r;
}
public void draw(Graphics g) {
g.drawOval(x-r, y-r, 2*r, 2*r);
}
public void move(int oldX, int oldY) {
x += oldX;
y += oldY;
}
public void erase() {
// paint the region with background color
}
}
We can do the same for the other two shapes, namely, Triangle and
Rectangle.
Of course, we could achieve the same objective by using an abstract class
instead of an interface. In this case, it doesn't really matter whether we
use an interface or an abstract class. Interfaces, however, are a very
useful tool for remote method invocations. For example, if you're familiar
with Java RMI (Remote Method Invocation), you know that the first order of
business in writing an RMI application is to define a public remote
interface. Such an interface for an arithmetic server might resemble the
following:
public interface MathServer extends
java.rmi.Remote {
int[] Add(int a[], int b[]) throws
java.rmi.RemoteException;
// other methods
}
The whole purpose of using the interface is so that a programmer for a
client of this interface can tell what operations the arithmetic server
provides and how to use them--just by looking at the interface. An
abstract class isn't a good choice in this example, since an abstract
class may have some non-abstract methods (i.e., methods with
implementations). Including non-abstract methods allows the client to look
at those implementations, which is a situation we may want to protect
against.
Multiple inheritance
Since Java doesn't support multiple inheritance, it actually avoids a lot
of troubles surrounding ambiguities. In essence, however, Java replaces
multiple inheritance with conformance to interfaces. While a class in Java
can't inherit from more than one base class, it is allowed to implement
multiple interfaces. This capability lets you achieve the same goals as
multiple inheritance, but without the ambiguities.
Suppose, for example, that there's a class named Calculate. This class
provides methods for calculating areas and other parameters for various
shapes. If we did inherit from this class, then we could write something
like this:
class Circle extends Calculate implements Shapes {
//
// provide implementation for methods and override others
}
If we don't have interfaces, then we won't be able to simulate multiple
inheritance, since inheriting from multiple base classes isn't allowed.
One last thing to note is that interfaces can actually inherit from each
other, as do classes.
Conclusion
Abstract classes and interfaces are alike in the sense that neither is
directly instantiable via the new operator. Both are vital tools for
writing extensible packages and frameworks of classes.
Warning: Failed opening 'vjp/includes/vjpbottom.htm' for inclusion
(include_path='.:/web/zdj/pages:/web/zdj/include:/web/zdj/security:/web/zdj/pages/content:/web/zdj/include/includes')
in /web/zdj/pages/vjp/s_vjp/9802/vjp9826.htm on line 273