Chapter 10

Understanding Java Interfaces and Abstract Classes

This book now arrives at the coup de grace of object-oriented programming: making use of abstract types. It can be a hard topic to get a handle on, but the payoff is huge. Once you’re able to think about method naming first and then decide how you’re going to make your methods work second, you’re ready for abstract types. It’s a tall order. Believe me, I know it. And so, in this chapter I’m going to use one extended example to tell a kind of story, with some side examples to make individual points along the way. Abstract typing is a design process, and design means working out relationships over multiple revisions. I’ll therefore spend less time touching on every compiler rule—although they’re still important—and more time on the power of writing programs by thinking about design first, implementation second.

In this chapter, we’ll cover the following topics:

  • Understanding abstract types
  • Using abstract classes
  • Using interfaces
  • Using the enum type
  • Distinguishing between abstract and concrete references

Understanding Abstract Types

In object-oriented programming, abstract types help answer a what-if question that arises when you need a type system. A type system describes any number of classes that use the polymorphic effect to share behavior, not just member names. Inheritance is the simplest way to achieve this effect, but it also creates dependencies and requirements that are sometimes too confining for your needs. With abstract types, you can create a type system that is more flexible and more dynamic than subclassing allows.

Let’s say you want to name some behavior (that is, a method) that you know you will implement differently for most subclasses. Let’s also say the behavior you have in mind, unlike the Child class’s play() method from Chapter 9, “Inheriting Code and Data in Java,” has no default logic or meaning of its own. The method name you have in mind merely characterizes the logic you’ll need, and that’s all a name can do.

If you’re writing a customer interface for a banking system, it’s easy to imagine you’ll need general behaviors such as deposit(), withdrawal(), transfer(), and getBalance(). It’s also clear these behaviors will have to work under one set of rules for a checking account, another for a retirement savings account, and still another for a certificate of deposit. So what’s a useful, general class name that takes all those products into consideration? Account? BankAccount? Product? FinancialInstrument? Whatever it is, you’d be smart to build a consensus on that key name early in the development process. If it’s going to sit at the top of a type system, other programmers and designers will have to stare at it a lot.

You still haven’t considered how the code will work—that’s a long way off in design. You just know that all the different products one bank offers ought to fit together into a type system somehow and that the way you’re going to make these things appear related to each other is by inheritance, as we discussed in Chapter 9.

So let’s put this idea in real-world terms, but let’s use something less controversial than banking for our chapter-long example. Let’s say I’m running a lawn care business. I’ve got six employees I can assign to six clients at one time. The general instructions for doing the work are always the same:

  • Clear the lawn of any debris.
  • Mow and edge the lawn.
  • Gather the clippings.
  • Water the lawn.

Of course, there’s no such thing as an abstract lawn or a general kind of lawn. There may be one type that’s common or popular, but that doesn’t mean it’s a suitable prototype for all other lawns. Even if I wanted to maintain a “standard lawn” on which to train new hires, what would that be? What I do instead is define what should be done to care for any lawn and then work out the details for each different type you encounter.

The conditions under which those operations are performed will vary from site to site. Some lawns are big, some are small. Some may have a small bit of debris to clear, some may have a lot. Some lawns may require no detail work, like edging or weeding, at all, while others may have grass racing down every crack in the sidewalk and driveway. The degree of work required for each task is made evident by the site itself. I therefore have to rely on my employees to apply my steps as it suits the conditions they find.

To promote the same idea of polymorphic behavior in Java, you communicate intention through the names you choose for your methods. The class name can help too, by expressing some collective meaning for those methods. Often a quick sketch of the operations will suggest the need for an organizing concept, as shown in Figure 10-1.

Figure 10-1: Sketch of the LawnService interface

c10f001.eps

All I’ve done in Figure 10-1 is mock up each discrete operation as a method and provide the concept name LawnService. I’ve given all of two minutes of thought to possible members and type names. It’s not a true class diagram. I haven’t applied Unified Modeling Language (UML) signifiers formally, much less correctly. I’ve only sketched out something that could be polished into a proper design later.

Now, assuming I have clients I want to remember and continue to please, I should offer to each site a concrete form of my services that matches their conditions and needs. Instead of capturing all the details for all jobs in one concept called LawnService, I can divide them into concrete services as shown in Figure 10-2.

I have applied one detail in particular to this diagram. I show the name LawnService in italics. This treatment implies that the LawnService collective is an abstraction, something that defines behaviors without necessarily providing them. As I am sure you can guess, I will translate each of these components to a Java class, but the italics help me signify a distinction between an abstract type and the various concrete types, including HackItBack, ShowItOff, TouchItUp, and KnockItDown.

Figure 10-2: Different LawnService jobs

c10f002.eps

The term implementation detail doesn’t mean writing code is easy or trivial, just that it doesn’t (and shouldn’t) alter the design.

I’ve also given each of the four services depicted here some detail to set them apart. The mower reference is represented by a different type for each class, suggesting the tool most appropriate to perform the job. In a proper diagram, you generally would not bother to illustrate that each type can have its own style of implementation. That’s not just implied; it’s expected. All each type has to do is support the declared methods. How they do it is commonly described as an implementation detail.

