Java Cookbook, Second Edition

Problem

You want to provide callbacks , that is, have unrelated classes call back into your code.

Solution

One way is to use a Java interface.

Discussion

An interface is a class-like entity that can contain only abstract methods and final fields. As we've seen, interfaces are used a lot in Java! In the standard API, the following are a few of the commonly used interfaces:

  • Runnable, Comparable, and Cloneable (in java.lang)

  • List, Set, Map, and Enumeration/Iterator (in the Collections API; see Chapter 7)

  • ActionListener, WindowListener, and others (in the AWT GUI; see Recipe 14.4)

  • Driver, Connection, Statement, and ResultSet (in JDBC; see Recipe 20.4)

Subclass, Abstract Class, or Interface?

There is usually more than one way to skin a cat. Some problems can be solved by subclassing, by use of abstract classes, or by interfaces. The following general guidelines may help:

  • Use an abstract class when you want to provide a template for a series of subclasses, all of which may inherit some of their functionality from the parent class but are required to implement some of it themselves. (Any subclass of a geometric Shapes class might have to provide a computeArea( ) method; since the top-level Shapes class cannot do this, it would be abstract. This is implemented in Recipe 9.8.)

  • Subclass whenever you want to extend a class and add some functionality to it, whether the parent class is abstract or not. See the standard Java APIs and the examples in Recipes Recipe 1.14, Recipe 5.11, Recipe 9.12, Recipe 10.10, and others throughout this book.

  • Subclass when you are required to extend a given class. Applets (see Recipe 18.2), servlets, and others use subclassing to ensure "base" functionality in classes that are dynamically loaded (see Recipe 25.3).

  • Define an interface when there is no common parent class with the desired functionality and when you want only certain unrelated classes to have that functionality (see the PowerSwitchable interface in Recipe 9.7).

  • Use interfaces as "markers" to indicate something about a class. The standard API uses Cloneable (Recipe 9.4) and Serializable (Recipe 10.18) as markers.

  • The " remote interface" the contact between the client and the server is specified as an Interface (in RMI, CORBA, and EJB)

Suppose we are generating a futuristic building management system. To be energy-efficient, we want to be able to remotely turn off (at night and on weekends) such things as room lights and computer monitors, which use a lot of energy. Assume we have some kind of "remote control" technology. It could be a commercial version of BSR's house-light control technology X10, it could be Bluetooth or 802.11 it doesn't matter. What matters is that we have to be very careful what we turn off. It would cause great ire if we turned off computer processors automatically people often leave things running overnight. It would be a matter of public safety if we ever turned off the building emergency lighting.[1]

[1] Of course these lights wouldn't have remote power-off. But the computers might, for maintenance purposes.

So we've come up with the design shown in Figure 9-1.

Figure 9-1. Classes for a building management system

The code for these classes is not shown (it's pretty trivial) but it's in the online source. The top-level classes BuildingLight and Asset are abstract classes. You can't instantiate them, as they don't have any specific functionality. To ensure both at compile time and at runtime that we can never switch off the emergency lighting, we need only ensure that the class representing it, EmergencyLight, does not implement the PowerSwitchable interface.

Note that we can't very well use direct inheritance here. No common ancestor class includes both ComputerMonitor and RoomLights that doesn't also include ComputerCPU and EmergencyLight. Use interfaces to define functionality in unrelated classes.

How we use these is demonstrated by the BuildingManagement class; this class is not part of the hierarchy shown in Figure 9-1, but instead uses a collection (actually an array, to make the code simpler for illustrative purposes) of Asset objects from that hierarchy.

Items that can't be switched must nonetheless be in the database, for various purposes (auditing, insurance, and so on). In the method that turns things off, the code is careful to check whether each object in the database is an instance of the PowerSwitchable interface. If so, the object is casted to PowerSwitchable so that its powerDown( ) method can be called. If not, the object is skipped, thus preventing any possibility of turning out the emergency lights or shutting off a machine that is busy running Seti@Home, downloading a big MP3 playlist, or performing system backups.

/** * BuildingManagement - control an energy-saving building. * This class shows how we might control the objects in an office * that can safely be powered off at nighttime to save energy - lots of * it, when applied to a large office! */ public class BuildingManagement { Asset things[] = new Asset[24]; int numItems = 0; /** goodNight is called from a timer Thread at 2200, or when we * get the "shutdown" command from the security guard. */ public void goodNight( ) { for (int i=0; i<things.length; i++) if (things[i] instanceof PowerSwitchable) ((PowerSwitchable)things[i]).powerDown( ); } // goodMorning( ) would be the same, but call each one's powerUp( ). /** Add an Asset to this building */ public void add(Asset thing) { System.out.println("Adding " + thing); things[numItems++] = thing; } /** The main program */ public static void main(String[] av) { BuildingManagement b1 = new BuildingManagement( ); b1.add(new RoomLights(101)); // control lights in room 101 b1.add(new EmergencyLight(101)); // and emerg. lights. // add the computer on desk#4 in room 101 b1.add(new ComputerCPU(10104)); // and its monitor b1.add(new ComputerMonitor(10104)); // time passes, and the sun sets... b1.goodNight( ); } }

When you run this program, it shows all the items being added, but only the PowerSwitchable ones being switched off:

> java BuildingManagement Adding RoomLights@2dc77f32 Adding EmergencyLight@2e3b7f32 Adding ComputerCPU@2e637f32 Adding ComputerMonitor@2f1f7f32 Dousing lights in room 101 Dousing monitor at desk 10104 >

Категории