Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 2 of 7
There is a reason why interfaces do not implement reference types: flexibility. Consider a starting and stopping reference type. You can use that reference type to indicate that you wish to start/stop a vehicle, an electric saw, a lawn mower, a washing machine, court proceedings, an expedition, a criminal investigation, and so on. If you implement that reference type as a class, you assign an implementation to the reference type. Once done, you can only use that reference type in the context of its implementation. In other words, you lose the flexibility of the starting and stopping reference type when you implement that type as a class. To preserve the generality of the starting and stopping reference type, you need to use an interface.
When you want to introduce, but not implement, a reference type in source code, you must declare an interface with the following syntax:
[ 'public' ] [ 'abstract' ] 'interface' interface_name
'{'
// constants and method signature declarations
'}'
An interface declaration minimally consists of keyword interface followed by an identifer -- interface_name -- that names the interface (and must not be a reserved word). By convention, an interface name begins with a capital letter,
and you capitalize the first letter of all subsequent words that compose that name. To make interface_name accessible to all classes in all packages (a concept I will discuss in a future column), precede keyword interface with keyword public. Also, to emphasize the abstractness of an interface, prefix keyword interface with keyword abstract. (Because specifying abstract is redundant, you can leave it out of your code.) A brace-delimited block follows the declaration header. Use that block
to present an interface's members: constants and method signatures. For your first taste of interfaces, examine Listing 1:
Listing 1. StartStop.java
// StartStop.java
interface StartStop
{
void start ();
void stop ();
}
The StartStop interface declaration introduces a reference type into source code. That reference type identifies two operations: starting, by way of the start() method signature, and stopping, by way of the stop()method signature. As with the interface header, you can include keyword abstract in each method signature, but it is redundant to do so. For example, you could declare the StartStop interface as follows:
abstract interface StartStop
{
abstract void start ();
abstract void stop ();
}
Just as classes extend other classes, interfaces can extend other interfaces (but not classes). Interface extension permits type specialization through additional method signatures. To extend an interface, append the following extends-clause syntax to an interface declaration header:
'extends' interface_name [ ',' interface_name ... ]
An extends clause begins with keyword extends and continues with a comma-delimited list of identifiers that name interfaces. You can think of the interface performing
the extension as a subinterface and each named interface in the extends clause as a superinterface. Listing 2's DiagnoseStartStop source code demonstrates the use of interface extension to declare a subinterface of Listing 1's StartStop interface:
Listing 2. DiagnoseStartStop.java
// DiagnoseStartStop.java
interface DiagnoseStartStop extends StartStop
{
boolean NOT_STARTED = false;
boolean STARTED = true;
boolean isStarted ();
}
The DiagnoseStartStop interface extends the StartStop interface by introducing a diagnostic capability. (Why might you want such a capability? Consider situations in which a lawn
mower does not start because it has no gas and when a conference fails to begin because the guest speaker has not arrived.)
The diagnostic capability takes the form of an isStarted() method and a pair of constants: NOT_STARTED and STARTED. (Note: Even though you cannot see keywords public, final, and static, NOT_STARTED and STARTED are implicitly public, static, and final. You can explicitly specify those keywords in an interface's constant declaration, but it is redundant to do so.)
Java's standard class library contains several empty interfaces that declare no constants or method signatures. Java uses
empty interfaces to tag, that is, mark object classes that are to be singled out for special treatment. Empty interface examples include Cloneable, Serializable, and UIResource.
An interface is typically left empty to notify various APIs that they can perform certain operations on objects whose classes
implement those interfaces. For example, a class implements Serializable to tell the Serializable API, which I will explore in a future column, to save and restore the data items in that class's
objects.
Once you declare an interface, that interface accomplishes nothing until you specify an implementation for its operations in a class. Use the following syntax to implement an interface in a class:
'class' class_name [ 'extends' superclass_name ]
[ 'implements' interface_name [ ',' interface_name ... ] ]
The syntax specifies an implements clause that you append to a class header. An implements clause begins with keyword implements and continues with a comma-delimited list of interface_name identifiers, where each identifier identifies an interface. The implements clause forces a class to inherit all constants
and/or method signatures in that clause's interfaces, a form of inheritance commonly known as interface inheritance.
To implement an interface, it is not sufficient to specify only an implements clause. If you do, you end up with an abstract
class -- a concept I will explore in next month's column. In addition to identifying an implements clause, you must attach
code bodies to the interface's method signatures. Listing 3 demonstrates interface implementation, by implementing the StartStop interface in a Vehicle class:
Listing 3. UseStartStop.java
// UseStartStop.java
class Vehicle implements StartStop
{
private String name;
Vehicle (String name)
{
this.name = name;
}
String getName ()
{
return name;
}
public void start ()
{
}
public void stop ()
{
}
}
class Car extends Vehicle
{
Car (String name)
{
super (name);
}
public void start ()
{
System.out.println ("Insert key into ignition and turn.");
}
public void stop ()
{
System.out.println ("Turn key and remove from ignition.");
}
public String toString ()
{
return getName ();
}
}
class UseStartStop
{
public static void main (String [] args)
{
Car c = new Car ("???");
c.start ();
c.stop ();
System.out.println (c.toString ());
StartStop ss = c;
ss.start ();
ss.stop ();
System.out.println (ss.toString ());
}
}
For the most part, UseStartStop's source code should be easy to understand. However, three items deserve mention. First, notice the public keyword that prefixes the start() and stop() method signatures in classes Vehicle and Car. That keyword is not part of the start() and stop() method signatures in the earlier StartStop interface. What is going on? Java considers all method signatures that appear in an interface to represent public methods
whether or not the public keyword is present. Java does not allow more restrictive method access in a class that inherits from either a class or an
interface. Also, a public method is the most accessible method. Because of those two facts, keyword public must appear in a method declaration when a class either extends a class that declares that public method (and chooses to
override the method), or implements an interface that specifies that method's signature.