The subclass names I’ve chosen are colloquial by design. I want to suggest that these are intended as internal references—that is, classes that are not designed to communicate directly with clients. I only want my clients to call and ask for a LawnService job. Once I have categorized the job by reviewing the details of their site, I’ll decide which concrete type is appropriate and send the right employee with the right resources for the job.


Bear in mind that I’m using UML artifacts rather casually. Applying UML is an exacting discipline in large-scale application development.

Every client expects a LawnService job, and they will each get the service they’ve ordered. The actual product will emphasize some operations and downplay others, according to their site. As an example, Figure 10-3 depicts a use case that links a LawnService request to a HackItBack job. The note included in the diagram helps define the kind of work that such a job entails.

I think it’s important for my business image that I use only the term LawnService with my clients. They just have one lawn, after all, and they don’t need to know how I categorize it, nor do they need to learn the internal vocabulary of my business. I also don’t want to let out the idea that the client should have to make line-item decisions on which tools or resources are necessary. It’s my job to ascertain the client’s needs and return the right kind of service. Clients don’t need to study services they can’t use. I don’t need them to hear terms like hack or knock it down and get the wrong idea.

Figure 10-3: Use case for the HackItBack job

c10f003.eps

With just that much consideration, I’ve started to form a type system with a public interface and some number of encapsulated concrete types. I know what the business logic looks like, so now I want to separate the high-level operations of the business from the concrete details of the work. Now all I need is a class model that will help me maintain that separation.

Or that’s the dream, anyway. Before you can realize it in Java, you need to know how the language lets you create and express abstract types and what rules you must observe to use them.

Using Abstract Classes

certobjective.eps

An abstract method includes most of the usual things a regular method has: modifiers (including the abstract modifier), a return type, a name, and a parameter list. What it cannot include is a method body (also called the implementation). An abstract method looks like this example:

public abstract void testMe();

You can declare as many methods abstract as you like in one class. The compiler uses the modifier to enforce the absence of a statement body. If you forget the semicolon separator and add curly braces instead, the compiler will complain. By using this modifier, you also implicitly declare that the class that contains it is incomplete. Any class may be declared abstract, but if it has abstract methods, it must be declared abstract.

Why does that matter? Because classes declared abstract cannot be constructed. This kind of class lets you establish a type that provides methods, with some or all of them implemented, without allowing objects to be made from it. I suppose you could say it is the opposite of classes that are implicitly concrete—that is, made to produce objects.

To use an abstract class, you have to subclass it, even if it has no abstract methods. If it does, and you want to make a concrete subclass, then you must implement all methods declared abstract in the parent. If you don’t, you’ll make another abstract class. Also, the compiler will complain about it unless you declare it to be one.


You’ll see how enforcing an implementation is possible in the section “Understanding the Template Method Pattern” later in this chapter.

You can cheat the intention of an abstract method by overriding it and implementing an empty code body. If you’re worried only about getting past the compiler, that’s all you need to do. Usually, however, abstract classes are written in such a way that they depend on their abstract methods to do something useful. Consider the following example:

abstract class PokerNight {
private int white = 1;
private int red = 5;
private int blue = 25;
private int black;
public int startingValue() {
int total = 0;
      total += (25 * white);
      total += (20 * red);
      total += (15 * blue);
      total += (10 * blackValue());
return total;
   }
public abstract int blackValue();
}

The PokerNight class uses the method startingValue() to determine how many chips each player gets to start. It sets the number of chips each player receives and their values, except for the black chips. Depending on the game, you might want to value them each at 50 cents, 1 dollar, or more. All you have to do is subclass the PokerNight class and override the blackValue() method to return the desired amount:

class MyPokerNight extends PokerNight {
   @Override
public int blackValue() {
return 100;
   }
public static void main(String args[]) {
      MyPokerNight mpn = new MyPokerNight();
float buyin = mpn.startingValue()/100;
      System.out.println("Buy-in = " + buyin); 
   }
}
$ java MyPokerNight
Buy-in = 15.0

Many abstract classes do something like this. They integrate their abstract method(s) into the logic of their concrete methods. A programmer can use this approach to supply missing values or logic and customize the abstract class. “Stubbing out” abstract methods just so they satisfy the compiler can foul this intent. Ten black chips that are worth 0, for example, won’t add much life to a poker game.

Of course you should also check that you can’t actually construct a PokerNight object. You should see an error like this:

PokerNight.java:5: error: PokerNight is abstract; 
cannot be instantiated
      PokerNight pn = new PokerNight();
                          ^
1 error

Abstract classes do have constructors; they’re needed by subclasses to complete the construction process.

Because you can’t instantiate an abstract class, you can’t use it in another class as a field, except as a reference type for its subclasses. Any time you extend one and implement its methods, you end up with a concrete class from which you can make objects. Listing 10-1 shows the Concrete class extending the Abstract class:

Listing 10-1: Extending and implementing an abstract class

