Objects and Classes
6.2 Defining your own classes
At least to a certain extent, the prospect of defining your own classes in Java should not be a particularly daunting prospect. You have, after all, been defining your own classes ever since Chapter 2. Every program is a class definition that extends one of the Program subclasses, which means that you have seen enough examples to get a sense of the structure of a class definition.
Syntax for a public class definition:
public class name extends super { class body
} where:
name is the name of the new class super is the name of the superclass
class body is a sequence of definitions that
will typically include constructors, methods, constants, and instance variables
The basic structure of a class definition is shown in the syntax box on the right. In Java, every class that is available for use by clients—as opposed to being defined only within the context of a package—must be marked with the keyword public. Each public class definition, moreover, should be in a separate source file called
name.java where name is the name of the new
class. The extends specification indicates the superclass from which this class extends. If the extends specification is missing, the new class extends the Object class in java.lang, which is the root of Java object hierarchy. The body of the class is a sequence of definitions of various kinds. In the classes you have seen so far, these definitions have been limited to methods and
named constants. More generally, classes will also contain constructors, which specify how to create new instances of the class, and instance variables, which maintain the state of the objects. Constructors are defined the same way as methods, with two exceptions: (1) the name of the constructor is always the same as that of the class, and (2) a constructor does not specify a result type. Instance variable declarations look much the same as any other variable declarations except that they appear outside of any method definition at the top level of a class.
Defining a Stoplight class
The role of constructors and instance variables can be illustrated most easily by example. Suppose, for example, that you want to represent a simple stoplight as a Java class. Stoplights have internal state, because the light can be in any of three states: green, yellow, and red. Stoplights also have behavior. A stoplight that is green will at some point turn yellow, then red, and eventually back to green, as illustrated by the following diagram:
Stoplights, however, never change in the opposite order, so that it would be impossible, for example, to have a stoplight turn from yellow to green.
A simple definition for a Stoplight class appears in Figure 6-6. The elements that make up the class are:
• The named constants GREEN, YELLOW, and RED. In order to determine what color is showing on the stoplight, the class must specify the possible states for the stoplight. The simplest strategy for doing so is to define named constants for the green, yellow, and red states. In the code in Figure 6-6, the values of these constants are set to the corresponding constant values in the Color class introduced briefly in Chapter 5. For now, the only advantage to using these values—as opposed, for example, to the
FIGURE 6-6 Implementation of a Stoplight class
import java.awt.*;
/**
* This class represents a simple implementation of a stoplight. * The client can determine the current state of the stoplight by * calling stoplight.getState() and change it to the next color * in the sequence (GREEN -> YELLOW -> RED -> GREEN) by calling * stoplight.advance().
*/
public class Stoplight {
/** Constant indicating the color GREEN */
public static final Color GREEN = Color.GREEN;
/** Constant indicating the color YELLOW */
public static final Color YELLOW = Color.YELLOW;
/** Constant indicating the color RED */
public static final Color RED = Color.RED;
/**
* Creates a new Stoplight object, which is initially GREEN. */
public Stoplight() { state = GREEN; }
/**
* Returns the current state of the stoplight.
* @return The state of the stoplight (GREEN, YELLOW, or RED) */
public Color getState() { return state;
}
/**
* Advances the stoplight to the next state. */
public void advance() { if (state == RED) {
state = GREEN;
} else if (state == YELLOW) { state = RED;
} else if (state == GREEN) { state = YELLOW;
} }
/* Private instance variable */
private Color state; }
characters 'G', 'Y', and 'R'—lies in the idea that using existing values reduces the number of new definitions and therefore preserves a certain economy. If, however, you wanted to use the Stoplight class in a graphical application, using the standard Java colors would offer the additional advantage of making it easier to draw the stoplight elements in the correct color.
• A constructor to create a new Stoplight instance. The first entry in the file after the named constants is the Stoplight constructor, which creates a new instance of the class. Constructors need not be specified unless a class needs to perform some initialization on new objects. In this case, the constructor sets the state of the stoplight to GREEN as specified in the constructor description.
• A method to determine the state of the stoplight. The getState method allows the client to determine the current state of the stoplight, which is always one of the constants GREEN, YELLOW, or RED. Internally, the class stores the current state in the instance variable state, but that variable is not directly accessible to the client for reasons that are outlined later in this section.
• A method to advance the stoplight to the next state. The advance method changes the state of the stoplight from GREEN to YELLOW, YELLOW to RED, or RED to GREEN, as appropriate.
• A private instance variable to store the current state. The instance variable state keeps track of the current color of the stoplight. Because the variable is declared to be private, only the methods in this class have direct access to it. Clients can determine the state of a stoplight indirectly by calling getState but cannot change the state other than by calling advance. Declaring this variable to be private therefore ensures that the stoplight object can never change directly from GREEN to RED without going through YELLOW in between.
If you look closely at the code in Figure 6-6, you may notice one other difference from the examples in the earlier chapters. Instead of beginning with /* as every comment has from the beginning of the book, the comments in the Stoplight class definition begin with /**. Because Java ignores everything between /* and */, the extra asterisk is simply part of the text of the comment and has no significance for the compiler. That extra asterisk, however, indicates that this comment contains information for use by the
javadoc documentation tool described earlier in this chapter. For now, these comments
include only a brief description of the declaration that follows. As you will discover later in this chapter, it is also possible to specify such additional information as details about the parameters accepted by a particular method or the type of result such a method returns.
The message-passing model
As discussed in Chapter 5, object-oriented programming uses the metaphor of sending messages from one object to another. In practice, message passing is implemented using method calls, but it is often useful to think in terms of messages when designing an object-oriented application. In the stoplight example, the basic model is that the client will create one or more instances of the Stoplight class and then manipulate those instances by sending messages.
The simplest example consists of creating a single stoplight, which you can do by invoking new Stoplight, as follows:
This declaration creates a new Stoplight instance and assigns it to myStoplight, which you could diagram conceptually as follows:
t h g i l p o t S y m
As you will discover in Chapter 7, the myStoplight variable is not large enough to hold all the information associated with the Stoplight object. That variable instead keeps track of an internal memory address that allows it to find the data associated with the actual Stoplight instance. At the more abstract level reflected in this diagram, that internal memory address is called a reference.
To advance the stoplight to the next phase in the cycle, all you need to do is send the “advance” message to myStoplight like this:
myStoplight.advance();
This operation corresponds to the following diagram, which shows the state of the stoplight before the message takes effect:
Advance to the next state
t h g i l p o t S y m
To verify that the light indeed changed, you could then call Color currentState = myStoplight.getState(); which you could diagram as follows:
Give me the current state
W O L L E Y t h g i l p o t S y m
Maintaining conceptual separation between the client and the implementation In the discussion of the various components of the Stoplight class, the reason offered for declaring state as private was that doing so ensured that the stoplight would always advance in the correct order, from GREEN to YELLOW to RED and back to GREEN again. Although that argument is powerful in its own right, there are many other reasons to make instance variables private to a class. One of the strongest justifications for using
private instance variables is that doing so gives the implementor greater freedom to change the implementation of a class. In the implementation shown in Figure 6-6, the state of each Stoplight object is stored in the variable state, which keeps track of the actual color. The implementation of the Stoplight class becomes slightly shorter if you change the underlying representation so that the state is maintained as an integer instead, as shown in Figure 6-7. The advance method then becomes simpler because it can use addition to move to the next state. The getState method, however, becomes more complex because it has to translate the integer used internally into the Color object that getState is defined to return.
The important point illustrated by the alternative implementation in Figure 6-7 is that the client has no way to tell the difference between the two implementations. The behavior of the two classes is exactly the same. If the implementor decided to replace one implementation with the other, clients of the Stoplight class shouldn’t even notice. The same would not be true if the client had access to the details of the internal representation. Changing the underlying representation would almost certainly upset some group of clients who had decided to rely on specific characteristics of the original one.
In fact, the advantages of making instance variables private are so compelling that all instance variables in this book—and indeed all instance variables in the ACM libraries— are marked using the keyword private. Although public instance variables have occasional uses (as do the protected and package private designations that are beyond the scope of this book), you will develop better programming habits if you adhere to the convention of using only private instance variables.