Chapter 5

Using Abstract Classes and Interfaces

IN THIS CHAPTER

check Understanding abstract methods and classes

check Using basic interfaces

check Using interfaces as types

check Adding constants to an interface

check Inheriting interfaces

check Working with callbacks

check Using default methods

In this chapter, you find out how to use two similar but subtly distinct features: abstract classes and interfaces. Both let you declare the signatures of the methods and fields that a class implements separately from the class itself. Abstract classes accomplish this by way of inheritance. Interfaces do it without using inheritance, but the effect is similar. This chapter also covers a feature called default methods, which are designed to make interfaces easier to work with.

Using Abstract Classes

Java lets you declare that a method or an entire class is abstract, which means that the method has no body. An abstract method is just a prototype for a method: a return type, a name, a list of parameters, and (optionally) a throws clause.

To create an abstract method, you specify the modifier abstract and replace the method body with a semicolon:

public abstract int hit(int batSpeed);

Here the method named hit is declared as an abstract method that returns an int value and accepts an int parameter.

A class that contains at least one abstract method is called an abstract class and must be declared with the abstract modifier on the class declaration. For example:

public abstract class Ball
{
public abstract int hit(int batSpeed);
}

warning If you omit the abstract modifier from the class declaration, the Java compiler coughs up an error message to remind you that the class must be declared abstract.

An abstract class can’t be instantiated. Thus, given the preceding declaration, the following line doesn’t compile:

Ball b = new Ball(); // error: Ball is abstract

The problem here isn’t with declaring the variable b as a Ball; it’s using the new keyword with the Ball class in an attempt to create a Ball object. Because Ball is an abstract class, you can use it to create an object instance.

You can create a subclass from an abstract class like this:

public class BaseBall extends Ball
{
public int hit(int batSpeed)
{
// code that implements the hit method goes here
}
}

When you subclass an abstract class, the subclass must provide an implementation for each abstract method in the abstract class. In other words, it must override each abstract method with a nonabstract method. (If it doesn’t, the subclass is also abstract, so it can’t be instantiated either.)

tip Abstract classes are useful when you want to create a generic type that is used as the superclass for two or more subclasses, but the superclass itself doesn’t represent an actual object. If all employees are either salaried or hourly, for example, it makes sense to create an abstract Employee class and then use it as the base class for the SalariedEmployee and HourlyEmployee subclasses.

Here are a few additional points to ponder concerning abstract classes:

  • Not all the methods in an abstract class have to be abstract. A class can provide an implementation for some of its methods but not others. In fact, even if a class doesn’t have any abstract methods, you can still declare it as abstract. (In that case, the class can’t be instantiated.)

    technicalstuff A private method can’t be abstract. That makes sense, because a subclass can’t override a private method, and abstract methods must be overridden.

  • Although you can’t create an instance of an abstract class, you can declare a variable by using an abstract class as its type. Then use the variable to refer to an instance of any of the subclasses of the abstract class.
  • A class can’t specify both abstract and final. That would cause one of those logical paradoxes that result in the annihilation of the universe. Well, ideally, the effect would be localized. But the point is that because an abstract class can be used only if you subclass it, and because a final class can’t be subclassed, letting you specify both abstract and final for the same class doesn’t make sense.

    technicalstuff Abstract classes are used extensively in the Java API. Many of the abstract classes have names that begin with Abstract — such as AbstractBorder, AbstractCollection, and AbstractMap — but most of the abstract classes don’t. The InputStream class (used by System.in) is abstract, for example.

Using Interfaces

An interface is similar to an abstract class, but an interface can include only abstract methods and final fields (constants), and an interface can’t be used as a base class. A class implements an interface by providing code for each method declared by the interface.

tip Interfaces have two advantages over inheritance:

  • Interfaces are easier to work with than an abstract class, because you don’t have to worry about providing any implementation details in the interface.
  • A class can extend only one other class, but it can implement as many interfaces as you need.

The following sections describe the details of creating and using interfaces.

Creating a basic interface

Here’s a basic interface that defines a single method, named Playable, that includes a single method named play:

public interface Playable
{
void play();
}