public abstract class DeclareAbstract {
public abstract void testMe();
}
final class Concrete extends DeclareAbstract {
   @Override
public void testMe() {
      System.out.println("Tested");
   }
public static void main(String args[]) {
      DeclareAbstract dab = new Concrete();
dab.testMe();
   }
}
$ javac DeclareAbstract.java
$ java Concrete
Tested

The Concrete class and testMe() method both drop the abstract modifier. In the main() method, I refer to the Concrete object with the DeclareAbstract variable dab, just to show that the same kind of relationship exists between parent and child type as discussed in Chapter 9. In fact, this use of types makes it clear that you cannot invoke code that belongs to the reference type. The reference defines which members of the referent are accessible, but they always belong to the referent.


Fields cannot be declared abstract.

As before, I could have implemented the testMe() method just by replacing the semicolon separator with curly braces. This override honors the letter of the contract but ignores the intent of it. If any problems arise from this code, you can catch them only by testing the program at runtime. A lazy implementation of an abstract class is bound to haunt you sooner or later; honor these contracts as fully as possible.

It stands to reason that you should not be able to declare an abstract class or method as final. It’s a self-defeating combination. If you could, you’d be able to make classes and methods that are incomplete and unavailable for subclassing and overriding. Fortunately, the compiler checks for this combination and disallows it. You cannot apply the static modifier to an abstract method for similar reasons: Binding an incomplete method to its class serves no useful purpose.

By understanding the rationale behind the compiler’s checks, you’ll better prepare yourself for exam questions that test your understanding of each modifier. Anywhere in this text where I recite a rule but don’t demonstrate it with test code, you should write the test yourself. It’s good practice, but more importantly, you’ll teach yourself the compiler’s exact responses. Some compiler complaints seem to bear no relationship to the code that raises them, so it’s well worth your time to learn these correlations. To get you started on that exercise, I’ve listed some key rules for the abstract modifier here.

The rules for an abstract class in Java are as follows:

  • It must include the abstract modifier in its declaration.
  • It cannot be declared final.
  • It can have constructors.

In addition, here are the rules for an abstract method in Java:

  • It must include the abstract modifier in its declaration.
  • It cannot be declared final or static.
  • It can be overloaded.
  • It must be overridden in a concrete subclass.

Understanding the Template Method Pattern

Here’s the thing about abstract methods and classes: You’re probably not going to use them very often. There are a couple of reasons why.


Chapter 9 discusses the use of super() to call an accessible constructor in the parent class.

Reason one is that a class is abstract only as a matter of degree. It can have zero or more methods in it that are declared abstract. The remainder of the class is still concrete in some sense; it can have state as expressed by its fields and behavior as expressed by its implemented methods. Although you can’t call an abstract class constructor with a reference, its subclasses must, either through the implicit construction process or by using the super() call.


A pure type is one that has no mutable state or code implementation.

An abstract class, therefore, is not a pure type, one that defines operations but has no behavior of its own. You can make a class purely abstract by declaring all of its methods that way. Unless you need to include a constructor, however, there’s a better way to do that: Write a Java interface. I’ll discuss those in the later, in the section “Using Interfaces.”


You can learn more about this pattern at http://en.wikipedia.org/wiki/Template_method_pattern.

Reason two is that there is a use case that suits the hybrid form of abstract classes very well. It just doesn’t come up very often, particularly in entry-level programming work. A design pattern called the Template Method pattern, when implemented in Java, exploits the abstract class’s part-concrete, part-abstract makeup. Before I put abstract classes to rest, I want to make sure you have at least one good motivation for remembering them and even applying them one day. If you’re going to learn only one way to use an abstract class, you could do much worse than committing the Template Method pattern to memory.

Learning design patterns on the whole is an advanced skill and well beyond the scope of this book, but I’m not trying to introduce the discipline as a whole here. Taken one at a time, design patterns are code techniques that prove themselves useful in a variety of situations. To take this subject area seriously, you’d want to learn the theory that drives their use and the practical trade-offs you make when applying one. Short of that, there’s no reason you can’t think of any one pattern as a simple recipe for getting work done that you’d otherwise have to discover by trial and error.

It turns out the LawnService example you’ve been following could resolve a particular issue with keeping its business operations consistent using the Template Method pattern. The next section explains how.

Applying the Template Method Pattern

The formal requirements of the Template Method pattern are something you should eventually learn, but they aren’t necessary in the moment, so I’ll just describe the challenge the pattern can address. Say you want to encapsulate the sequence of method calls associated with the LawnService concept. (See Figure 10-1 for a refresher.) It’s important to preserve the order of these operations. Keeping to a particular sequence makes training new hires easier and gives repeat customers a sense of consistency they like.

Certain aspects of each job setup are fixed. Every employee, for example, carries a hose to use for watering. If there’s no need for it on a particular job type, you can override the default water() code with an empty statement body and ignore the hose. Other aspects of each job vary with the type of service required —what equipment to bring for cutting and edging, for example, or whether a truck is needed to haul clippings away. These needs derive from the kind of lawn, the total area to cut, and so on.

The Template Method pattern describes a way to modify the individual steps of each procedure without disturbing the sequence. First, you define a final method that describes the procedure as a series of Java statements. You can then declare as abstract the methods that can’t be implemented until you have all the concrete details you need. Listing 10-2 shows a skeletal example that will compile.

