The cause is hidden, but the result is well known.
—OVID,METAMORPHOSE EXAMPLES
public double getTotal(double price, double tax) public void setValue(int count, char rating) public void readInput()
public int predictPopulation(int years)
The syntax for a complete method definition is given on page 277.
294 CHAPTER 5 / Defining Classes and Methods
Never tell people how to do things. Tell them what to do and they will sur- prise you with their ingenuity.
—GEORGE S. PATTON
*OGPSNBUJPO IJEJOH TPVOET BT UIPVHI JU DPVME CF B CBE UIJOH UP EP 8IBU advantage could hiding information have? As it turns out, in computer science hiding certain kinds of information is considered a good programming technique, one that makes the programmer’s job simpler and the programmer’s code easier to understand. It is basically a way to avoid “information overload.”
Information Hiding
A programmer who is using a method that you have defined does not need to know the details of the code in the body of the method definition to be able to use the method. If a method—or other piece of software—is well written, a programmer who uses the method need only know what the method accomplishes and not how the method accomplishes its task. For example, you can use the Scanner method nextInt without even looking at the definition of that method. It is not that the code contains some secret that is forbidden to you. The point is that viewing the code will not help you use the method, but it will give you more things to keep track of, which could distract you from your programming tasks.
%FTJHOJOHBNFUIPETPUIBUJUDBOCFVTFEXJUIPVUBOZOFFEUPVOEFSTUBOE the fine detail of the code is called information hiding. The term emphasizes the fact that the programmer acts as though the body of the method were hidden from view. If the term information hiding sounds too negative to you, you can use the term abstraction. The two terms mean the same thing in this context. This use of the term abstractionTIPVMEOPUCFTVSQSJTJOH8IFOZPVBCTUSBDUTPNFUIJOH you lose some of the details. For example, an abstract of a paper or a book is a brief description of the paper or book, as opposed to the entire document.
■ PROGRAMMING TIP When You Design a Method, Separate What from How
.FUIPETTIPVMECFTFMGDPOUBJOFEVOJUTUIBUBSFEFTJHOFETFQBSBUFMZGSPNUIF incidental details of other methods and separately from any program that uses the method. A programmer who uses a method should need only know what
the method does, not how it does it. ■
Precondition and Postcondition Comments
An efficient and standard way to describe what a method does is by means of specific kinds of comments known as preconditions and postconditions.
A method’s precondition comment states the conditions that must be true before the method is invoked. The method should not be used, and cannot be expected to perform correctly, unless the precondition is satisfied.
Separate the what from the how
A precondition states a method’s requirements
The postcondition comment describes all the effects produced by a method invocation. The postcondition tells what will be true after the method is executed in a situation in which the precondition holds. For a method that returns a value, the postcondition will include a description of the value returned by the method.
For example, the following shows some suitable precondition and postcondition comments for the method writeOutput shown in Listing 5.3:
/**
Precondition: The instance variables of the calling object have values.
Postcondition: The data stored in (the instance variables of) the receiving object have been written to the screen.
*/
public void writeOutput()
The comments for the method predictPopulation in Listing 5.6 can be expressed as follows:
/**
Precondition: years is a nonnegative number.
Postcondition: Returns the projected population of the receiving object
after the specified number of years.
*/
public int predictPopulation(int years)
If the only postcondition is a description of the value returned, programmers usually omit the word postcondition. The previous comment would typically be written in the following alternative way:
/**
Precondition: years is a nonnegative number.
Returns the projected population of the receiving object after the specified number of years.
*/
public int predictPopulation(int years)
4PNFEFTJHOTQFDJGJDBUJPOTNBZSFRVJSFQSFDPOEJUJPOTBOEQPTUDPOEJUJPOTGPS all methods. Others omit explicit preconditions and postconditions from certain NFUIPET XIPTF OBNFT NBLF UIFJS BDUJPO PCWJPVT /BNFT TVDI BTreadInput, writeOutput, and set are often considered self-explanatory. However, the sound rule to follow is to adhere to whatever guidelines your instructor or supervisor gives you, and when in doubt, add preconditions and postconditions.
4PNF QSPHSBNNFST QSFGFS OPU UP VTF UIF XPSET precondition and postcondition in their comments. However, you should always think in terms of preconditions and postconditions when writing method comments. The really important thing is not the words precondition and postcondition, but UIFDPODFQUTUIFZOBNF/PUFUIBUQSFDPOEJUJPOBOEQPTUDPOEJUJPODPNNFOUT are examples of assertions.
A postcondition states a method’s effect
296 CHAPTER 5 / Defining Classes and Methods
The public and private Modifiers
As you know, the modifier public, when applied to a class, method, or instance variable, means that any other class can directly use or access the class, method, or instance variable by name. For example, the program in Listing 5.7 contains the following three lines, which set the values of the public instance variables for the object speciesOfTheMonth:
speciesOfTheMonth.name = "Klingon ox";
speciesOfTheMonth.population = 10;
speciesOfTheMonth.growthRate = 15;
Any class can use a public class, method, or instance variable
LISTING 5.7 Using a Method That Has a Parameter /**
Demonstrates the use of a parameter with the method predictPopulation.
*/
public class SpeciesSecondTryDemo {
public static void main(String[] args) {
SpeciesSecondTry speciesOfTheMonth = new SpeciesSecondTry();
System.out.println("Enter data on the Species of the " + "Month:");
speciesOfTheMonth.readInput();
speciesOfTheMonth.writeOutput();
int futurePopulation =
speciesOfTheMonth.predictPopulation(10);
System.out.println("In ten years the population will be " + futurePopulation);
//Change the species to show how to change //the values of instance variables:
speciesOfTheMonth.name = "Klingon ox";
speciesOfTheMonth.population = 10;
speciesOfTheMonth.growthRate = 15;
System.out.println("The new Species of the Month:");
speciesOfTheMonth.writeOutput();
System.out.println("In ten years the population will be " + speciesOfTheMonth.predictPopulation(10));
} }
Sample Screen Output The output is exactly the same as in Listing 5.4.
The object speciesOfTheMonth is an object of the class SpeciesSecondTry, whose definition appears in Listing 5.6. As you can see by looking at that class definition, the instance variables name, population, and growthRate all have the modifier public, and so the preceding three statements are perfectly valid.
8IJMF JU JT OPSNBM UP IBWF QVCMJD DMBTTFT BOE NFUIPET JU JTnot a good programming practice to make the instance variables of a class public. Typically, all instance variables should be private. You make an instance variable private by using the modifier private instead of the modifier public. The keywords public and private are examples of access modifiers.
4VQQPTFUIBUXFDIBOHFUIFNPEJGJFSUIBUJTCFGPSFUIFJOTUBODFWBSJBCMF name in the definition of the class SpeciesSecondTry in Listing 5.6 from public to private so that the class definition begins as follows:
public class SpeciesSecondTry {
private String name;//Private! public int population;
public double growthRate;
8JUIUIJTDIBOHFUIFGPMMPXJOH+BWBTUBUFNFOUJO-JTUJOHJTJOWBMJE speciesOfTheMonth.name = "Klingon ox"; //Invalid when private.
The following two statements remain valid, because we left the modifiers of population and growthRate as public:
speciesOfTheMonth.population = 10;
speciesOfTheMonth.growthRate = 15;
8IFOBOJOTUBODFWBSJBCMFJTQSJWBUFJUTnameis not accessible outside of the class definition. Even so, you can still use its name in any way you wish within any method inside the class definition. In particular, you can directly change the value of the instance variable. Outside of the class definition, however, you cannot make any direct reference to the instance variable’s name.
For example, let’s make the three instance variables in the class SpeciesSecondTry private, but leave the method definitions unchanged. The result is shown in Listing 5.8 as the class SpeciesThirdTry. Because the instance variables are all private, the last three of the following statements would be invalid within any class other than the class SpeciesThirdTry:
SpeciesThirdTry secretSpecies = new SpeciesThirdTry();
//Valid
secretSpecies.readInput(); //Valid
secretSpecies.name = "Aardvark"; //Invalid
System.out.println(secretSpecies.population); //Invalid System.out.println(secretSpecies.growthRate); //Invalid
Instance variables should be private
Private instance variables are accessible by name only within their own class
298 CHAPTER 5 / Defining Classes and Methods
/PUJDF IPXFWFS UIBU UIF JOWPDBUJPO PG UIF NFUIPEreadInput JT WBMJE 4P there is still a way to set the instance variables of an object, even though those JOTUBODFWBSJBCMFTBSFQSJWBUF8JUIJOUIFEFGJOJUJPOPGUIFNFUIPEreadInput (shown in Listing 5.3) are assignment statements such as
name = keyboard.nextLine();
and
population = keyboard.nextInt();
that set the value of instance variables. Thus, making an instance variable private does not mean that you cannot change it. It means only that you cannot use the instance variable’s name to refer directly to the variable anywhere outside of the class definition.
.FUIPETDBOBMTPCFQSJWBUF*GBNFUIPEJTNBSLFEprivate, it cannot be invoked outside of the class definition. However, a private method can still be JOWPLFEXJUIJOUIFEFGJOJUJPOPGBOZPUIFSNFUIPEJOUIBUTBNFDMBTT.PTU methods are public, but if you have a method that will be used only within the definition of other methods of that class, it makes sense to make this iIFMQJOHw NFUIPE QSJWBUF 6TJOH QSJWBUF NFUIPET JT BOPUIFS XBZ PG IJEJOH implementation details within the class.
Classes themselves can be private as well, but we will not discuss that until Chapter 12.
LISTING 5.8 A Class with Private Instance Variables import java.util.Scanner;
public class SpeciesThirdTry {
private String name;
private int population;
private double growthRate;
<The definitions of the methods readInput, writeOutput, and predictPopulation are the same as in Listing 5.3 and
Listing 5.6.>
}
We will give an even better version of this class later in the chapter.
Private methods are called only within their own class.
RECAP Thepublic and private Modifiers
Within a class definition, each instance variable declaration and each method definition, as well as the class itself, can be preceded by either public or private. These access modifiers specify where a class, instance variable, or method can be used. If an instance variable is private, its name cannot be used to access it outside of the class definition. However, it can be used within the definitions of methods in its class. If an instance variable is public, there are no restrictions on where you can use its name.
(continued)
■ PROGRAMMING TIP Instance Variables Should Be Private You should make all the instance variables in a class private. By doing so, you force the programmer who uses the class—whether that person is you or someone else—to access the instance variables only via the class’s methods.
This allows the class to control how a programmer looks at or changes the instance variables. The next programming example illustrates why making
instance variables private is important. ■
PROGRAMMING EXAMPLE
If a method definition is private, the method cannot be invoked outside of the class definition. However, it can be invoked within the definitions of methods in its class. If the method is public, you can invoke it anywhere without restriction.
Normally, all instance variables are private and most methods are public.
A Demonstration of Why Instance Variables Should Be Private
Listing 5.9 shows a simple class of rectangles. It has three private instance variables to represent a rectangle’s width, height, and area. The method setDimensions sets the width and height, and the method getArea returns the rectangle’s area.
LISTING 5.9 A Class of Rectangles /**
Class that represents a rectangle.
*/
public class Rectangle {
private int width;
private int height;
private int area;
public void setDimensions(int newWidth, int newHeight) {
width = newWidth;
height = newHeight;
area = width * height;
}
public int getArea() {
return area;
} }
300 CHAPTER 5 / Defining Classes and Methods
You might use the class Rectangle in the following way:
Rectangle box = new Rectangle( );
box.setDimensions(10, 5);
System.out.println("The area of our rectangle is " + box.getArea());
The output from these statements would be The area of our rectangle is 50
8IBU XPVME IBQQFO JG UIF UISFF JOTUBODF WBSJBCMFT JORectangle were public instead of private? After creating a 10-by-5 rectangle box, you would be BCMFUPDIBOHFUIFWBMVFPGBOZPSBMMPGUIFJOTUBODFWBSJBCMFT4PXIJMFarea is 50, you could change width, for example, to 6 by writing
box.width = 6; //You could do this if width were public.
If you then called getArea, you would still get 50 as the area, instead of the new area, 30. By making the instance variables in Rectangle public, we create the possibility that the instance variable area will be unequal to width * height.BLJOHUIFJOTUBODFWBSJBCMFTQSJWBUFFOBCMFTZPVUPSFTUSJDUIPXUIFZ are accessed or changed.
GOTCHA Public Instance Variables Can Lead to the Corruption of a Class’s Data
The previous programming example shows how the ability to change an instance variable directly by name can cause inconsistent data within an object.
4JODF QVCMJD JOTUBODF WBSJBCMFT QSPWJEF UIJT BCJMJUZ BMXBZT NBLF JOTUBODF
variables private. ■
Private instance variables enable the class to restrict how they are accessed or changed
PROGRAMMING EXAMPLE Another Implementation of a Class of Rectangles
Let’s look at another class, Rectangle2 in Listing 5.10, that has exactly the same methods as the previous class Rectangle, but implements them in a slightly different way. Our new class computes the area of the rectangle only when the method getArea is called. Additionally, the area is not saved in an instance variable.
/PUJDF UIBU ZPV XPVME VTF UIF DMBTT Rectangle2 in the same way that you would use Rectangle. The only change you would make to the three statements that we wrote in the previous programming example to demonstrateRectangle would be to replace Rectangle with Rectangle2:
Investigating public and private access VideoNote
Public instance variables can lead to the corruption of an object’s data
Rectangle2 box = new Rectangle2( );
box.setDimensons(10, 5);
System.out.println("The area of our rectangle is " + box.getArea());
That is, you invoke the methods in the same way, and they have the same behavior, regardless of their class.
LISTING 5.10 Another Class of Rectangles /**
Another class that represents a rectangle.
*/
public class Rectangle2 {
private int width;
private int height;
public void setDimensions(int newWidth, int newHeight) {
width = newWidth;
height = newHeight;
}
public int getArea() {
return width * height;
} }
REMEMBER Implementation Should Not Affect Behavior Two classes can have the same behavior but different implementations.
8F IBWF UXP DMBTTFTRectangle and Rectangle2—that behave in the same way. That is, what they do is the same, but how they perform their tasks differs. In Rectangle2, the method getArea, computes and then returns the area without saving it. Rectangle, however, has an instance variable—area— for the rectangle’s area. The method setDimensions in Rectangle computes the area and stores it in area. The method getArea then simply returns the value in the instance variable area.
Is one of these two classes “better” than the other? The answer depends PO XIBU XF NFBO CZ iCFUUFSw BOE IPX XF VTF UIF DMBTT 8F DBO NBLF UIF following observations:
tRectangle uses more memory than Rectangle2 because it has an additional instance variable.
tRectangle always computes the area, even if it is not needed.
Classes can define the same behaviors but have different implementations
302 CHAPTER 5 / Defining Classes and Methods
The implication of our second observation is that using Rectangle could require more computer time than using Rectangle2. To appreciate this, you need to imagine that both classes have several more methods, so that not invoking getArea is a real possibility. If you rarely ask for the area of a particular rectangle, using Rectangle2 saves you both execution time and memory. However, if you repeatedly invoke getArea for a particular rectangle, using Rectangle will save you execution time because it computes the area only once.
REMEMBER Implementation Can Affect Efficiency
The way you implement a class can affect its execution time and memory requirements.
Accessor Methods and Mutator Methods
.BLJOHBMMJOTUBODFWBSJBCMFTQSJWBUFEPFTDPOUSPMBDDFTTUPUIFNCVUXIBUJG you have a legitimate reason to access an instance variable? For these cases, you should provide accessor methods. An accessor method or get method, orgetter, is simply a method that allows you to look at data contained in an instance variable. In Listing 5.11, we have rewritten the class for a species yet another time. This version has accessor methods for obtaining the value of each instance variable. The names of these methods start with the word get, as in getName.
Accessor methods allow you to look at the data in a private instance variable. Other methods, known as mutator methods, or set methods, or setters, allow you to change the data stored in private instance variables. Our class definition has a mutator method, called setSpecies, for setting the instance variables to new values. The program in Listing 5.12 illustrates the use of the mutator method setSpecies. That program is similar to the one in Listing 5.7, but because this version of our species class has private instance variables, we must use the mutator method setSpecies to reset the values of the instance variables.
%FGJOJOHBDDFTTPSNFUIPETBOENVUBUPSNFUIPETNJHIUBQQFBSUPEFGFBU the purpose of making instance variables private, but that is not true. A mutator method can check that any change is appropriate and warn the user of a problem. For example, the mutator method setSpecies checks to see whether the program inadvertently tries to set population equal to a negative number.
An accessor method retrieves data in an object
A mutator method changes data in an object
The names of these methods need not involve get and set; you can use any method name that is convenient. For example, you might prefer to give them names such as retrieveValue,reset, or giveNewValues. However, it is traditional to begin the names of accessor methods with get and mutator methods with set.
LISTING 5.11 A Class with Accessor and Mutator Methods import java.util.Scanner;
public class SpeciesFourthTry {
private String name;
private int population;
private double growthRate;
<The definitions of the methods readInput, writeOutput, and predictPopulation go here. They are the same as in Listing 5.3 and Listing 5.6.>
public void setSpecies(String newName, int newPopulation, double newGrowthRate)
{
name = newName;
if (newPopulation >= 0)
population = newPopulation;
else {
System.out.println(
"ERROR: using a negative population.");
System.exit(0);
}
growthRate = newGrowthRate;
}
public String getName() {
return name;
}
public int getPopulation() {
return population;
}
public double getGrowthRate() {
return growthRate;
} }
Yes, we will define an even better version of this class later.
A mutator method can check to make sure that instance variables are set to proper values.
304 CHAPTER 5 / Defining Classes and Methods
LISTING 5.12 Using a Mutator Method (part 1 of 2) import java.util.Scanner;
/**
Demonstrates the use of the mutator method setSpecies.
*/
public class SpeciesFourthTryDemo {
public static void main(String[] args) {
SpeciesFourthTry speciesOfTheMonth =
new SpeciesFourthTry();
System.out.println("Enter number of years to project:");
Scanner keyboard = new Scanner(System.in);
int numberOfYears = keyboard.nextInt();
System.out.println(
"Enter data on the Species of the Month:");
speciesOfTheMonth.readInput();
speciesOfTheMonth.writeOutput();
int futurePopulation =
speciesOfTheMonth.predictPopulation(numberOfYears);
System.out.println("In " + numberOfYears +
" years the population will be " + futurePopulation);
//Change the species to show how to change //the values of instance variables:
speciesOfTheMonth.setSpecies("Klingon ox", 10, 15);
System.out.println("The new Species of the Month:");
speciesOfTheMonth.writeOutput();
futurePopulation =
speciesOfTheMonth.predictPopulation(numberOfYears);
System.out.println("In " + numberOfYears +
" years the population will be " + futurePopulation);
} }
Sample Screen Output
Enter number of years to project:
10
Enter data on the Species of the Month:
What is the species' name?
Ferengie fur ball
(continued)