This interface declares that any class that implements the Playable interface must provide an implementation for a method named play that accepts no parameters and doesn’t return a value.

This interface has a few interesting details:

  • The interface itself is declared as public so that it can be used by other classes. Like a public class, a public interface must be declared in a file with the same name. Thus this interface must be in a file named Playable.java.
  • The name of the interface (Playable) is an adjective. Most interfaces are named with adjectives rather than nouns because they describe some additional capability or quality of the classes that implement the interface. Thus classes that implement the Playable interface represent objects that can be played.

    tip In case you haven’t been to English class in a while, an adjective is a word that modifies a noun. You can convert many verbs to adjectives by adding -able to the end of the word — playable, readable, drivable, and stoppable, for example. This type of adjective is commonly used for interface names.

  • Another common way to name interfaces is to combine an adjective with a noun to indicate that the interface adds some capability to a particular type of object. You could call an interface that provides methods unique to card games CardGame, and this interface might have methods such as deal, shuffle, and getHand.
  • technicalstuff All the methods in an interface are assumed to be public and abstract. If you want, you can code the public and abstract keywords on interface methods. That kind of coding is considered to be bad form, however, because it might indicate that you think the default is private and not abstract.

Implementing an interface

To implement an interface, a class must do two things:

  • It must specify an implements clause on its class declaration.
  • It must provide an implementation for every method declared by the interface.

Here’s a class that implements the Playable interface:

public class TicTacToe implements Playable
{
// additional fields and methods go here

public void play()
{
// code that plays the game goes here
}
// additional fields and methods go here
}

Here the declaration for the TicTacToe class specifies implements Playable. Then the body of the class includes an implementation of the play method.

tip A class can implement more than one interface:

public class Hearts implements Playable, CardGame
{
// must implement methods of the Playable
// and CardGame interfaces
}

Here, the Hearts class implements two interfaces: Playable and CardGame.

tip A class can possibly inherit a superclass and implement one or more interfaces. Here’s an example:

public class Poker extends Game
implements Playable, CardGame
{
// inherits all members of the Game class
// must implement methods of the Playable
// and CardGame interfaces
}

Using an interface as a type

In Java, an interface is a kind of type, just like a class. As a result, you can use an interface as the type for a variable, parameter, or method return value.

Consider this snippet of code:

Playable game = getGame();
game.play();

Here I assume that the getGame method returns an object that implements the Playable interface. This object is assigned to a variable of type Playable in the first statement. Then the second statement calls the object’s play method.

For another (slightly more complex) example, suppose that you have an interface named Dealable defining a method named deal that accepts the number of cards to deal as a parameter:

public interface Dealable
{
void deal(int cards);
}

Now suppose that you have a method called startGame that accepts two parameters: a Dealable object and a String that indicates what game to play. This method might look something like this:

private void startGame(Dealable deck, String game)
{
if (game.equals("Poker"))
deck.deal(5);
else if (game.equals("Hearts"))
deck.deal(13);
else if (game.equals("Gin"))
deck.deal(10);
}

Assuming that you also have a class named CardDeck that implements the Dealable interface, you might use a statement like this example to start a game of Hearts:

Dealable d = new CardDeck();
startGame(d, "Hearts");

Notice that the variable d is declared as a Dealable. You could just as easily declare it as a CardDeck:

CardDeck d = new CardDeck();
startGame(d, "Hearts");

Because the CardDeck class implements the Dealable interface, it can be passed as a parameter to the startGame method.

More Things You Can Do with Interfaces

There’s more to interfaces than just creating abstract methods. The following sections describe some additional interesting things you can do with interfaces. Read on….

Adding fields to an interface

Besides abstract methods, an interface can include final fields — that is, constants. Interface fields are used to provide constant values that are related to the interface. For example:

public interface GolfClub
{
int DRIVER = 1;
int SPOON = 2;
int NIBLICK = 3;
int MASHIE = 4;
}

Here any class that implements the GolfClub interface has these four fields (that is, constants) available.

technicalstuff Note that interface fields are automatically assumed to be static, final, and public. You can include these keywords when you create interface constants, but you don’t have to.