Listing 10-2: Foundation of a Template Method pattern

public abstract class LawnService {
// fields have been defined
private String address;
// Subclass must invoke super(String)
protected LawnService(String addr) {
address = addr;
   }
public void clear() { // implemented
   }
public void edge()  { // implemented
   }
public void water() { // implemented
   }
public final void execute() {
      clear();
      mow();
      edge();
      gather();
      water();
   }
public abstract void mow();
public abstract void gather();
}

The execute() method invokes the operations in their intended order and is declared final so it can’t be overridden. The subclasses (or jobs, as I refer to them in the discussion of Figure 10-2) must implement the methods mow() and gather() to become concrete classes They can override the methods clear(), edge(), and water(), if appropriate, or rely on the parent’s implementation.

I also added a constructor that requires a String parameter. An Address class that could validate the location (and maybe even map it!) would be cool, but for a quick class sketch like this, a String placeholder is tolerable. This constructor obliges the subclass to inform the parent through a super() call. The address variable is included just so the code will compile; I’ll have to replace that once I get serious about developing this class properly.

Nothing in Listing 10-2 will keep a subclass from passing in the null value or an empty String. Since it’s all my code and it’s only half-formed so far, I’m on my own honor system to observe the spirit of the code. If other programmers are going to write subclasses, my code has to take enforcement up a notch and keep them honest, but also it has to be resilient to honest mistakes.

Enforcing a Pattern

There are some techniques to support the methods mow() and gather() so a subclass doesn’t just implement them with empty bodies. Since it doesn’t take much to get past the compiler, your best bet is to help the code fail as quickly or as visibly as you can. If the mow() method returns a value the gather() method relies on, you could validate the received value and throw an exception when it’s inappropriate or out of range. You’ll learn how to throw exceptions in the next chapter.

You could also change a state condition in the object, using perhaps a private field called internalError, so the execute() method can acknowledge the flag but also complete without crashing. At the same time, it’s not usually worth the time it takes to keep code running in the face of every possible abuse others can visit upon it. Somewhere between checking the input to every method and adding flags for every possibly malady, there’s a happy medium. The Template Method pattern isn’t the only use for an abstract class, but it’s a pretty good one. Some packages in the core libraries will use abstract methods to deal with low-level variations—such as filesystem, operating system, or even hardware elements—they can’t express as a general case. The pixel is an excellent example. A pixel is defined by each operating system’s windowing software. Some cross-platform graphics libraries manage it by declaring their pixel-getting methods abstract, then using code in a platform-specific library to implement their properties, similar to our PokerNight example.

You should think to use an abstract class when you want to create some bigger picture that needs to be completed. You should also think of it when you want to declare a fixed procedure in a way that lets another programmer change the implementation of the steps. You won’t find many other novel uses for abstract methods and classes, but these two conditions cover a lot of ground.

Using Interfaces

certobjective.eps

We’re now ready to discuss Java’s version of a completely abstract type, the interface. An interface has no mutable data, no implemented methods, and no constructors. The first two restrictions imply the third. Given a type that has no data you can change and no methods you can invoke, there’s not much point in trying to make an object from it.


Methods in an interface are implicitly public and abstract. Including the keywords is considered redundant and is discouraged.

All a Java interface can do is declare abstract methods and immutable data. It doesn’t seem like much fun, but it is a very powerful tool. Java interfaces add an additional aspect to the what-if case I presented at the beginning of the chapter.

It goes like this: What if I wanted to describe how to care for a lawn by listing the essential steps any lawn service should include? I don’t want to impose a procedure, as the Template Method pattern lets me do, or provide any implementations at all. I just want to describe the steps required, expressed as methods.

Here is a LawnServices interface that declares the operations I have used before. Now, however, there’s no code and no data, just a type that declares certain methods without implementing them or even suggesting how they should relate to each other:

public interface LawnServices {
void clear();
void edge();
void water();
void mow();
void gather();
}

There’s no way to enforce order of invocation, to encapsulate them, or to do almost anything else I’ve been doing with methods in the last few chapters. What I can do with an interface, however, is name the methods for any lawn service I care to implement and declare a common type for them. A Java interface is nothing more than a definition of methods that can be treated as a type.

Let’s say my lawn service is just crushing the competition. Other operators must now capitulate to my grass-mowing, clipping-gathering, edge-trimming juggernaut of a business. I’m prepared to buy them all out, but first I want to make sure I can adapt their business operations to mine. I don’t want to impose the details of my operations on theirs; acquiring a business and then disrupting it with unfamiliar, mandated practices is a time-honored way to lose existing customers. I just want to make sure I can incorporate the services they provide and manage them on my own terms. This case is where a pure type, one that allows only a definition of behavior, comes in handy.

A Java interface is a so-called pure type. A good one names a list of methods that communicate a cohesive service. When you want to incorporate the methods of an interface into your class, you implement it by providing a method body for each of its members. A class that implements an interface implicitly declares that it includes the type of the interface in its definition.