Extending interfaces

You can extend interfaces by using the extends keyword. An interface that extends an existing interface is called a subinterface, and the interface being extended is called the superinterface.

When you use the extends keyword with interfaces, all the fields and methods of the superinterface are effectively copied into the subinterface. Thus the subinterface consists of a combination of the fields and methods in the superinterface plus the fields and methods defined for the subinterface.

Here’s an example:

public interface ThrowableBall
{
void throwBall();
void catchBall();
}
public interface KickableBall
{
void kickBall();
void catchBall();
}
public interface PlayableBall
extends ThrowableBall, KickableBall
{
void dropBall();
}

Here three interfaces are declared. The first, named ThrowableBall, defines two methods: throwBall and catchBall. The second, named KickableBall, also defines two methods: kickBall and catchBall. The third, named PlayableBall, extends ThrowableBall and KickableBall, and adds a method of its own named dropBall.

Thus any class that implements the PlayableBall interface must provide an implementation for four methods: throwBall, catchBall, kickBall, and dropBall. Note that because the catchBall methods defined by the ThrowableBall and KickableBall interfaces have the same signature, only one version of the catchBall method is included in the PlayableBall interface.

Using interfaces for callbacks

In the theater, a callback is when you show up for an initial audition, they like what they see, and they tell you that they want you to come back so they can have another look.

In Java, a callback is sort of like that. It’s a programming technique in which an object lets another object know that the second object should call one of the first object’s methods whenever a certain event happens. The first object is called an event listener because it waits patiently until the other object calls it. The second object is called the event source because it’s the source of events that result in calls to the listener.

Okay, my theater analogy was a bit of a stretch. Callbacks in Java aren’t really that much like callbacks when you’re auditioning for a big part. A callback is more like when you need to get hold of someone on the phone, and you call him when you know he isn’t there and leave your phone number on his voicemail so he can call you back.

Callbacks are handled in Java by a set of interfaces designed for this purpose. The most common use of callbacks is in graphical applications built with Swing, where you create event listeners that handle user-interface events, such as mouse clicks.

You find out all about Swing in Book 6. For now, I look at callbacks by using the Timer class, which is part of the javax.Swing package. This class implements a basic timer that generates events at regular intervals — and lets you set up a listener object to handle these events. The listener object must implement the ActionListener interface, which defines a method named actionPerformed that’s called for each timer event.

The Timer class constructor accepts two parameters:

  • The first parameter is an int value that represents how often the timer events occur.
  • The second parameter is an object that implements the ActionListener interface. This object’s actionPerformed method is called when each timer event occurs.

The ActionListener interface is defined in the java.awt.event package. It includes the following code:

public interface ActionListener extends EventListener {
/**
* Invoked when an action occurs.
*/
public void actionPerformed(ActionEvent e);
}

As you can see, the ActionListener interface consists of a single method named actionPerformed. It receives a parameter of type ActionEvent, but you don’t use this parameter here. (You do use the ActionEvent class in Book 6.)

The Timer class has about 20 methods, but I talk about only one of them here: start, which sets the timer in motion. This method doesn’t require any parameters and doesn’t return a value.

Listing 5-1 shows a program that uses the Timer class to alternately display the messages Tick… and Tock… on the console at 1-second intervals. The JOptionPane class is used to display a dialog box; the program runs until the user clicks the OK button in this box. Figure 5-1 shows the Tick Tock program in action. (Actually it takes a while for the JOptionPane class to shut down the timer, so you’ll see a few extra tick-tocks after clicking OK.)

image

FIGURE 5-1: The Tick Tock application in action.

LISTING 5-1 The Tick Tock Program

import java.awt.event.*; →1
import javax.swing.*; →2
public class TickTock
{
public static void main(String[] args)
{
// create a timer that calls the Ticker class
// at one second intervals
Timer t = new Timer(1000, new Ticker()); →10
t.start(); →11
// display a message box to prevent the
// program from ending immediately
JOptionPane.showMessageDialog(null, →15
"Click OK to exit program");
}
}
class Ticker implements ActionListener →20
{
private boolean tick = true; →22
public void actionPerformed(ActionEvent event) →24
{
if (tick)
{
System.out.println("Tick…"); →28
}
else
{
System.out.println("Tock…"); →32
}
tick = !tick; →34
}
}