Since my LawnService concept has already taken shape and I am happy with it, I want to keep using it. I want to apply it to other new types without forcing inheritance. So here’s a really neat thing about Java interfaces: I can just redeclare the operations of my abstract class in an interface and then declare that the abstract class implements it, like this:

public abstract class LawnService implements LawnServices {…}

I’ve just added a new type to the LawnService definition without changing a thing. You can do that with all or part of an existing class interface. I will even claim here that good interfaces aren’t usually conceived out of thin air. Instead, they are factored out of well-designed classes by people who realize the broader applications of that design.

My LawnService class is now a type of the LawnServices interface. What have I gained? My class can now share a type with any other class that also implements the LawnServices interface. Let’s say one business I’m about to buy is Joe’s Cut ‘n’ Blow Express. I tell him I need to see his business code so I can test it for adaptation to my interface. Joe uses a single top-level class that looks like this:

public final class MowAndBlow {
public void cut() { }
public void blow() { }
public void go() { }
}

Assume the methods shown here are fully implemented. Unlike me, Joe likes to improvise a lot while on the job, so there’s no enforced sequence to his operations. The class is also marked final, so I can’t just extend the MowAndBlow class. I don’t want to anyway; I don’t want to find out down the road that the class is unsuitable for extending. Using the LawnServices interface, however, I can just wrap Joe’s methods with mine, as shown in Listing 10-3.

Listing 10-3: The MowAndBlow class implementing the LawnServices interface

public final class MowAndBlow implements LawnServices {
// Joe's methods
public void cut() { }
public void blow() { }
public void go() { }
// methods to implement
   @Override public void clear() {}

   @Override public void edge() {}

   @Override public void water() {}

   @Override public void mow() {
this.cut();
   }

   @Override public void gather() {
this.blow();
this.go();
   }
}

Wrapping the cut() method with my mow() method seems like the right thing to do. So does rolling the blow() and go() methods together into my gather() method. There could be other things to fit in the methods that are currently empty, but at least I can persuade the compiler that the MowAndBlow class is now a LawnServices type of thing.

That’s all it takes to fold the MowAndBlow class into my LawnServices type. I don’t have to modify the existing code or remove the final modifier. I also don’t require the MowAndBlow class to extend another class. I have expanded my type system without using inheritance, overloading constructors, or mucking with package access. I can’t do much about concealing Joe’s public methods, but for initial testing it’s not a primary concern.

Let this fact seep in for a bit. Just by implementing an interface, you can confer an additional type on an existing class. You get the power of type-sharing that works around Java’s single-inheritance rule. You also get the flexibility of composition. That’s a sweet deal. There are better and subtler ways to exploit this feature, but this is your first look. Refinements can wait while you absorb the primary lesson.


Many interface methods do no harm if you give them empty implementations.

In addition, a class can implement more than one interface by declaring them all in a comma-delimited list:

import java.io.Serializable;
import java.util.List;
class GimmeEverything implements Serializable, List { }

You must then implement all the methods they declare. You don’t have to integrate these methods with each other or with the existing methods of your class. However, as a class becomes an aggregation of interfaces, it also tends to lose focus. The infinite possibilities of implementing interfaces might be interesting to imagine, but in practice it can be tedious and distracting to try.


Many Java programmers, if not most, consider it bad form to include implied modifiers in an interface declaration.

You can include fields in an interface definition too, like this:

public interface Deck {
int COUNT = 52;
}

An interface field is the closest thing Java has to a constant, an immutable value that serves as a kind of dictionary term for a program. Interface fields are implicitly public, final, and static, so you have to declare their values up front. You can then use the field name as a mnemonic device to clarify program code:

class Test {
public static void main(String args[]) {
      System.out.println(“Total cards in a deck: “ + Deck.COUNT);
    }
}

In the old days of Java, it was common to see long lists of these immutable values in an interface. If you see it in recently written code, make a habit of wincing at it. There is a much better way to do this kind of work now, but you should know what the old style looks like:

interface Position {
int PITCHER = 1;
int CATCHER = 2;
int FIRST_BASE = 3;
// … more values
int RIGHT_FIELD = 9;
}

This technique approximates a traditional programming structure called an enumeration. Enumerations are useful when you have a list of items with a well-known order that you want to represent in code in a more readable way. Consider the numeric field positions for a baseball team as an example. Fans of the game know them by their number as well as their name. You can say “six to four to three” to baseball fans, and they will understand you to mean a double play that involved the third baseman (sixth position), the second baseman (fourth position) for one out, and the first baseman (third position) for another out. You could write a double play into a baseball computer game using those numbers, and your average baseball-loving Java programmer will understand it. Unfortunately, programmers who don’t know or care for baseball will have no idea what’s going on without some more help.

By using mnemonics in an enumeration, you can make the same thing clearer to more code readers. Instead of numbers, you can express something like Position.THIRD_BASE to Position.SECOND_BASE to Position.FIRST_BASE. This approach makes the meaning explicit in the code. The mnemonics are still supported by their underlying integer representation. It’s a bulkier expression, but it pays off in readability.

This same technique is attractive for lots of uses, but it gets hairy if the list itself isn’t stable. Say, for example, you wanted to change the numbering of field positions to make the shortstop sixth and the third baseman fifth. And you wanted to insert a position for the designated hitter even though he doesn’t play on defense.

Any code that relies on the original numbering wouldn’t necessarily break—that would actually be pretty nice. Because the underlying integers are legal types, you’d more likely have broken code that works oddly. The enumeration merely assigns the integers themselves a name. If you don’t care about baseball but maintain baseball game software, you might not figure out what’s wrong, at least not until a user complains that their designated hitter just tagged out a runner at shortstop.

As a result, programmers who use traditional enumerations have few attractive options. One option is to freeze an enumeration once it’s written and resort to writing a new one if the elements have to change. Another option is to add elements only at the end of an existing enumeration. If you do that often enough, the underlying numeric representation can appear more arbitrary with each change.

To get past these and other limits, Java first introduced a specific type, called the enum, in Java 5. It’s a very useful thing, and I want to make sure you see why. That discussion is coming up later in this chapter.

Planning for Variation

Before we talk about the enum type, let’s return to the LawnServices example and create a use case for it. My testing so far shows I can absorb Joe’s business application logic into my own by yoking both systems under a common interface. Doing it that way saves me a lot of fuss. In production code, it might take more careful testing and possibly some ugly workarounds to complete the integration, but a good interface design will help me isolate and mitigate any trouble spots. I can now visualize my class relationships as shown in Figure 10-4.

Figure 10-4: LawnServices interface incorporating dissimilar classes

c10f004.eps

In this figure, I’ve added the LawnServices interface and factored my common operations into it. There’s no need to repeat those methods in the implementing classes. The association shown by the dashed line, called a realization in UML, implies that. Instead, I can just add to each class the details that distinguish it from the other realizations.

If I decided the interface should include more operations, I could add them, but then I’d have to add an implementation into each existing subclass. If I decided a certain operation was redundant or interfered with the interface’s cohesiveness, I could remove it, but then I would probably break more than a few implementing classes.

With interfaces, your best approach is to keep them simple and commit to them for the long run. Correcting for oversights down the road only gets harder as more classes implement your interface. There’s no Java programming frustration quite so intense as having your code break because someone you don’t even know changed an interface you depend on, and code you haven’t modified in months now just won’t work.

Reviewing Polymorphism

Looking again at Figure 10-4, notice that the LawnService class is still marked abstract and the MowAndBlow class is still marked final. Those are incompatible differences, but it doesn’t matter. Neither class relies on the other. They just share a common type in the LawnServices interface.

You can take advantage of this shared relationship another way. The LawnServices interface hides the differences among my concrete classes—it’s a prime candidate for use as a reference type, as a field, return type, or parameter. That means you can create methods that can use any object whose class implements the LawnServices interface.


Are you curious what one of the exercises for this chapter might be?

Let’s say I want to track all the jobs I can complete in one day. I estimate an average of two jobs per day per employee. After I absorb Joe’s business, I’ve got 10 employees—that’s 20 jobs a day. To keep things simple, I can use a regular Java array and track each job by its index number.

interface Jobs {
int MAX = 20;
}

LawnServices[] jobs; //populated elsewhere
public LawnServices jobTracker(int index) {
if (index < Jobs.MAX)
return jobs[index];
}
…

The full power of polymorphism becomes apparent once you use it to communicate behaviors that have been implemented by the concrete object that has been conveyed in the communication. This form lends itself to code that doesn’t require the programmer to consider the variation of every possible concrete type to understand what’s supposed to happen. It also hides a lot of detail; such a terse appearance can be a little frustrating while you’re first learning how this all works. Give yourself time to acclimate to it. I’m sure once you have a chance to appreciate the resulting simplicity, you’ll be happy to contribute to it.

Using the enum Type

Java introduced the enum type in version 1.5. Up to that point, a programmer had to use an interface populated with integers and good variable names to emulate an enumerated type. The idea’s simple enough: Name some elements of a common group, like the positions of a baseball team. Give each position a unique number in that enumeration.

The variable names you choose serve as mnemonic devices in your program code. The numeric values give you a way to communicate by type. It’s a useful arrangement that’s used heavily in languages like C and C++ to make code easier to read.


The Wikipedia article on enumerated types describes its uses and limits: http://en.wikipedia.org/wiki/Enumerated_type.

Java programmers at one time emulated this structure with interfaces. Now we can do much better. The enum type not only resolves certain limits inherent in representing names with an underlying number; it also provides an entire type scheme. Each enumerated value is itself an object of the type that declares it, not just a name.

This form makes it very easy to implement simple constructs of a well-known collection in which the items are unique and may have a natural order to them. Consider this rewrite of the Position interface I described earlier as an enum type:

public enum Position {
PITCHER,
CATCHER,
FIRST_BASE,
SECOND_BASE,
SHORTSTOP,
THIRD_BASE,
LEFT_FIELD,
CENTER_FIELD,
RIGHT FIELD
} 

That’s everything. Each of the listed elements is a Position type, not a name with some other underlying value.

An enum element has a variety of additional useful properties. Because it is an instance of the enum type, you can write methods that declare the type as a return type or a parameter; the range of acceptable values is inherently limited to the enumerated elements. Unlike an interface, which gives you a way to address any class that implements it, an enum type lets you limit the acceptable values of one type. You can always add an element to a Java enumeration without breaking any class that already uses it. Removing an element will not break all users of the type. It will, of course, affect any program that relied on that deleted element.

You can print the name of an element just by passing it to the System.out.println() method, as shown in Listing 10-4.

Listing 10-4: Using an enum element as a parameter

class Report {
public void printPos(Position pos) {
      System.out.println(pos);
   }
public static void main(String args[]) {
      Report r = new Report();
r.printPos(Position.CATCHER);
   }
}
$ javac Report.java 
$ java Report 
CATCHER

Notice the printPos() method accepts a Position type as a parameter. The element Position.CATCHER is one element of that type. You can treat that element as a constant, but you can also treat it like an Object subclass when using it for a parameter. When the toString() method is called on an enum element, it will return a String object that matches its name.

Using this feature, let’s say I wanted to encode the different lawn jobs I’ve named. I now want to make more readable code by using these internal names while still keeping them separate from how I communicate with a client. This work is trivial for an enum type:

enum Job {
HACKITBACK,
SHOWITOFF,
TOUCHITUP,
KNOCKITDOWN,
MOWANDBLOW}

Now that I have named each job type in code, there’s a great deal more I can do. Figure 10-3 illustrated the case for selecting a job type the client needs, based on site conditions, but returning a LawnService reference to the caller.

I’ll use the LawnServices interface as a drop-in replacement for the LawnService class. That way, I can also include concrete types that use the same operations but don’t extend the LawnService class. That means I can include the MowAndBlow type with the LawnService subclasses I have.

Now I can write a method that returns a concrete reference, selected by processing an incoming parameter, but in the form of the interface type:

public LawnServices getLawnServices (Job job) { } 

The compiler will actually complain if you use fully qualified case labels with an enum.

Notice how the power of an enum type kicks in when I implement this method. I can use it as an argument to a switch statement. Because the elements are constants, I can use them as case labels just as they are. I don’t even need to give their fully qualified name because it is made plain by the argument to the switch test. My getLawnServices() method looks like this:

   public LawnServices getLawnServices(Job job) {
switch(job) {
case HACKITBACK:
return new HackItBack();
case SHOWITOFF:
return new ShowItOff();
case TOUCHITUP:
return new TouchItUp();
case KNOCKITDOWN:
return new KnockItDown();
case MOWANDBLOW:
return new MowAndBlow();
case default:
return null;
      } 
   }

This arrangement resembles a scheme called a Factory Pattern, in which a method returns a subclass of the expected type, based on the input.

Each case label uses a return statement, so there is no cascading effect and no need for break statements. All the elements listed in the Job enumeration have a case label, so there is also no need for a default label. It’s considered best practice to have one, so I included it. And now we have a method that creates the right object for each job but always returns the same interface.

To test it, you’ll need a few more pieces. Rather than implement all the methods I’ve described so far, however, it will be less work if you declare just one of the methods given in the LawnServices interface. When you get that one working properly, add the others. For example, you could start with just this much:

interface LawnService {
void testing();
}

Then you can write a dummy class for each job to show that the objects will get created:

class HackItBack implements LawnServices { 
   @Override public void testing() {
      System.out.println("HackItBack: testing()");
   }
}
class ShowItOff implements LawnServices {
   @Override public void testing() { 
      System.out.println("ShowItOff: testing()");
   }
}
class TouchItUp implements LawnServices {
   @Override public void testing() { 
      System.out.println("TouchItUp: testing()");
   }
}
class KnockItDown implements LawnServices {
   @Override public void testing() { 
      System.out.println("KnockItDown: testing()");
   }
}
class MowAndBlow implements LawnServices {
   @Override public void testing() {
      System.out.println(“MowAndBlow: testing()”);
   }
}

Each class can implement the testing() method with a self-identifying message—that’s enough to show the right method has been invoked. All that’s needed now is a class to contain the getLawnServices() method and then a main() method to test it. But wait, there’s more!

You can also use the enhanced for loop with any enum type. The compiler inserts a values() method into each one so it can return an array of its elements. Iterating through one is short work:

public class Switcheroo {
// Insert getLawnServices() method here
public static void main(String args[]) {
      Switcheroo scope = new Switcheroo();
      for (Job job : Job.values())
         scope.getLawnServices(job).testing();
   }
}
$ javac JobTest.java
$ java JobTest
HackItBack: testing()
ShowItOff: testing()
TouchItUp: testing()
KnockItDown: testing()
MowAndBlow: testing()

The last thing I have done, both to reduce the code required and to show you a tiny trick about method invocation, is invoke the testing() method directly after calling the getLawnServices() method. The latter method evaluates to an object of type LawnServices. It’s not required that you declare a reference and assign the result to it. You can just chain a method that is accessible from that type, using dot notation as you can see.

You now have enough of a framework that you can add concrete classes whenever you want. The existing design ensures that every type-conforming subclass will “just work” once it is added to the framework. If you later want to incorporate a top-level class of another business into it, the process is straightforward:

1. Implement the LawnServices interface in the new class.
2. Add an element for the modified class in the Job enum.
3. Add a case for the modified class in the getLawnServices() method.
4. Test thoroughly.

This arrangement shows the benefits of object-oriented design. There’s a bit more theory that informs this kind of development, but this result is what you want: a way to expand the number of concrete classes without having to change the way the framework supports them. To add new job types, you only have to modify the Job enumeration and the getLawnServices() method. And, therefore, those are the only framework components that require testing.

It takes a while to learn to think this way, but as I said at the beginning of the chapter, the rewards are substantial.

Distinguishing between Abstract and Concrete References

certobjective.eps

Imagine, for example, that I want to know how many square feet of lawn my business covers in a day, perhaps for advertising or other bragging rights. Rather than add this requirement to an existing interface, I could write a new one called Coverage:

interface Coverage {
int getCoverage() ;
}

Why bother? Because it’s easier, especially in programs with many lawns to manage, to use a Coverage reference and imply exactly what I’m after. It’s the difference between using a reference type that has only one method I can call and a reference type that lets me call any number of methods:

Coverage cov = new TouchItUp();
Object obj = cov;
LawnService svc = cov;
LawnServices isvc = cov;

Each reference type implies my intentions with the referent. The Coverage interface leaves no room to guess. I can call getCoverage(), get an integer, and get on with it. Using abstract reference types this way isn’t just a question of demonstrating that you can do it. When you can narrow the context of a piece of code by employing a precise type, you’re making it easier for other programmers to understand your intent.

Using a general type, like the interface LawnServices, helps when you want to create collections. It also helps when defining return and parameter types for a method because it opens the door to future implementations. When you use concrete types, you limit the possibilities to subclasses. Consider the following example method:

public LawnServices getScope(Coverage cov) { … }

You know that the return value and parameter have to refer to concrete objects. You also know it doesn’t matter to the caller what the names of those objects are. Callers need to know the interface of the parameter and return types, and that’s all. The method tells them everything they need to know—that is, which method calls are available when receiving a Coverage object and when returning a LawnServices object.

There’s no hard science to designing methods with abstract types in mind, and it goes a bit beyond the required scope for this book. I’m not elaborating with examples for that reason, just planting seeds for the future. But now that you’ve come this far, it’s worth telling you what the next step for thinking in Java looks like.


The Essentials and Beyond
In this chapter, you learned and applied three key types: the abstract class, the interface, and the enum. Each form provides capabilities that can’t be satisfied with concrete classes alone. Each one promotes the use of abstraction to separate the meaning of class interfaces, which move to the top of the type system, from the implementation details, which move to the bottom.
This separation associates behavior with the method interfaces that you design and operations with the concrete classes that you implement. By working in this manner, you can ultimately minimize how much code any future changes or additions may affect.
Additional Exercises
1. Write an enum type for the days of the week. Add two methods to it (yes, it’s allowed): a static method to print the elements and a main() method that calls the static method so you can test it.
2. Write a class called NullJob that implements a LawnService interface with just the testing() method. Include a constructor that prints a message indicating a NullJob object has been made.
3. Write an empty class called Ball. Write an interface called Catcher with a receive() method that takes a Ball parameter. Write a Pitcher class with a method called partner() that takes a Catcher parameter and assigns it to a member variable. Write a Player class that implements the Catcher interface. Finally, write a Test class with a main() method that creates Pitcher and Player objects. Pass the Player reference to the Pitcher object’s partner() method.
When that is all done and compiled, add a pitch() method to the Pitcher class so it will throw a Ball object to the Player.
Review Questions
1. Which option identifies a difference between an execute() method declared in an interface and an abstract method named execute()?
A. An abstract method can have default access.
B. An interface method has public access.
C. An abstract method can be overloaded.
2. True or false: An interface method cannot be declared with protected access.
3. Which of the following interfaces are declared in the java.lang package? (Choose all that apply.)
A. Serializable
B. Runnable
C. Readable
D. List
4. True or false: All interfaces are instances of the Object class.
5. Which of the following declarations is legal?
A. private abstract testMe();
B. public abstract testMe(Object obj) ;
C. private final static void testMe() {}
D. protected abstract null testMe() ;
6. Which of the following reference types would be legal parameters for a method declared public String getAddress(Location loc) {} ?
A. Any subclass of Location, if Location is a class
B. Any subclass of the String class
C. Any implementation of Location, if Location is an enum
D. Any implementation of Location, if Location is an interface
7. True or false: You can make an interface’s methods visible only within their package.
8. Assume you have an enumeration called Cards in which the first declared element is ACE_SPADES. What is the result of System.out.println(Cards.ACE_SPADES)?
A. 0
B. 1
C. null
D. ACE_SPADES
9. True or false: You can declare an interface method abstract.
10. If you declare an interface Tag with no methods, what is required to implement the interface?
A. You only have to declare implements Tag.
B. Same as A, but the class must also be concrete.
C. You cannot implement an interface with no methods.
D. Same as A, but the class must also be abstract.

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

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