The following paragraphs describe the important details of this program’s operation:

  1. →1 The ActionListener interface is part of the java.awt.event package, so this import statement is required.
  2. →2 The Timer class is part of the javax.swing package, so this import statement is required.
  3. →10 This statement creates a new Timer object. The timer’s interval is set to 1,000 milliseconds — which is equivalent to 1 second. A new instance of the Ticker class is passed as the second parameter. The timer calls this object’s actionPerformed method at each timer tick — in other words, once per second.
  4. →11 This statement calls the start method to kick the timer into action.
  5. →15 The JOptionPane class is used to display a dialog box that tells the user to click the OK button to stop the application. You might think I include this dialog box to give the user a way to end the program. In reality, I use it to give the timer some time to run. If I just end the main method after starting the timer, the application ends, which kills the timer. Because I use JOptionPane here, the application continues to run as long as the dialog box is displayed. (For more information about JOptionPane, see Book 2, Chapter 2.)
  6. →20 This line is the declaration of the Ticker class, which implements the ActionListener interface.
  7. →22 This line is a private boolean class field that’s used to keep track of whether the Ticker displays Tick… or Tock… Each time the actionPerformed method is called, this field is toggled.
  8. →24 This line is the actionPerformed method, which is called at each timer interval.
  9. →28 This line prints Tick… on the console if tick is true.
  10. →32 This line prints Tock… on the console if tick is false.
  11. →34 This line toggles the value of the tick variable. In other words, if tick is true, it’s set to false. If tick is false, it’s set to true.

Using Default Methods

Although interfaces are an incredibly useful feature of Java, they have an inherent limitation: After you define an interface and then build classes that implement the interface, there’s no easy way to modify the interface by adding additional methods to it.

For example, suppose you have created the following interface:

public interface Playable
{
void play();
}

You then build several classes that implement this interface. Here’s a simple example:

class Game implements Playable
{
public void play()
{
System.out.println("Good luck!");
}
}

This is a pretty pointless game, of course; it simply prints the message “Good luck!” whenever the play method is called.

Now suppose that you decide that the Playable interface should have an additional feature — specifically, you want to add the ability to end the game by calling a method named quit.

You’d be tempted to just add the new method to the existing interface, like this:

public interface Playable
{
void play();
void quit();
}

Unfortunately, however, doing so will break the Game class because it doesn’t provide an implementation of the quit method.

You could, of course, modify the Game class by adding an implementation of the quit method. But what if you have written dozens, or even hundreds, of classes that implement Playable? As you can imagine, once an interface has become popular, it becomes nearly impossible to modify.

To alleviate this problem, Java 1.8 introduces a new type of interface method called a default method, which supplies code that will be used as the implementation of the method for any classes that implement the interface but do not themselves provide an implementation for the default method.

Thus, Java 8 allows you to safely add the quit method to the Playable interface by specifying it as a default method, like this:

interface Playable
{
void play();
default void quit()
{
System.out.println("Sorry, quitting is not allowed.");
}
}

Here the Playable interface specifies that if an implementing class does not provide an implementation of the quit method, the default method will be used. In this case, the default method simply prints the message "Sorry, quitting is not allowed."

Note that the preceding example won’t compile on versions of Java prior to 1.8. If you get an error message when you try to compile an interface that uses the default keyword, check your Java version to make sure you’re running version 1.8 or later.

Here’s a complete example that uses the Playable interface and its default method:

public class TestLambdaCollection
{
public static void main(String[] args)
{
Game g = new Game();
g.play();
g.quit();
}
}
interface Playable
{
void play();
default void quit()
{
System.out.println("Sorry, quitting is not allowed.");
}
}
class Game implements Playable
{
public void play()
{
System.out.println("Good luck!");
}
}

When you run this program, the following will be displayed on the console:

Good luck!
Sorry, quitting is not allowed.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset