Learning to Program With Robots Book
Learning to Program With Robots Book
COPYRIGHT 2007 Thomson Course ALL RIGHTS RESERVED. No part of ISBN 0-619-21724-3
Technology, a division of Thomson this work may be reproduced, tran-
Learning. Thomson Learning is a trade- scribed, or used in any form or by any Photo Credits
mark used herein under license. means—graphic, electronic, or mechani- Figure 1-5: Courtesy of
cal, including photocopying, recording, NASA/JPL-Caltech
Printed in the United States taping, Web distribution, or information Figure 1-22: Courtesy of the U.S. Navy
storage and retrieval systems—without Figure 3-3: Cartoon © 2005
1 2 3 4 5 6 7 8 9 CW 10 09 08 07 06 prior written permission of the publisher. ScienceCartoonsPlus.com. Used with
permission.
Disclaimer The Web addresses in this book are sub- Cover: Drawing © 2001 by Joel Weber
Thomson Course Technology reserves ject to change from time to time as nec- Becker. Used with permission.
the right to revise this publication and essary without notice.
make changes from time to time in its Some portions of this work are based on
content without notice. For permission to use material from this Karel++: A Gentle Introduction to the
text or product, submit a request online Art of Object-Oriented Programming
For more information, contact Thomson at http://www.thomsonrights.com by Joseph Bergin, Mark Stehlik,
Course Technology, 25 Thomson Place, Jim Roberts, and Richard Pattis.
Boston, MA 02210; or find us on Any additional questions about permis- Copyright © 1997 by John Wiley
the World Wide Web at sions can be submitted by e-mail to & Sons, Inc. Used with permission of
www.course.com. [email protected] John Wiley & Sons, Inc.
Contents
iii
CONTENTS iv
CONTENTS
Chapter 3 Developing Methods 115
3.1 Solving Problems 116
3.2 Stepwise Refinement 117
3.2.1 Identifying the Required Services 118
3.2.2 Refining harvestField 120
3.2.3 Refining harvestTwoRows 126
3.2.4 Refining harvestOneRow 127
3.2.5 Refining goToNextRow 129
3.2.6 Refining positionForNextHarvest 129
3.2.7 The Complete Program 130
3.2.8 Summary of Stepwise Refinement 132
3.3 Advantages of Stepwise Refinement 133
3.3.1 Understandable Programs 133
3.3.2 Avoiding Errors 134
3.3.3 Testing and Debugging 135
3.3.4 Future Modifications 136
3.4 Pseudocode 138
3.5 Variations on the Theme 139
3.5.1 Using Multiple Robots 140
3.5.2 Multiple Robots with Threads (advanced) 142
3.5.3 Factoring Out Differences 146
3.6 Private and Protected Methods 147
3.7 GUI: Using Helper Methods 151
3.7.1 Declaring Parameters 153
3.7.2 Using Parameters 153
3.8 Patterns 155
3.8.1 The Helper Method Pattern 155
3.8.2 The Multiple Threads Pattern 156
3.8.3 The Template Method Pattern 157
3.8.4 The Parameterized Method Pattern 158
3.9 Summary and Concept Map 159
3.10 Problem Set 160
CONTENTS
5.4 Boolean Expressions 231
5.4.1 Combining Boolean Expressions 231
5.4.2 Simplifying Boolean Expressions 236
5.4.3 Short-Circuit Evaluation 238
5.5 Exploring Loop Variations 239
5.5.1 Using a for Statement 239
5.5.2 Using a do-while Loop (optional) 242
5.5.3 Using a while-true Loop (optional) 243
5.5.4 Choosing an Appropriate Looping Statement 246
5.6 Coding with Style 246
5.6.1 Use Stepwise Refinement 247
5.6.2 Use Positively Stated Simple Expressions 247
5.6.3 Visually Structure Code 250
5.7 GUI: Using Loops to Draw 251
5.7.1 Using the Loop Counter 252
5.7.2 Nesting Selection and Repetition 253
5.8 Patterns 257
5.8.1 The Loop-and-a-Half Pattern 257
5.8.2 The Temporary Variable Pattern 258
5.8.3 The Counting Pattern 259
5.8.4 The Query Pattern 259
5.8.5 The Predicate Pattern 260
5.8.6 The Cascading-if Pattern 261
5.8.7 The Counted Loop Pattern 262
5.9 Summary and Concept Map 262
5.10 Problem Set 264
CONTENTS
7.6 GUI: Using Java Interfaces 374
7.6.1 Specifying Methods with Interfaces 375
7.6.2 Implementing an Interface 377
7.6.3 Developing Classes to a Specified Interface 378
7.6.4 Informing the User Interface of Changes 379
7.7 Patterns 381
7.7.1 The Test Harness Pattern 381
7.7.2 The toString Pattern 381
7.7.3 The Enumeration Pattern 382
7.7.4 The Assign a Unique ID Pattern 383
7.8 Summary and Concept Map 384
7.9 Problem Set 386
CONTENTS
9.9 Patterns 508
9.9.1 The Open File for Input Pattern 508
9.9.2 The Open File for Output Pattern 508
9.9.3 The Process File Pattern 508
9.9.4 The Construct Record from File Pattern 509
9.9.5 The Error-Checked Input Pattern 509
9.9.6 The Command Interpreter Pattern 510
9.10 Summary and Concept Map 511
9.11 Problem Set 512
CONTENTS
12.6 GUI: Layout Managers 680
12.6.1 The FlowLayout Strategy 680
12.6.2 The GridLayout Strategy 681
12.6.3 The BorderLayout Strategy 683
12.6.4 Other Layout Strategies 683
12.6.5 Nesting Layout Strategies 684
12.7 Patterns 686
12.7.1 The Polymorphic Call Pattern 686
12.7.2 The Strategy Pattern 687
12.7.3 The Equals Pattern 688
12.7.4 The Factory Method Pattern 689
12.8 Summary and Concept Map 689
12.9 Problem Set 690
Epilogue 765
Index 815
Preface
Approach
This text begins with programming virtual robots to teach object-oriented program-
ming in general (dark green in Figure 1). Once students are comfortable with many
aspects of objects and classes, the examples shift from robots to a much broader set of
examples (white). Each chapter ends with a section on graphics and graphical user
interfaces (light green), applying the concepts learned to a different context.
Transferring the knowledge gained using robots to another problem (graphics) is an
important part of mastering the material. The graphics sections at the end of each
chapter should be viewed as an integral part of the curriculum.
(figure 1)
Extending Classes with Services
Graphics
Collaborative Classes
Developing Methods
Other
Using Variables
Polymorphism
Arrays
1 2 3 4 5 6 7 8 9 10 11 12 13
Chapters
(figure 2)
We can easily “program” a student or instructor to complete this task with the follow-
ing instructions. Assume the person’s name is “Karl.”
xvii
PREFACE
Karl, move
Karl, pick up a thing
Karl, move
Karl, pick up a thing
Karl, move
Karl, pick up a thing
Karl, move
Karl, put down a thing
Karl, put down a thing
Karl, put down a thing
Karl, move
After verbally directing Karl, it is easy to introduce a simple program that does the
same thing where karl is the name of a robot object, as follows:
karl.move();
karl.pickThing();
karl.move();
karl.pickThing();
karl.move();
karl.pickThing();
karl.move();
karl.putThing();
karl.putThing();
karl.putThing();
karl.move();
There are additional details to cover before this Java fragment can be executed as a
complete program. However, these details form an easily learned pattern, leaving the
focus on using robot objects to accomplish tasks.
Other kinds of objects can be included in robot programs, including walls that can
block a robot from moving and lights that can be turned on and off. We can also cre-
ate new kinds of objects to use.
The fundamental object-oriented concepts learned with robot objects can all be
transferred to programs that have nothing to do with robots. Each chapter includes a
section focusing on graphics to help with the conceptual transfer. The latter part of the
book includes many examples that have nothing to do with robots.
For Students
You are about to embark on an exciting journey of learning to program using Java.
Before we begin, let’s take a few moments to orient ourselves to this textbook and to
the software you will need to complete all the exercises in the book.
Textbook Features
This textbook includes a number of features to make your life as a student easier. They
include the following:
Objectives: A brief list of objectives appears at the beginning of each chapter to pro-
vide an overview of the chapter contents. Knowing your destination helps you make
the most of your journey through the chapter.
Program listings: Each chapter contains many examples of working code demon-
strating the principles under discussion. The code is often shown as a complete listing
that is available for you to download, modify, and run yourself.
Figures: Each chapter provides a rich collection of figures to help illustrate the con-
cepts. Figures include UML diagrams, illustrations of robot programs, flowcharts,
screen shots of program output, and many others illustrating program features, object-
oriented concepts, and the principles of effective program design.
xix
PREFACE
Key terms and glossary: Every discipline has its own vocabulary, including com-
puter science. When a term is used for the first time, it’s highlighted. A complete glos-
sary in Appendix A is a handy reference for those times that you need a reminder.
Margin notes: The margin of each chapter contains four types of notes. Find the
Code notes direct you to files containing sample code. Key Idea notes summarize key
ideas discussed on the page and help you review. Looking Back notes link current dis-
cussions with ideas covered earlier in the book. Looking Ahead notes preview concepts
or techniques introduced in later chapters.
Pattern icons and discussion: In addition to margin notes, each chapter includes
pattern icons to highlight code or to explain common programming patterns. Learning
to recognize these patterns is an important part of becoming a good programmer. A
section named “Patterns” near the end of each chapter summarizes the patterns and
generalizes them so that they’re more broadly applicable.
Graphical user interface sections: Each chapter includes a section presenting the
chapter’s topics in the context of graphical user interfaces, helping you transfer your
understanding to new situations. In addition, many of the problems in each chapter
have a graphical user interface to make your homework look more like the programs
you use every day. In the early chapters, the interface is provided by the robot world. In
the middle chapters, graphical user interfaces are often provided to work with the code
you write. In the last chapter, you will write the interfaces yourself.
Concept maps and summaries: Each chapter concludes with a brief written sum-
mary of the important concepts, followed by a concept map. The concept map gives a
visual representation of the ideas discussed and how they are related to each other.
For Instructors
Robots uses objects to their fullest extent from day one, but doesn’t overwhelm the stu-
dents. How? It provides a rich set of classes that students use to learn about objects
before they are asked to write their own classes. Let’s explore this Use, Then Write ped-
agogy further by comparing it with the alternatives.
Object-Oriented Pedagogies
The concepts of object and class are intimately related. Each kind of object in a stu-
dent’s program is created from a class that a programmer writes to define the objects’
characteristics. Given that students need to master both using objects and writing the
classes that define them, a crucial question is how to order these topics. There are three
possibilities for writing classes and using the resulting objects:
Write and use: In this approach students are asked to master the basics of writing a
class at the same time they are learning how to use objects. One author, for example,
introduces classes and objects by describing how to use a bank account object in only
two pages. The author then delves into the details of writing the class to define it. This
requires introducing students to the distinction between class and object, declaring
objects, object instantiation, invoking methods, the structure of a class, defining meth-
ods, declaring parameters and passing arguments, return values, and instance vari-
ables. This presents an incredible cognitive load for students. The author chose a
wonderful example to convey all these concepts, but it is still difficult to understand all
the concepts all at once, even at an introductory level.
Write, then use: When actually writing a program, programmers first write the
required classes and then use the objects they define. I am aware of only one textbook
that has chosen to follow this same ordering. It includes a light treatment on the idea
of an object, but then delves into the details of writing classes with very few examples
of how the objects they define would be used. This lessens the cognitive load on the
students by focusing on just one of the two aspects, but leaves students wondering how
these classes are used. Much of the instruction on writing classes is lost because stu-
dents don’t have practical experience in using the resulting objects.
Use, then write: A third possibility is to first use objects and then learn how to write
classes defining new kinds of objects. Robots uses this approach. Students make exten-
sive use of robot objects, learning how to declare objects, instantiate objects, and
invoke their methods. All the details of writing their own classes come later, after they
are comfortable with using objects.
Robots provides a gentle but thorough introduction to object-oriented program-
ming using the Use, Then Write pedagogy. It’s an approach that helps students write
interesting, object-oriented programs right away. It uses objects early and consistently,
even with the traditional subjects of selection and repetition. Furthermore, it has been
classroom tested with over 6,000 students at the University of Waterloo.
xxi
PREFACE
Organization and Coverage
Chapter 1, “Programming with Objects,” introduces students to instantiating and
using objects.
Chapter 2, “Extending Classes with Services,” discusses extending an existing class
with new parameterless methods.
Chapter 3, “Developing Methods,” continues the theme of writing methods, but with
a focus on strategies for writing complex methods—pseudocode and stepwise refinement.
Chapter 4, “Making Decisions,” explores how to alter a program’s flow with repeti-
tion and selection, and includes the basics of the Boolean expressions used in such con-
structs. Introducing parameters adds even more flexibility to the methods students write.
Chapter 5, “More Decision Making,” continues exploring decision-making con-
structs with a process for writing correct loops, additional control statements, and
manipulating Boolean expressions. Temporary (local) variables are introduced to sim-
plify some algorithms.
Chapter 6, “Using Variables,” introduces integer instance variables and constants,
and expands on using temporary variables and parameter variables.
Chapter 7, “More on Variables and Methods,” examines using variables with types
other than int, including strings. Queries are used to examine the state of an object
and to test it using a test harness. This chapter also includes the first large case study
that does not involve robots or graphics.
Chapter 8, “Collaborative Classes,” presents classes that use references to another
class and thoroughly explores the differences between reference types and primitive types.
Exceptions are introduced, as well as Java collections to collaborate with many objects.
Chapter 9, “Input and Output,” covers reading information from files, writing
information to files, and interacting with users via the console.
Chapter 10, “Arrays,” explains how to work with arrays. A number of algorithms
are discussed, including a careful treatment of Selection Sort. Handling changing num-
bers of elements and multi-dimensional arrays are also covered.
Chapter 11, “Building Quality Software,” identifies characteristics of quality soft-
ware and explains how to follow a development process that promotes quality.
Chapter 12, “Polymorphism,” explores writing polymorphic programs using inher-
itance and interfaces. It also discusses building an inheritance hierarchy and using the
strategy and factory method patterns to make programs more flexible.
Chapter 13, “Graphical User Interfaces,” examines how to write a graphical user
interface using existing Java components, structure a graphical user interface using the
model-view-controller pattern and multiple views, and write new components for use
in graphical user interfaces.
PREFACE xxii
Dependencies
This text is, of necessity, printed in a particular order. You may find that a different orga-
nization suits you and your students better. The dependency chart shown in Figure 3
serves as a guide to reordering the material. The core material is shown with heavy lines
and should be presented in the order shown. Other material can be rearranged around it
at your discretion.
Textbook Features
Most of the textbook’s features are listed in the section for students. Three features
that instructors are more likely than students to appreciate are listed here:
Written exercises: The problem set at the end of each chapter includes written exer-
cises, which provide an opportunity for students to synthesize the ideas and techniques
they have learned in the chapter.
Programming exercises: The problem sets also include programming exercises,
which prompt students to write, improve, or experiment with smaller programs.
Programming projects: Finally, the problem sets present projects that encourage
students to create complete classes or programs.
Supplemental Resources
The following ancillary materials are available when this book is used in a classroom
setting. All of the teaching tools available with this book are provided to the instructor
on a single CD.
Instructor’s Manual: Additional instructional material to assist in class preparation,
including suggested syllabi for 14 and 16 week courses, and complete lecture notes.
PowerPoint Presentations®: This book comes with Microsoft PowerPoint slides for
each chapter. In addition to reviewing the chapter, they contain examples and case
studies illustrating the current topics. The slides are included as a teaching aid for class-
room presentation, to make available to students on the network for chapter review, or
to be printed for classroom distribution. Instructors can add their own slides for addi-
tional topics they may introduce to the class.
Solution Files: Sample solutions to most exercises.
Example Programs: The source code to almost all of the Java programs listed in
this book are easily available to you and your students. They are on the CD accompa-
nying each copy of the book, the Instructor Resources CD, the book’s Web site
(www.learningwithrobots.com), and the Thomson Course Technology Web site.
ExamView Test Bank: This assessment tool can help instructors design and
administer tests.
Software: JDK 5.0, jGRASP, and JCreator are included with each copy of this book.
Also provided are the libraries containing the robot classes. These libraries work with any
Java development environment (JDK 5.0 and above) and permit you to write, run, and
animate robot programs. Because a regular development environment is used, students do
xxiii
PREFACE
(figure 3)
Graphics
Dependency chart
Using Objects Intro Components
1.1–1.5 1.1–1.5
GUIs
13.1–13.8
PREFACE xxiv
not experience a transition in technology from writing robot programs to any other
kind of program. Complete graphical user interfaces are also provided in the support-
ing libraries for use in a number of homework problems.
Web site: www.learningwithrobots.com makes many of these resources available to
you and your students wherever you have an Internet connection.
Acknowledgements
In recalling those who have helped this book become a reality, I think of five groups
of people.
Originators: Rich Pattis developed the idea of using robots to teach programming
in the early 1980s. The idea was later adapted to an object-oriented style by Joe Bergin.
These are the giants upon whose shoulders this work stands. Without them, this text
and the core ideas it builds on would not exist. Thank you to Rich, in particular, who
has been very encouraging of my attempts to adapt his ideas to a full CS1 textbook.
Facilitators: Bruce Spatz, Bill Zobrist, Paul Crockett, and all of John Wiley and
Sons were flexible with their intellectual property rights to the original Karel the Robot
book. Thank you.
Brainstormers: Jack Rehder, Judene Pretti, and Arnie Dyck are all wonderful col-
leagues of mine at the University of Waterloo. Much of the text has been shaped and
improved by brainstorming sessions with them in the course of teaching this material
together. Thank you for the ideas, the clarifications, and the suggestions. A large group
of other instructors and tutors also contributed in countless smaller ways.
Polishers: Many people helped put the finishing touches on this book to get it ready
for publication. They include the team at Course Technology: Lisa Ruffolo, Alyssa
Pratt, Kelly Robinson, Mary Franz, and Mac Mendelsohn. Thank you for all your
hard work and willingness to listen to my views on the design. Carrie Howells, a col-
league at University of Waterloo, did a wonderful job of proofreading and critiquing
many chapters. Michael Diramio, one of our former tutors, rescued my sanity by writ-
ing some of the solutions to problem sets. Finally, a huge thank you to the reviewers:
John Ridgeway (Wesleyan University), Mary Goodwin (Illinois State University), Noel
LeJeune (Metropolitan State College of Denver), and especially Rich Pattis (Carnegie
Mellon University). Their insightful comments caused me to rework many sections that
I had thought were finished.
Cheerleaders: My two sons, Luke and Joel, who can hardly wait to learn to pro-
gram with “Dad’s robots,” cheered me on. Joel’s artwork graces the cover. A colleague,
Sandy Graham, was a wonderful evangelist for the approach.
Finally, the biggest thank you is to Ann, the most wonderful woman a man could
ever marry, for her indulgence as I wrote.
Chapter Objectives
After studying this chapter, you should be able to:
➤ Describe models
➤ Describe the relationship between objects and classes
➤ Understand the syntax and semantics of a simple Java program
➤ Write object-oriented programs that simulate robots
➤ Understand and fix errors that can occur when constructing a program
➤ Read documentation for classes
➤ Apply the concepts learned with robots to display a window as used in a graphical
user interface
A computer program usually models something. It might be the ticket sales for a con-
cert, the flow of money in a corporation, or a game set in an imaginary world.
Whatever that something is, a computer program abstracts the relevant features into a
model, and then uses the model to help make decisions, predict the future, answer
questions, or build a picture of an imaginary world.
In this chapter, we create programs that model a world filled with robots, directing
them to move, turn, pick up, transport, and put down things. This robot world is sim-
ple to model, but quickly reveals key concepts of object-oriented programming:
objects, classes, attributes, and services.
1
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 2
2
OBJECTS
Fifteen years ago I went to the local concert hall and asked the ticket agent for two
tickets for the March 21 concert. “Where do you want to sit?” he asked.
The agent grabbed a printed map of the concert hall. It was clearly dated “March 21,”
noted the name of the performer, and showed a map of the auditorium’s seats. Seats
that had already been sold were marked with a red X. The seats were also color-coded:
the most expensive seats were green, the moderately priced seats were black, and the
least expensive seats were blue.
Fifteen years ago, the ticket agent showed me the map and stabbed his finger on a pair
of seats. “These are the best seats left, but the choice is yours.”
I quickly scanned the map and noticed that a pair of less expensive seats with almost
the same sightlines was not far away. I chose the cheaper seats, and the agent promptly
marked them with a red X.
Fast-forward fifteen years. Today I order tickets from the comfort of my home over the
Web. I visit the concert hall’s Web site and find the performance I want. I click the
“purchase tickets online” button and am shown a color-coded map of the theatre. I
click on the seats I want, enter my credit card information, and am assured that the
tickets will be mailed to me promptly.
A model is a simplified description of something. It helps us, for example, make deci- KEY IDEA
sions, predict future events, maintain up-to-date information, simulate a process, and A model is a
so forth. Originally, the local concert hall modeled ticket sales with a simple paper simplified description
of something.
map of the auditorium. Later, a Web-based computerized model performed the same
functions—and probably many more.
To be useful, a model must be able to answer one or more questions. The paper-based
model of ticket sales could be used to answer questions such as:
➤ What is the date of the concert?
➤ Who is playing?
➤ How many tickets have been sold to date?
➤ How many tickets are still unsold?
➤ Is the ticket for seat 22H still available?
➤ What is the price of the ticket for seat 22H?
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 3
1.1 MODELING
➤ Which row has unsold tickets for 10 consecutive seats and is closest to the stage?
➤ What is the total value of all the tickets sold to date?
WITH
Models often change over time. For instance, the ticket sales model was updated with
two new red X’s when I bought my tickets. Without being updated, the model quickly
OBJECTS
diverges from the thing it represents and loses its value because the answers it provides
are wrong.
We often speak of models or elements of a model as if they were real. When the ticket
agent pointed to the map and said, “These are the best seats left,” we both knew that
what he was pointing at were not seats, but only images that represented actual seats.
The model provided a correspondence. Anyone could use that model to find those two
seats in the concert hall.
We often build models without even being aware of it. For example, you might make
a mental list of the errands you want to run before having supper ready for your
roommate at 6 o’clock, as shown in Figure 1-1: stopping at the library to pick up a
book (10 minutes), checking e-mail on a public terminal at the library (5 minutes),
and buying a few groceries (10 minutes). Checking your watch (it’s 4:15) and factor-
ing in 45 minutes for the bike ride home and 30 minutes to prepare supper, you esti-
mate that you can do it all, with a little time to spare. It takes longer than expected,
however, to find the book, and there’s a line at the library checkout counter. The
library errand took 20 minutes instead of 10. Now it’s 4:35, and you must make some
choices based on your updated model: have supper a little late, skip the e-mail, hope
that you can cook supper in 25 minutes instead of 30, and so forth. You have been
modeling your time usage for the next two hours.
(figure 1-1)
4:15 Pick up library book.
Sample schedule 4:25 Check e-mail.
4:30 Buy groceries.
4:40 Bike home.
5:25 Cook supper.
6:00 Supper.
KEY IDEA Models form an abstraction. Abstractions focus only on the relevant information and
Models focus on organize the remaining details into useful higher-level “chunks” of information. People
relevant features. can only manage about seven pieces of information at a time, so we must carefully
choose the information we manage. By using abstraction to eliminate or hide some
details and group similar details together into a chunk, we can manage more complex
ideas. Abstraction is the key to dealing with complexity.
For example, the ticket sales model gives ticket buyers and agents information about
which tickets are available, where the corresponding seats are located, and their price.
These were all relevant to my decision of which tickets to purchase. The map did not
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 4
4
OBJECTS
provide information about the seat’s fabric color, and I really didn’t care, because that
WITH
was irrelevant to my decision. Furthermore, the color-coding of the concert hall map
conveniently chunked information, which helped me make a decision quickly. It was
CHAPTER 1 | PROGRAMMING
easier to see all the least expensive seats in blue rather than consulting a long list of seat
numbers.
Beyond information, models also provide operations that can be performed on them.
In the concert hall model operations include “sell a ticket” and “add a new concert.”
In the informal time management model for errands, operations include “insert a new
errand,” “drop an errand from the list,” and “recompute the estimated start time for
each errand.”
The concert hall’s computer program and the paper map it replaced model the ticket- KEY IDEA
selling problem using different technologies. One uses pre-printed sheets of paper Object-oriented
marked with a simple X. The other involves a computer with a detailed set of instruc- programs use
software objects to
tions, called a program. If the program is written in an object-oriented programming
model the problem
language, such as Java, the computer program uses cooperating software objects. A at hand.
software object usually corresponds to an identifiable entity in the problem. The con-
cert hall program probably has an object modeling the concert hall’s physical layout, a KEY IDEA
collection of objects that each models a seat in the concert hall, and another collection Computer science has
of objects that each models an upcoming concert. A program maintaining student a specialized
vocabulary to allow
enrollments in courses would likely model each student with an object, and use other
precise
objects to model each course. communication. You
must learn this
Each of the software objects can perform tasks such as: vocabulary.
➤ Maintain information about part of the problem the program models.
➤ Answer questions about that part of the problem based on the information it
maintains.
➤ Change its information to reflect changes in the real-world entity it models.
The information kept by the object is called its attributes. Objects respond to queries KEY IDEA
for information and to commands to change their attributes. Queries and commands Server objects
are collectively referred to as services. An object provides these services to other provide services—
queries and
objects, called clients. The object providing the service is called, appropriately, the
commands—to client
server. We will explore these concepts in the coming pages. objects.
Queries are the questions to which an object can respond. A query is always answered
by the object to which it is directed. It might be true or false (“Is the ticket for seat 22H
still for sale?”), a number (“How many tickets have been sold?”), a string of characters
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 5
1.1 MODELING
(“What is the name of the band that is playing?”), or even another object such as a
date object (“What is the date of the concert?”). Queries are said to return answers to
their clients. An object can’t respond to just any query, only to those it was designed
WITH
and programmed to support.
OBJECTS
KEY IDEA The answers provided by queries are always based on the object’s attributes. If an
Objects have object must answer the query, “What is the date of the concert?” then it must have an
attributes. Answers to attribute with information about the date. Similarly, if it must answer the question,
queries are based on “How many tickets have been sold to date?” it must have an attribute that has that
the values of the
attributes.
information directly, or it must have a way to calculate that information, perhaps by
counting the number of tickets that have been sold. Information about which tickets
have been sold would be kept in an attribute.
The concert hall’s program must model ticket sales for many concerts, each repre-
sented by its own concert object. If we look at several concert objects, we’ll notice
they all have the same set of attributes, although the values of those attributes may be
different. One way to show the differing attribute values is with an object diagram, as
shown in Figure 1-2. Each rounded rectangle represents a different concert object.
The type of object is shown at the top. Below that is a table with attribute names on
the left and attribute values on the right. For example, the attribute “date” has a value
of “21-March-2008” for one concert. That same concert has the value “Great Big
Sea” for the “performer” attribute.
(figure 1-2)
Concert
Object diagram showing
date: 28-March-2008
three concert objects with
performer: Toronto Symphony
their attributes
unsoldTickets: 35A, 35B, 35C
Concert
soldTickets: 10A, 10B, ...,3 4 Z, ...
... 21-March-2008
35D , date:
performer: Great Big Sea
unsoldTickets: 10D, 22H, 25A,
25B, 25C, 28Z,...
Sold Seats: 10A, 10B, 10C,
Concert Type of
...,2 2 N, 22P, ... object
date: 22-March-2008
performer: U2
Attribute Attribute
names unsoldTickets: 35A, 35B, 35C values
soldTickets: 10A, 10B, ..., 34Z,
... 35D, ...
One analogy for objects is that an object is like a form, such as an income tax form. The
government prints millions of copies of the form asking for a person’s name, address, tax-
payer identification number, earned income, and so forth. Each piece of information is pro-
vided in a little box, appropriately labeled on the form. Each copy of the form starts like all
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 6
6
OBJECTS
the others. When filled out, however, each form has unique values in those boxes. It could
WITH
be that two people have exactly the same income and birthday, with the result that some
forms have the same values in the same boxes—but that’s only a coincidence.
CHAPTER 1 | PROGRAMMING
Just as each copy of that tax form asks for the same information, every concert object KEY IDEA
has the same set of attributes. Each copy of the form is filled out with information for Every object of a given
a specific taxpayer; likewise, each concert object’s attributes have values for a specific type has the same set
concert. In general, there may be many objects of a given type. All have the same set of of attributes,
but usually has
attributes, but probably have different values for those attributes. different values for the
attributes.
Commands
When a ticket is sold for seat 22H for the March 21 concert, the appropriate concert KEY IDEA
object must record that fact. This record keeping is done with a command. The object Commands change
is “commanded” to change its attributes to reflect the new reality. This change can be the state of the
object.
visualized with a state change diagram, as shown in Figure 1-3. A state change diagram
shows the state of the object before the command and the state of the object after the
command. The state is the set of attributes and their values at a given point in time. As
time passes, it is normal for the state of an object to change.
(figure 1-3)
Concert Concert
date: 21-March-2008 date: 21-March-2008 State change diagram
performer: Great Big Sea performer: Great Big Sea showing the change in
unsold 10D, 22H, 25A, unsold 10D, 25A, state after a command to
Command:
Tickets: 25B, 25C , 28Z,... Tickets: 25B, 25C, 28Z,... sell seat 22H is given to a
Sell ticket
soldTickets: 10A, 10B, 10C, 22H soldTickets: 10A, 10B, 10C, 22H, concert object
..., 22N, 22P, ... ..., 22N, 22P, ...
Classes
When we write a Java program, we don’t write objects, we write classes. A class is a KEY IDEA
definition for a group of objects that have the same attributes and services. A pro- A Java programmer
grammer writing the concert hall program would write a concert class to specify that writes a class by
all concert objects have attributes storing the concert’s date, performers, and so on. specifying the
attributes and
The class also specifies services that all concert objects have, such as “sell a ticket,” and services the classes’
“how many tickets have been sold?” objects will possess.
Once a concert class is defined, the programmer can use it to create as many concert
objects as she needs. Each object is an instance, or one particular example, of a class.
When an object is first brought into existence, we sometimes say it has been instantiated.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 7
1.1 MODELING
The distinction between class and object is important. It’s the same as the distinction
between a factory and the cars made in the factory, or the distinction between a cookie
cutter and the cookies it shapes. The pattern used to sew a dress is different from the dress
WITH
produced from it, just as a blueprint is different from the house it specifies. In each case,
OBJECTS
one thing (the class, factory, or cookie cutter) specifies what something else (objects, cars,
or cookies) will be like. Furthermore, classes, factories, and cookie cutters can all be used
to make many instances of the things they specify. One factory makes many cars; one class
can make many objects. Finally, just as most of us are not interested in cookie cutters for
their own sakes, but in the cookies made from them, our primary interest in classes is to
get what we really want: software objects that help model some problem for us.
Class Diagrams
KEY IDEA Just as architects and dress designers communicate parts of their designs visually through
A class diagram blueprints and patterns, software professionals use diagrams to design, document, and
summarizes all of the communicate their programs. We’ve already seen an object diagram in Figure 1-2 and a
objects belonging to
state change diagram (consisting of two object diagrams) in Figure 1-3. Another kind of
that class.
diagram is the class diagram. Class diagrams show the attributes and services common to
all objects belonging to the class. The class diagram for the concert class summarizes all
the possible concert objects by showing the attributes and services each object has in com-
mon with all other concert objects.
A class diagram is a rectangle divided into three areas (see Figure 1-4). The top area
contains the name of the class. Attributes are named in the middle area, and services
are in the bottom area.
Every computer program has a model of a problem. Sometimes the problem is tangi-
ble, such as tracking concert ticket sales or the time required to run errands before sup-
per. At other times, the problem may be more abstract: the future earnings of a
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 8
8
OBJECTS
company under a given set of assumptions or the energy loss of a house. Sometimes the
WITH
Many of the programs in this textbook model imaginary robots and the city in which
they operate. The programs cause robots to move on the computer screen as they per-
form various tasks. This model was chosen to be basic enough to grasp easily, yet com-
plex enough to be interesting; simple enough to be easy to program, yet rich enough to
show many important object-oriented concepts. The robots and their world are
described in Section 1.2. Section 1.3 describes using software objects to model the
robots, and Section 1.4 will present the first program.
The robots our programs model are similar to the small robotic explorers NASA
landed on Mars. The first, named Sojourner, landed on Mars on July 4, 1997. It could
move around the Martian landscape, take photographs, and conduct scientific experi-
ments. Sojourner was about two feet long and could travel at a top speed of two feet
per minute. A photo of the explorer is shown in Figure 1-5.
(figure 1-5)
Sojourner, a robotic
explorer landed on Mars
by NASA
Sojourner was controlled from Earth via radio signals. Because radio signals take approx-
imately 11 minutes to travel from Earth to Mars, Sojourner could not be controlled in real
time. (Imagine trying to drive a car with a minimum of 22 minutes elapsing between turn-
ing the steering wheel and receiving feedback about the change in direction.) Instead, con-
trollers on Earth carefully mapped out the movements and tasks Sojourner was to do,
encoding them as a sequence of messages. These messages were sent to Sojourner, which
then attempted to carry them out. Feedback regarding the entire sequence of messages
was sent back to Earth, where controllers then worked out the next sequence of messages.
Sojourner had a computer on board to interpret the messages it received from Earth
into electrical signals to control its motion and scientific instruments. The computer’s
processor was an Intel 80C85 processor containing only 6,500 transistors and execut-
ing about 100,000 instructions per second. This processor was used almost 15 years
earlier in the Radio Shack TRS-80 home computer.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 9
Why did Sojourner use such a primitive processor? The 80C85 consumes tiny amounts
of power compared with its state-of-the-art cousins and is much more likely to operate
correctly in the presence of cosmic rays and extreme temperatures.
What a city does have are roads. Some roads, called streets, run east and west, while
other roads, called avenues, run north and south. (A helpful way to remember which is
which is that the “A” and “v” in “Avenue” point up and down—or north and south on
a map—whereas the cross strokes of the “t”s in “Street” run east and west.)
Streets and avenues are both numbered starting with 0. This convention is unusual
among urban planners, but normal among Java programmers. Street 0 is located on
the north (top) side, while Avenue 0 runs along the west (left) side. The place where
these two roads meet is called the origin.
Figure 1-6 shows a small portion of a city with a robot facing east at the origin and
another facing south at the intersection of 1st Street and 2nd Avenue. We can use a short-
hand notation for specifying intersections. Instead of “1st Street and 2nd Avenue,” we can
write (1, 2). The first number in the pair gives the street, and the second gives the avenue.
1 We e
will often name robots “karel” (pronounced “kär- l”—the same as “Karl” or “Carl”) in recogni-
tion of the Czechoslovakian dramatist Karel Capek (1890–1938), who popularized the word robot in
his 1921 play R.U.R. (Rossum’s Universal Robots). The word robot is derived from the Czech word
robota, meaning “forced labor.” The name is lowercase, in keeping with Java style.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 10
10
OBJECTS
0 1 2 3 (figure 1-6)
0
WITH
1
and one at the intersection
2 of 1st Street and 2nd Avenue
facing south
Intersections are unusually wide. Many robots can be on the same intersection at the
same time without interfering with each other.
The city shown in Figure 1-6 contains three walls, including two at the edges of the
intersection at (1, 3). Another wall is immediately in front of the robot at (1, 2) and
blocks it from proceeding south. The robot may go around the wall, of course.
Intersections may also have nondescript things. They are purposefully nondescript so
we can imagine them to be whatever we want them to be: newspapers, lights, pieces of
carpet, or flags. One such thing appears at (2, 0) in Figure 1-6. Robots can usually pick
a thing up, put it in a backpack to carry it somewhere else, and then put it down again.
Eventually we will learn how to define classes of things with different appearances and
services. Two examples already exist: flashers, like you might find marking a construc-
tion site, and streetlights.
1.2.3 Robots
Robots exist to serve their clients. The four services they perform most often are mov-
ing, turning, picking things up, and putting things down. Some additional services
robots provide include answering queries about their location and direction, and
responding to a command controlling their speed.
These are primitive services. Clients using a robot must give many small instructions to
tell the robot how to perform a task. Beginning with Chapter 2, we will learn how to cre-
ate new kinds of robots that provide services tailored to solving the problem at hand.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 11
11
Turning
Robots always start out facing one of the four compass points: north, south, east, or
west. Because robots can turn only in 90-degree increments, they always face one of
those four directions (except while they are in the act of turning).
LOOKING AHEAD Robots do not have a turnRight instruction because it is not needed; three turnLeft
In Chapter 2, we will messages accomplish the same task.
create a new kind of
robot that responds Turning is a safe activity. Unlike moving, picking things up, or putting things down,
to a turnRight nothing can go wrong when turning.
message.
Moving
When a robot receives a move message, it attempts to move from its current intersec-
tion to the next intersection in the direction it is facing. It remains facing the same
direction. Robots can’t stop between intersections; they are either on an intersection or
in the process of moving to another one.
Things can go wrong when a robot receives a move message. In particular, if there is a
wall immediately in front of a robot, moving causes that robot to break. When a robot
breaks, it is displayed in three pieces, as shown in Figure 1-7, an error message is
printed on the screen, and the program halts. An example of the error message is
shown in Figure 1-20.
(figure 1-7)
12
OBJECTS
Handling Things
WITH
When a robot receives a pickThing message, it attempts to pick up a thing from its
CHAPTER 1 | PROGRAMMING
current intersection. If there are several things the robot could pick up, it randomly
chooses one of them. Robots have a backpack where they carry the things they pick up.
Things are small and the backpack is large, so many things fit in it. Robots can also put
things down in response to the putThing message.
As you might expect, a robot can experience difficulties in handling things. If a robot LOOKING AHEAD
receives a pickThing message when there is nothing to pick up on the current inter- In Chapter 4 we will
section, the robot breaks. Similarly, when a robot receives a putThing message and its learn how to write
programs where
backpack is empty, the robot breaks. As with moving, after such a malfunction the
robots can detect if
robot appears damaged, an error message is printed, and the program halts. something can be
picked up.
The software does not actually control real, physical robots that you can touch. Instead,
it displays images of robots on the computer screen. The programs we will write cause
the images to move about the city (also displayed on the screen) and perform various
tasks. These programs are only useful in that they provide an excellent way to learn
how to program a computer. You can transfer the knowledge you gain in writing robot
programs to writing programs that model the problems that concern you.
As shown earlier in Figure 1-4, we can summarize objects with a class diagram that
shows the attributes and services of each object belonging to the class. A class diagram
for the Robot class is shown in Figure 1-8. The class diagram shows the four services
discussed earlier, along with a special service to construct Robot objects.
(figure 1-8)
Robot
int street Incomplete class diagram
int avenue for the Robot class
Direction direction
ThingBag backpack
Robot(City aCity, int aStreet, int anAvenue
Direction aDirection)
void move( )
void turnLeft( )
void pickThing( )
void putThing( )
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 13
13
Recall that the middle section of the class diagram lists the attributes. From the Robot
class diagram, we can infer that each Robot object has four attributes. We might guess
that the two named street and avenue record the street and avenue the robot cur-
WITH
rently occupies, and that direction records the direction it is facing. Finally, the
backpack attribute might plausibly be where each robot keeps track of the things it is
SOFTWARE OBJECTS
carrying. We can’t know any of these details with absolute certainty, but it makes sense
given what we know about the robot world described in Section 1.2, “Understanding
Karel’s World,” and from the names of the attributes themselves.
LOOKING AHEAD Preceding the names of the attributes is the type of information to which they refer. The
Attributes can have type specifies the set of valid values for the attribute. The street and avenue attrib-
types other than int. utes are preceded by int, which is Java shorthand for “integer.” This information
See Chapter 7. makes sense because we have been referring to streets and avenues with integers, such as
0, 1, or 5, but never with real numbers, such as 3.14159.
LOOKING AHEAD The type of backpack is a ThingBag. ThingBags can store a variable number of
The type of an Thing objects. This attribute illustrates that a robot object makes use of other
attribute can be the objects—these objects cooperate to model the problem.
name of a class, like
ThingBag. See Sometimes a class diagram does not include all of the attributes. Why? The important
Chapter 8. part of a class is the services its objects provide—the things they can do. It is appropri-
KEY IDEA ate to say that the programmer implementing the class needs to know the attributes,
Class diagrams are but it’s no one else’s business how the object works internally. Nevertheless, we will
designed to help you find it helpful in discussing the services to know what attributes they need to maintain.
understand a class. The class diagram shown earlier in Figure 1-8 occupies a middle ground. It shows
They may omit low- attributes that contribute to understanding the class, but omits others that don’t, even
level details in the
interest of clarity.
though they are necessary to implement the class.
1.3.2 Constructors
The Robot class diagram lists five services: Robot, move, turnLeft, pickThing, and
putThing.
KEY IDEA The first, Robot, is actually a constructor rather than a service, but is listed here for
Constructors create convenience. Although constructors have some similarities to services, there are impor-
new objects. Services tant differences. The key difference is their purposes: services are performed by an
are performed by an object for some client, while constructors are used by a client to construct a new object.
object that
already exists.
(Recall that the client is the object using the services of the Robot object.) Constructors
always have the same name as the class.
When a new object is constructed, its attributes must be set to the correct initial values. The
initial position of the robot is determined by the client. The client communicates the desired
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 14
14
OBJECTS
values, for each of the constructor’s parameters. The parameters are shown in the class dia- The constructor is
gram between parentheses: Robot(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue,ƒ responsible for
CHAPTER 1 | PROGRAMMING
correctly initializing
DirectionƒaDirection). Notice that there is a remarkable similarity between the con-
the attributes.
structor’s parameters and the classes’ attributes.
1.3.3 Services
Suppose we have a robot that we refer to as karel. We can tell karel what to do by KEY IDEA
sending it messages such as move or turnLeft. A message requests that the object per- A client requests a
form one of its services for a client, the sender of the message. The services in the service from an object
by sending a message
Robot class diagram tell us which messages we can send to a robot object. A message
to the object.
to karel contains the name karel, a dot, and the name of a service followed by an
argument list and a semicolon, as shown in Figure 1-9.
In this message, karel identifies who is supposed to move. We don’t want to move any KEY IDEA
robot (or vehicle or cow or anything else that can move), only the particular robot A message is always
known as karel. Stating the object first is like having a conversation in a group of peo- sent to a specific
object.
ple. When you speak to a specific person within the group, you often start by saying his
or her name—“Karel, please pass the potatoes.” Because a program almost always
contains many objects, identifying the recipient of the message is a requirement.
After referring to the object, we place a dot, which connects the object to the message, KEY IDEA
move. The message must be one of the services the object knows how to perform—a Each message must
service listed in the class diagram. Sending the message “jump” to karel would result correspond to one of
its recipient’s
in an error because karel does not have a jumping service.
services.
Like the constructor, a service may have a list of parameters to convey information the
object needs to carry out the service. Parameter lists always begin and end with paren-
theses. None of the four Robot services listed require additional information, and so
all their parameter lists are empty (but the parentheses must still be present).
Consequently, when the corresponding messages are sent to a Robot object no argu-
ments are needed, although parentheses are still required.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 15
15
When a robot receives the move message, it moves. It also updates the street and
avenue attributes to reflect its new location. If karel is standing at 2nd Street and
1st Avenue facing south, street and avenue contain 2 and 1, respectively. As the
move service is executed, street is updated to 3, but avenue remains 1. The Robot
object also sends many messages to other objects in the program to make an image
move on the computer’s screen.
KEY IDEA The move service is preceded in the class diagram with the word void. This word
Commands are means that move is a command that changes the state of a robot object rather than a
preceded by the word query that answers a question. If it were a query, void would be replaced with the type
void in class
of the answer it returns—an integer, a real number, or a string of characters, for exam-
diagrams.
ple. Using the keyword void to mean “returns no answer” can be related to an English
meaning of the word: “containing nothing.”
Invoking the remaining services listed in the class diagram (turnLeft, pickThing,
and putThing) follows the same pattern as move. Start with a reference to a specific
Command Invocation robot. Then add a dot, the message you want to send the robot, an empty argument
list, and a semicolon. The designated robot responds by turning, picking, or putting, as
described earlier. Furthermore, the services of any other class are invoked by following
this same pattern. Not only the Robot, Wall, and Thing classes, but also classes mod-
eling students or employees or printers or checkbooks or concerts follow this pattern.
All objects follow this pattern.
1.4.1 Situations
When writing a program (or reading a program someone else has written), you must
understand what the program is supposed to do. For our first program, let’s imagine that
a delivery robot is to pick up a parcel, represented by a Thing, at intersection (1, 2) and
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 16
16
OBJECTS
deliver it to (2, 3). The initial situation, shown in Figure 1-10, represents the state of the KEY IDEA
WITH
city before the robot does its task. The final situation, also shown in Figure 1-10, is how Many robot tasks can
we want the city to appear after the task is done. be specified by
CHAPTER 1 | PROGRAMMING
0 1 2 3 0 1 2 3 (figure 1-10)
0 0
Initial and final situations
1 1 of a task to pick up and
deliver a parcel
2 2
3 3
This path is illustrated on the left side of Figure 1-11. A more roundabout path is
shown on the right. The roundabout path also accomplishes the task but results in a
less efficient solution. If the robot were real, which solution would cause the robot to
use the least power from its battery pack?
0 1 2 3 0 1 2 3 (figure 1-11)
0 0
Two approaches for the
1 1 robot to perform the
delivery task
2 2
3 3
Obviously, the robot could take any one of many possible paths to solve this problem.
The following program takes the more efficient approach outlined in Figure 1-11.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 17
17
LOOKING BACK Listing 1-1 shows the source code of a program to carry out the task just described. The
Before you can run the source code contains the words and other symbols we write to instruct the computer.
program in Listing 1-1,
you need to have Based on the previous discussion, you should be able to read the main body of the pro-
software installed on gram and have a feel for how it works. Of course, you won’t understand everything
your computer. See now, but it will all be explained in due course. You may be interested in knowing that
the Preface for
instructions.
much of the code in Listing 1-1 is repeated in every Java program and even more is
repeated in every program using robots.
The line numbers on the left are not part of the program. They are included in the list-
ing only so we can easily refer to specific parts of the program.
This program divides naturally into three parts: the code in lines 8–10, which con-
structs objects to set up the initial situation, the code in lines 13–22, which sends mes-
sages directing the robot to the final situation, and the remaining “housekeeping”
required by the Java language. This division is reinforced by the comments written by
the programmer at lines 7 and 12.
At a lower level of detail, Table 1-1 describes the purpose of each line of code. Use it to
get a feel for the kinds of information present in a Java program, but don’t expect to
understand it all this early in the book. Lines 8–22 are the most important for right
now; all will be discussed in detail later in the book.
The source code for the program in Listing 1-1 is available from the Robots Web site.
Download the file examples.zip. After saving and expanding it, look in the directory
ch01/deliverParcel/.
18
OBJECTS
Listing 1-1:
WITH
14 ƒƒƒƒkarel.move();
15 ƒƒƒƒkarel.pickThing();
16 ƒƒƒƒkarel.move();
17 ƒƒƒƒkarel.turnLeft();ƒƒƒƒƒƒ// start turning right as three turns lefts
18 ƒƒƒƒkarel.turnLeft();
19 ƒƒƒƒkarel.turnLeft();ƒƒƒƒƒƒ// finished turning right
20 ƒƒƒƒkarel.move();
21 ƒƒƒƒkarel.putThing();
22 ƒƒƒƒkarel.move();
23 ƒƒ}
24 }
1 Makes code written by other programmers, such as the Robot class, easily An explanation of
available. Listing 1-1
2, 11 Blank lines often add clarity for a person reading the program, but do not
affect its execution in any way.
4, 6, 23, 24 Java uses braces to give structure to the program. The braces at lines 4
and 24 contain all the code belonging to the class. The braces at lines 6
and 23 contain all the code belonging to the service named main.
5 Identifies where the program will begin execution. Every program must
have a line similar to this one.
7, 12 Text between two consecutive slashes and the end of the line is a comment.
Comments are meant to help human readers and do not affect the execution
of the program in any way.
13–22 Messages telling the robot named karel which services it should perform.
We now turn to a detailed discussion of lines 8–22. The remainder of the program will
be discussed in Section 1.4.7.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 19
19
The initial situation, as shown in Figure 1-10, is set up by constructing three objects in
lines 8-10. The City object corresponds to all the intersections of streets and avenues.
The Robot and Thing objects obviously correspond to the robot and thing shown in
the initial situation.
Each of the four statements has a similar structure. Consider line 10 as an example:
10ƒƒƒƒRobotƒkarelƒƒ=ƒnewƒRobot(prague,ƒ1,ƒ0,ƒDirection.EAST);
Object Instantiation
On the left side of the equal sign (=) is a variable declaration. A variable declaration
LOOKING AHEAD
first states the type of the object—in this case Robot—and then the name of the vari-
We will see similar able being declared, karel. A variable uses a name (karel) to refer to a value (in this
declarations in other
situations. All have
case a Robot object), allowing the value to be used easily in many places in the pro-
the type first, then gram. The choice of variable name is up to the programmer. A meaningful name helps
the name. the understanding of people reading the program, including the programmer.
The object is instantiated on the right side of the equal sign. The keyword new signals
that a new object will be constructed. After new, a constructor is named, in this case,
Robot. It must be compatible with the type of the variable on the left side of the equal
sign. For now, “compatible” means the two are identical. Eventually, we will ease this
restriction.
When an object is constructed, the client object may need to provide information for
the constructor to do its job. In this case, the client specifies that the new robot is to be
created in the city named prague at the intersection of Street 1 and Avenue 0, facing
east. Recall the values or arguments provided at line 10:
10ƒƒƒƒRobotƒkarelƒƒ=ƒnewƒRobot(prague,ƒ1,ƒ0,ƒDirection.EAST);
The arguments list the city first, then the street, avenue, and direction—in that order.
Furthermore, the types of the values provided match the types given in the parameter
list. prague refers to a City object, just as the parameter list specifies what the first
value must be. Similarly, the second and third values are integers, just as the types for
avenue and street specify.
LOOKING AHEAD The type of the Robot constructor’s last parameter is Direction and the value passed
We will learn how to to it in line 10 of Listing 1-1 is Direction.EAST. Direction is a class used to define
define our own sets of values with program-specific meanings. EAST is one of those special values. It should
values in Chapter 7.
come as no surprise that WEST, NORTH, and SOUTH are other values defined by the
Direction class. When one of these values is used in a program, its defining class,
Direction, must accompany it.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 20
20
OBJECTS
Finally, line 10 ends with a semicolon (;), which marks the end of the statement. The
WITH
function of semicolons in Java is similar to periods marking the end of English sentences.
CHAPTER 1 | PROGRAMMING
Lines 13–22 in Listing 1-1 direct the robot from the initial situation to the final situa-
tion. They give a precise sequence of instructions the robot must perform to accom-
plish the task. If the instructions were performed in a different sequence, the problem
is unlikely to be solved correctly.
Each instruction follows the command invocation pattern observed earlier: give the
name of the object being addressed, a dot, and then the service desired from that
object. For example, karel.move(); instructs the robot karel to execute its move Command Invocation
service. The result is that the robot moves from one intersection to the next intersection Sequential Execution
in the direction it is currently facing (unless something blocks its way).
Each of these instructions is one statement in the program. Lines 8-10 are another kind
of statement, declaration statements, because they declare variables and assign initial
values to them. In time, we will learn other kinds of statements that allow the program
to make decisions, execute other statements repeatedly, and so on. Recall that all state-
ments end with a semicolon.
Simulating the execution of a program, also known as tracing a program, is one way to
understand what it does and verify that the sequence of statements is correct. Tracing a
program is like building a state change diagram, such as the one shown in Figure 1-12,
for each statement in the program. Notice that the diagram covers two statements and
shows the state before each statement and after each statement.
A program almost always involves many objects, so this can involve tracking a lot of
information. Also, a formal state change diagram is difficult to draw and maintain. To
overcome these problems, it’s best to decide at the outset what information is relevant
and to organize it in a table. It seems that the relevant information in the
DeliverParcel program (Listing 1-1) includes the state of the robot and the state of
the parcel, represented by a Thing. For the state of the robot, we will have four columns,
one for each of the four attributes in the class diagram shown in Figure 1-8. For the par-
cel we will use a column for its street and another for its avenue. These are listed as head-
ings in Table 1-2. If the program contained another robot, we would trace it by adding
another group of four columns to the table.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 21
21
A table recording the Program Statement street avenue direction backpack street avenue
change of program 1 0 east 1 2
state while tracing the
execution of the 13ƒƒkarel.move();
DeliverParcel 1 1 east 1 2
program in Listing 1-1
14ƒƒkarel.move();
1 2 east 1 2
15ƒƒkarel.pickThing();
1 2 east parcel 1 2
16ƒƒkarel.move();
1 3 east parcel 1 3
17ƒƒkarel.turnLeft();
1 3 north parcel 1 3
18ƒƒkarel.turnLeft();
1 3 west parcel 1 3
19ƒƒkarel.turnLeft();
1 3 south parcel 1 3
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 22
22
OBJECTS
karel parcel
(table 1-2) continued
WITH
Program Statement street avenue direction backpack street avenue A table recording the
change of program
CHAPTER 1 | PROGRAMMING
20ƒƒkarel.move();
state while tracing the
2 3 south parcel 2 3 execution of the
DeliverParcel
21ƒƒkarel.putThing();
program in Listing 1-1
2 3 south 2 3
22ƒƒkarel.move();
3 3 south 2 3
Like the state change diagram, the table is organized to show the state of the program
both before and after each statement is executed. It does this by inserting the state-
ments between the rows that record the program’s state.
The first row of the table gives the initial state as established when the objects are con-
structed in lines 8–10. This state reflects the initial situation shown in Figure 1-10. As
we trace the program, we list the statement executed and then the resulting state. The
effect is equivalent to a long series of state change diagrams like the one in Figure 1-12,
but considerably easier to manage.
After tracing the program, we see that the robot finishes on intersection (3, 3) as the
final situation requires. In addition, it has picked up the thing and deposited it on inter-
section (2, 3).
Tracing the program helps us understand what it does and increases our confidence in KEY IDEA
the correctness of the solution. As you trace a program, you must do exactly what the Computers follow the
program says. It is tempting to take shortcuts, updating the table with what we intend program exactly.
the program to do. The computer, however, does not understand our intentions. It does When tracing the
execution, we must
exactly what the program says. If we don’t do the same while tracing, the value of trac- also be exact.
ing is lost and we can no longer claim confidence in the correctness of the solution.
Having performed a trace, we now understand that the sequence of statements in the
program is important. If lines 14 and 15 are reversed, for instance, the robot would try
to pick up the thing before it arrives at the thing’s intersection. The result would be a
broken robot on intersection (1, 1).
23
One obvious difference between this program and DeliverParcel is the use of Wall
objects. A wall is instantiated using the same pattern as Robot and City objects, as
shown in the following statement:
WallƒblockAve0ƒ=ƒnewƒWall(ny,ƒ0,ƒ2,ƒDirection.WEST);
This statement constructs a wall on the western boundary of the intersection at (0, 2).
This is the wall that prevents mark from proceeding west from its current location. The
object is referenced with the name “blockAve0,” a name chosen by the programmer.
(figure 1-13) 0 1 2 0 1 2
0 0
Initial and final
situations for two robots, 1 1
mark and ann
2 2
In the initial situation, two robots, The final situation, where mark and ann
mark and ann, are on opposite sides of have gone around the roadblock to meet
a roadblock. They would like to meet on intersection (2, 1).
on intersection (2, 1).
You may want to stop here and think about how you would write a program to solve
this problem. What changes would you make to the DeliverParcel program? Then
look at Listing 1-2 and see how much of it makes sense to you.
24
OBJECTS
Listing 1-2:
WITH
A program where mark goes around a road block and meets ann
ch01/roadblock/
CHAPTER 1 | PROGRAMMING
1 importƒbecker.robots.*;
2
3 publicƒclassƒGoAroundRoadBlockƒ
4 {
5 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
6 ƒƒ{
7 ƒƒƒƒ// Set up the initial situation
8 ƒƒƒƒCityƒƒnyƒƒƒƒƒƒƒƒ=ƒnewƒCity();
9 ƒƒƒƒWallƒƒblockAve0ƒ=ƒnewƒWall(ny,ƒ0,ƒ2,ƒDirection.WEST);
10 ƒƒƒƒWallƒƒblockAve1ƒ=ƒnewƒWall(ny,ƒ1,ƒ2,ƒDirection.WEST);
Object Instantiation
11 ƒƒƒƒRobotƒmarkƒƒƒƒƒƒ=ƒnewƒRobot(ny,ƒ0,ƒ2,ƒDirection.WEST);
12 ƒƒƒƒRobotƒannƒƒƒƒƒƒƒ=ƒnewƒRobot(ny,ƒ0,ƒ1,ƒDirection.EAST);
13
14 ƒƒƒƒ// mark goes around the roadblock
15 ƒƒƒƒmark.turnLeft();
16 ƒƒƒƒmark.move();
17 ƒƒƒƒmark.move();
Command Invocation
18 ƒƒƒƒmark.turnLeft();ƒƒƒƒƒƒ// start turning right as three turns left Sequential Execution
19 ƒƒƒƒmark.turnLeft();
20 ƒƒƒƒmark.turnLeft();ƒƒƒƒƒƒ// finished turning right
21 ƒƒƒƒmark.move();
22
23 ƒƒƒƒ// ann goes to meet mark
24 ƒƒƒƒann.turnLeft();ƒƒƒƒƒƒƒ// start turning right as three turns left
25 ƒƒƒƒann.turnLeft();
26 ƒƒƒƒann.turnLeft();ƒƒƒƒƒƒƒ// finished turning right
27 ƒƒƒƒann.move();
28 ƒƒƒƒann.move();
29 ƒƒƒƒann.turnLeft();
30 ƒƒ}
31 }
Multiple Objects
The GoAroundRoadBlock program contains two robots, mark and ann. Each has its
own internal state, which is completely independent of the other. mark can turn and
move without affecting ann; ann can turn and move without affecting mark. The state
change diagram in Figure 1-14 shows an object for mark and another for ann. After
mark turns, ann’s object has not changed (although mark’s direction is now different).
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 25
25
mark.turnLeft();
Robot Robot
street: 0 street: 0
avenue: 1 avenue: 2
direction: EAST direction: SOUTH
backpack: backpack:
KEY IDEA In a well-designed object-oriented program, an object’s state does not change unless a
The only way to message has been sent to the object. When mark is sent the turnLeft message, no mes-
change an object’s sage is sent to ann, so that object doesn’t change. This property of not changing unless an
attributes should be explicit message is received is called encapsulation. An object builds a capsule or wall
via its services.
around its attributes so that only the object’s services can change them. One strength of
object-oriented programming is that it makes encapsulation easy. Earlier programming
methodologies did not make encapsulation as easy, with the result that programmers
were often left wondering “Now, how did that information get changed?”
It is possible to write Java programs that do not use encapsulation, but it is not
recommended.
Let’s turn now to the first six lines and the last two lines of the DeliverParcel and
GoAroundRoadBlock programs. Both programs are almost identical in these areas;
we take Listing 1-1 as our example. For programs involving robots, only one of these
eight lines vary from program to program. The statements we are interested in are
shown in Listing 1-3, with the part that changes shown in a box.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 26
26
OBJECTS
Listing 1-3:
WITH
Line 1 makes a large body of code written by other people easily available. The 24 lines
of code contained in Listing 1-1 are not nearly enough to specify everything that this
program does. The program makes use of more than 3,700 lines of code written by the
textbook’s author. That, in turn, uses many tens or even hundreds of thousands of lines
of code written by still other programmers.
All that code is organized into packages. The code written by the author is in the pack- LOOKING AHEAD
age becker.robots. It is a group of about 50 classes that work together to enable In Section 1.6, we will
robot programs. Line 1 makes those classes more easily accessible within the see programs that
DeliverParcel program. import packages for
manipulating
The class name on line 3 is simply that—a name by which this class will be known. The a window on
the screen.
name of the file containing the classes’ source code must be the same as this name, with
“.java” added to the end (for example, “DeliverParcel.java”).
The braces on lines 4 and 24 enclose the statements that are specific to this class.
The special name main appears in every Java program. It marks where execution of the
program begins. The words surrounding main are required and tell Java more about this
code. The braces in lines 6 and 23 enclose all of the statements associated with main.
The Robot class includes more than 30 services—too many to discuss here.
Furthermore, the software accompanying this book contains more than 100 other
classes. How can you find out about the other classes and all the services they (includ-
ing Robot) offer?
The creators of Java have included a facility to extract information from the Java
source code and put it in a form suitable for the World Wide Web. A sample Web page
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 27
27
(figure 1-15)
The upper-left panel in the window shows an area labeled “Packages”. It contains
becker.robots and several other packages. Clicking one of these links displays in the
lower-left panel a list of the classes contained in that package. For example, if you click
becker.robots, the lower-left panel displays the classes contained in the
becker.robots package. The classes used in our programs thus far (City, Robot, Wall,
and Thing) are listed here.
Clicking one of the class names displays documentation for that class in the main part
of the window. For example, if you click the Robot class, its documentation appears,
the beginning of which is shown in Figure 1-15. It shows the relationship between the
Robot class and a number of other classes. We’ll learn more about these relationships
in Chapter 2.
Figure 1-16 shows the documentation’s descriptive overview of the Robot class.
The overview is used to describe the purpose of the class and sometimes includes
sample code.
28
OBJECTS
(figure 1-16)
WITH
Descriptive overview of
the Robot class
CHAPTER 1 | PROGRAMMING
(figure 1-17)
Summary descriptions of
Robot constructors; each
method has a similar
summary
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 29
29
Errors are often revealed by one of the last two steps. You must then go back to the
first step, make changes, and try again. These steps result in a cycle, illustrated in
Figure 1-19, of editing, compiling, and running, which is repeated until the program is
finished. When things go right, the light path is followed, providing the desired results.
When things go wrong, one of the three dark paths is taken and the source code must
be edited again to fix the error.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 30
30
OBJECTS
(figure 1-19)
Edit program's
WITH
Intent Desired
errors results
Compile
the program
Run-
time
errors Run the
program
Compile-
time
errors
What kinds of things can go wrong? There are three kinds of errors: compile-time errors,
run-time errors, and intent errors. Compile-time errors are exposed when the compiler
translates the program into byte code. Run-time errors are discovered when the program
runs, but attempts to do something illegal. Intent errors are also called logic errors. They
occur when the program does not carry out the programmer’s intentions.
Compile-time errors occur when the program does not conform to the rules of the Java
language. People can easily recognize (or even overlook) errors in written English.
Computers cannot. The programmer must obey the rules exactly.
Listing 1-4 contains a program with many compile-time errors. Each is explained by
the boxed annotation beside it. You can find the code in ch01/compileErrors/.
Every time the compiler finds an error it prints an error message. These messages are KEY IDEA
helpful in finding the errors—be sure to read them carefully and use them. For exam- The compiler can
ple, misplaced semicolons can be a struggle for beginning programmers. The missing help you find
compile-time errors.
semicolon at line 8 results in an error message similar to the following:
CompileErrors.java:8:ƒ';'ƒexpected
ƒƒƒRobotƒkarelƒ=ƒnewƒRobot(london,ƒ1,ƒ1,ƒDirection.EAST)
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ^
This message has three parts. The first is where the error was found.
“CompileErrors.java” is the name of the file containing the error. It won’t be long
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 31
31
Third is the line from the program where the compiler found the error. Beneath it is a
caret (^) symbol showing where in the line the error was detected.
If you use an integrated development environment (IDE), it may show errors in a different
format. It may even move the cursor to the line containing the error. No matter what the
format is, however, you should still be able to learn the nature and location of the error.
Sometimes the messages are more cryptic than the one shown in the CompileErrors
example, and occasionally the location of the error is wrong. For example, the com-
piler reports many errors if you misspell the variable name at line 8 where it is
declared, but spell it correctly everywhere else. Unfortunately, none of the errors are at
line 8. In other words, one error can cause many error messages, all pointing to the
wrong location.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 32
32
OBJECTS
Because one error can cause many error messages, a reasonable debugging strategy is
WITH
➤ Fix the most obvious errors, beginning with the first error reported.
➤ Compile the program again to obtain a revised list of the remaining errors.
Furthermore, do these tasks early in your program’s development, and do them often. KEY IDEA
Waiting too long to compile your program will often result in many cryptic error mes- Compiling early and
sages that are hard to understand. Errors are easier to find and fix when you compile often makes finding
early and often. compile-time errors
easier.
Run-time errors are discovered when the program is actually run or traced. They are
the result of instructions executing in an illegal context. For example, the instruction
karel.move(); will compile correctly (as long as a robot named karel has been
constructed). However, if karel is facing a wall when this instruction executes, it will
break. The instruction is executed in an illegal context.
The error of crashing a robot into a wall is reported in two different ways. First, the
robot’s icon is changed to show that the robot is broken. Second, an informative error
message is printed. An example is shown in Figure 1-20.
This error message results from removing line 17 from Listing 1-2. The result is that
mark does not move far enough to go around the roadblock and crashes into it.
The first line of the message contains technical information until the colon (:) charac-
ter. A description of what went wrong usually appears after the colon. In this case, we
are told that a robot crashed while moving west.
Lines 2-4 say where in the source code the error occurred and how the program came to
be executing that code. Line 2 says the error happened while the program was executing
line 558 in the source file Robot.java. That might be helpful information if we had
access to Robot.java, but we don’t. It is part of the becker.robots package imported
into our robot programs. Line 3 indicates the error is also related, somehow, to line 148 in
that same source file. Finally, line 4 mentions GoAroundRoadBlock.java, the file shown
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 33
33
One situation that can be confusing for beginning programmers is when the Java sys-
tem cannot even begin running your program. Usually, the run-time error message will
contain the “word” NoClassDefFoundError. This means that the Java system can-
not find your program to run, perhaps because it has not been successfully compiled or
perhaps because the compiler did not place the compiled version of your program in
the expected place.
An intent error occurs when the program fails to carry out its intended purpose. The
program may not have any compile-time or run-time errors, yet still fail to accomplish
the job for which it was written. An intent error is also called a logic error.
These errors can be among the hardest to find. The computer can help find compile-
time and run-time errors because it knows something is wrong. With an intent error,
however, the computer cannot tell that something is wrong, and therefore provides no
help other than executing the program. Remember, a computer does what it is told to
do, which might be different from what it is meant to do.
For example, consider a program intended to move a thing from (2, 1) to (1, 1). The
initial and final situations are shown in Figure 1-21.
(figure 1-21) 0 1 2 0 1 2
0 0
Initial and final situations
for a task to move a thing 1 1
2 2
Suppose the programmer omitted the turnLeft instruction, as in the following pro-
gram fragment:
Sequential Execution
katrina.move();
katrina.pickThing();
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// should have turned left here
katrina.move();
katrina.putThing();
katrina.move();
As a result, the robot would finish the task at (2, 3) instead of (0, 1), and the thing
would be at (2, 2) instead of (1, 1)—not what was intended.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 34
34
OBJECTS
Fortunately, the visual nature of robot programs makes many intent errors easy to find.
WITH
Programming errors of any kind are often called bugs. The process of finding and fix-
ing the errors is called debugging. The origin of the term is not certain, but it is known
that Thomas Edison talked about bugs in electrical circuits in the 1870s. In 1947, an
actual bug (a moth) was found in one of the electrical circuits of the Mark II computer
at Harvard University, causing errors in its computations (see Figure 1-22). When the
moth was found, it was taped into the operator’s log book with the notation that it is
the “first actual case of a bug being found.” Apparently, the term was already in use
for non-insect causes of computer malfunctions.
(figure 1-22)
35
These concepts are not only for robot programs, but apply to every object-oriented
Java program ever written. To illustrate, each chapter of this book includes a section
applying the concepts learned using robots to graphical user interfaces, or GUIs (pro-
nounced “gooey”). Applying the concepts to classes supplied with the Java language
that have nothing to do with robots shows you how these concepts can be used in
many other contexts.
Graphical user interfaces are the part of the program that interacts with the human user.
It probably obtains input from the user and displays results to the user. In terms of
what the user sees, the GUI consists of the windows, dialog boxes, lists of items to
select, and so on.
GUIs add a lot of complexity and development time to a program. Fortunately, Java
provides a rich set of resources to help develop interfaces. The beginning point is the
window, called a frame in Java. The simplest possible frame is shown in Figure 1-23.
(figure 1-23)
Maximize button
Minimize button
Though it is empty, the frame nevertheless has substantial functionality. If the user
clicks the close box, the program quits. If the user clicks the minimize box the frame
becomes as small as possible, while clicking the maximize box enlarges the frame to
take up the entire screen. The user may also adjust the size of the frame by clicking and
dragging an edge of the frame.
The program that displays this frame is even simpler than a robot program and is shown
in Listing 1-5. Notice the similarity to Listing 1-1, including the following features:
➤ Both programs include import statements (although the actual packages dif-
fer), the declaration of the class at line 3 (although the class name differs), the
placement of braces, and the declaration of the special service main.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 36
36
OBJECTS
The EmptyFrame program differs from the DeliverParcel program in the kinds of
objects created and the services demanded of them. A JFrame object serves as a container
for information displayed to the user. By default, however, the frame has no title, is not
visible on the screen, has no area for displaying information, and hides the frame when the
close box is clicked (leaving us with no good way to stop the program). The services
invoked in lines 10-14 override those defaults to provide the functionality we need.
The setTitle service causes its argument to be displayed at the top of the frame.
The setLocation service says where the frame should appear. Its first argument is the
distance from the left side of the screen to the left side of the frame. The second argu-
ment specifies the distance from the top of the screen to the top of the frame. The
setSize service specifies the size of the frame. The first argument is the width and the
second argument is the height. All of the arguments to these two services are given in
pixels, an abbreviation for picture elements, which are the tiny dots on the screen that
make up the image. The meaning of these arguments is illustrated in Figure 1-24.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 37
37
150
LOOKING AHEAD Finally, the setVisible service specifies whether the frame is visible on the screen. If
true and false are the argument is true, the frame will be visible, while a value of false will hide it.
Boolean values used
to represent true or
false answers, and 1.6.2 Adding User Interface Components
are covered in detail
in Chapter 4. A frame with nothing in it is rather boring and useless. We can fix that by setting the
content pane, the part of a frame designed to display information. We can add to the
content pane buttons, textboxes, labels, and other user interface elements familiar to
modern computer users. These buttons, labels, and so on are often called components.
A component is nothing more than an object designed to be part of a graphical user
interface.
An early warning, however: The resulting programs may look like they do something
useful, but they won’t. We are still a long way from writing a graphical user interface
that actually accepts input from the user. The following programs emphasize the
graphical rather than the interface.
Figure 1-25 shows a snapshot of a running program that has a button and a text area
displayed in a frame. The frame’s content pane has been set to hold a JPanel object.
The JPanel, in turn, holds the button and text area. Listing 1-6 shows the source code
for the program.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 38
38
OBJECTS
(figure 1-25)
WITH
Listing 1-6 FramePlay, a program to display a frame containing a button and a text area
ch01/framePlay/
1 importƒjavax.swing.*;ƒƒƒƒƒ// use JFrame, JPanel, JButton, JTextArea
2
3 publicƒclassƒFramePlayƒ
4 {
5 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
6 ƒƒ{ƒ// declare the objects to show
7 ƒƒƒƒJFrameƒframeƒ=ƒnewƒJFrame();
8 ƒƒƒƒJPanelƒcontentsƒ=ƒnewƒJPanel();
9 ƒƒƒƒJButtonƒsaveButtonƒ=ƒnewƒJButton("Save");
10 ƒƒƒƒJTextAreaƒtextDisplayƒ=ƒnewƒJTextArea(5,ƒ10);
11
12 ƒƒƒƒ// set up the contents
Display a Frame
13 ƒƒƒƒcontents.add(saveButton);
14 ƒƒƒƒcontents.add(textDisplay);
15
16 ƒƒƒƒ// set the frame’s contents to display the panel
17 ƒƒƒƒframe.setContentPane(contents);
18
19 ƒƒƒƒ// set up and show the frame
20 ƒƒƒƒframe.setTitle("FramePlay");
21 ƒƒƒƒframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
22 ƒƒƒƒframe.setLocation(250,ƒ100);
23 ƒƒƒƒframe.setSize(150,ƒ200);
24 ƒƒƒƒframe.setVisible(true);
25 ƒƒ}
26 }
In lines 7–10, we declare and instantiate several objects. The JPanel instance named
contents is simply a container. It will hold the things we are really interested in, the
button and text area. The button and text area are instances of JButton and
JTextArea, respectively. The JPanel’s add service in lines 13 and 14 adds them to its
list of things to display.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 39
39
1.7 PATTERNS
When the button is constructed in line 9, “Save” is passed to the constructor’s para-
meter. This text appears on the button when it is displayed. When the text area is con-
structed in line 10, the number of lines of text it should hold and how many characters
wide it should be are passed as arguments. Both measures are approximate.
You can learn much more about these classes by browsing the online documentation at
java.sun.com/j2se/1.5.0/docs/api/.
If you run the FramePlay program, you will find that the objects used have quite a bit
of built-in functionality. The frame responds to the close, minimize, and maximize
boxes, and resizes when you drag an edge. The save button flashes when clicked, and
you can type in the textbox. This amount of functionality is remarkable for a 26-line
program. As with the robot programs, much of this functionality is due to the pro-
grammers who wrote the classes we are using.
KEY IDEA Again, notice the similarity between the FramePlay program and all the other programs
The ideas you learn in this chapter. The concepts we learned with the robot programs truly are general and
with robots—such as can be used in all Java programs. In fact, many of the ideas apply to all programs,
classes, objects, and
whether or not they are written in Java. Some ideas, such as modeling, abstraction, and
services—apply to all
object-oriented patterns apply to lots of different problems, whether or not they have a computer solu-
programs. tion. You are learning a portable set of skills that can be applied in many circumstances.
1.7 Patterns
Many patterns appear in software—problems that appear repeatedly that have the same
solution. A number of these have already been identified for you in the margins of this
text. Figure 1-26 shows the icon used to identify a pattern. Expert software developers
know many patterns and apply an appropriate one to solve the issue at hand, almost
without thinking about it. Much of your work, as you learn to program, will involve
learning to recognize which software patterns apply to the issue you are facing.
(figure 1-26)
Software patterns began as a way to capture and discuss big ideas, such as how to struc-
ture many classes and objects to model a particular kind of problem. In this book we
extend the idea of patterns to smaller ideas that may cover only one or two lines of code.
As beginning programmers, our attention will be focused primarily on these elementary
patterns.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 40
40
OBJECTS
Our elementary patterns have five elements: name, context, solution, consequences,
WITH
and related patterns. In the pattern expositions they are clearly shown by name and
typography, as shown in Figure 1-27. However, instead of describing an actual pattern,
CHAPTER 1 | PROGRAMMING
(figure 1-27)
Name: The name gives programmers a common vocabulary with which to
discuss their work. Soon you will be able to say to a classmate, “I think the Parts and format of a
Command Invocation pattern applies here,” and your classmate will know the pattern description
kind of issue you think needs solving, your proposed solution, and the
consequences of implementing that solution. Naming concepts increases our
ability to work with abstractions and communicate them to others. LOOKING AHEAD
Naming is a powerful
Context: The context describes the situations in which the pattern is applicable. idea. Here we are
Obviously, the pattern’s context must match the context of your programming naming patterns. In
issue for the pattern to be useful.
the next chapter, we
Solution: The solution describes an approach to resolving the programming will name a sequence
issue. For many patterns, the most appropriate form for the solution is several of instructions. Doing
lines of code, likely with well-defined places where the code must be customized so increases our
for the particular issue at hand. The places to customize appear in italics between power to think about
« and ». For other patterns, an appropriate form for the solution is a class diagram complex problems.
that shows how two or more classes work together to resolve the issue.
Related Patterns: Finally, related patterns name patterns that have a different
approach to resolving the issue or are based on the same ideas.
Patterns are found or discovered, not invented. They result from sensing that you are
repeating something you did earlier, investigating it enough to find the common ele-
ments, and then documenting it for yourself and others. Because patterns arise from
experience, they are a good way to help inexperienced programmers gain expertise
from experienced programmers.
The patterns listed here should feel familiar. You have seen them a number of times
already, often accompanied with a margin note like the one shown here. Once the pat-
tern has been formally presented, however, we will no longer call attention to its appli- Command Invocation
cation with margin notes.
41
1.7 PATTERNS
Solution: Implement a class that contains a service named main. In particular,
customize the following template:
importƒ«importedPackage»;ƒƒƒƒƒƒƒƒ// may have 0 or more import statements
publicƒclassƒ«programClassName»ƒ
{ƒ
ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
ƒƒ{ƒ«listƒofƒstatementsƒtoƒbeƒexecuted»
ƒƒ}
}
The import statement makes classes from the named package easier to access. Typical
values for «importedPackage» include becker.robots.* for robot programs, and
java.awt.* and javax.swing.* for programs with graphical user interfaces.
The «className» is chosen by the programmer and must match the filename con-
taining the source code.
Consequences: A class is defined that can be used to begin execution of the program.
Related Patterns: All of the other patterns in this chapter occur within the context of
the Java Program pattern.
Solution: Instantiate the object using the new keyword and a constructor from the
appropriate class. Provide arguments for all of the constructor’s parameters. Finally,
assign the object reference provided by new to a variable. Examples:
Cityƒmanilaƒ=ƒnewƒCity();
Robotƒkarelƒ=ƒnewƒRobot(manila,ƒ5,ƒ3,ƒDirection.EAST);
JButtonƒsaveButtonƒ=ƒnewƒJButton("save");
In general, a new object is instantiated with a statement matching the following template:
«variableType»ƒ«variableName»ƒ=ƒ
ƒƒƒƒƒƒƒƒƒƒƒnewƒ«className»(«argumentList»);
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 42
42
OBJECTS
The «variableName» is used in the Command Invocation pattern whenever services LOOKING AHEAD
WITH
Until we have studied polymorphism in Chapter 12, «variableType» and choosing variable
«className» will be the same. After that, they will often be different, but related. The names wisely in
«className», of course, determines what kind of object is constructed. The Section 2.4.2.
«objectReference».«commandName»(«argumentList»);
Related Patterns:
➤ The Object Instantiation pattern must always precede this pattern to create the
object.
➤ The Sequential Execution pattern uses this pattern two or more times.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 43
43
1.7 PATTERNS
1.7.4 The Sequential Execution Pattern
LOOKING AHEAD Solution: List the steps to be executed in a correct order. A correct order is one where
Problem 1.4 further each statement appears after all the statements upon which it depends. Each statement
explores the idea of is terminated with a semicolon. An example from the DeliverParcel program (see
“correct order.”
Listing 1-1) demonstrates the solution:
7 ƒ// Set up the initial situation
8 ƒƒƒƒCityƒpragueƒ=ƒnewƒCity();
9 ƒƒƒƒThingƒparcel=ƒnewƒThing(prague,ƒ1,ƒ2);
10 ƒƒƒƒRobotƒkarelƒ=ƒnewƒRobot(prague,ƒ1,ƒ0,ƒDirection.EAST);
11
12 // Direct the robot to the final situation
13 ƒƒƒƒkarel.move();
14 ƒƒƒƒkarel.move();
15 ƒƒƒƒkarel.pickThing();
Lines 9 and 10 require a city when they are constructed; they therefore must appear
after line 8, where the city is constructed. However, lines 9 and 10 are independent of
each other and can appear in either order.
The robot karel cannot pick up a Thing at (2, 1) until it has reached that intersec-
tion. Lines 13 and 14 must therefore come before the pickThing command in line 15
to establish the context for it to execute (that is, move the robot to the intersection con-
taining the Thing it picks up).
LOOKING AHEAD Consequences: Each statement, except the first one, may depend on statements that
Long sequences of come before it. If any of those statements are out of order or wrong, an error or
statements are hard
unexpected result may appear later in the program, which can make programming a
to understand. In
Chapter 2 we will tedious process.
learn methods to
help keep such This model of execution assumes that each statement executes completely before the
sequences short. next one has begun. It is as if each statement were strung on a thread. Follow the
thread from the beginning and each statement is reached in order. Follow the thread
back through time and you have a complete history of the statements executed prior to
the current statement.
Related Patterns: The Command Invocation pattern is used two or more times by
this pattern.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 44
44
OBJECTS
publicƒclassƒ«programClassName»ƒ
{
ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
ƒƒ{ƒ// declare the objects to show
ƒƒƒƒJFrameƒ«frame»ƒ=ƒnewƒJFrame();
ƒƒƒƒJPanelƒ«contents»ƒ=ƒnewƒJPanel();
ƒƒƒƒ«statementsƒtoƒdeclareƒandƒaddƒcomponentsƒtoƒcontents»
Consequences: The frame is displayed along with the contents of the JPanel. The
JFrame’s functionality is automatically available, including resizing, minimizing, and
closing the frame.
Related Patterns:
➤ This pattern is a specialized version of the Java Program pattern.
➤ The Model-View-Controller pattern (Chapter 13) builds further on this pattern.
45
Each chapter of this textbook includes a concept map as part of the summary. A con-
cept map shows the major concepts in the chapter and relates them to each other with
short phrases. The short phrases should be read in the direction of the arrow. For
example, the following portion of the diagram should be read as “objects are instances
of classes.”
By studying the concept map, you remind yourself of important vocabulary (such as
class, object, and instance in the preceding example) and the relationships between
concepts.
Three suggested ways to use the concept maps as study tools are:
➤ After reading the chapter but before you look at the concept map, try drawing
your own concept map and then compare it with the one in the chapter. They
will undoubtedly be different, but hopefully will include many of the same
concepts. The differences will identify places for you to clarify your under-
standing.
➤ Try reproducing the concept map but with the arrows running backwards.
You will need to adjust the connecting phrase accordingly. Consider the fol-
lowing example:
➤ The concept maps include the most important concepts, but not all of them.
Try to integrate additional concepts into the map. For example, how could
you include the concepts of constructors, initial situations, and final situa-
tions in the following concept map?
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 46
46
OBJECTS
he
in t
WITH
es model
tri
en
to ntsa
CHAPTER 1 | PROGRAMMING
d ab
me st
n
le ra
po
imp c
res ts
a program a
cor
Robot and
City
ing problem to
m us
be solved
are ex
is
co
roble
mp
am
os
sap
ed
ples o
of
move,
solve
turnLeft
f
objects are instances of classes
are e
hav define
e th
eir
xamp
fro own
m val
the ues
def
l
es of
sam for
ine
ec
las
s sha attributes
arguments re a
com
mon
pro set
vid of
ev
alu
es services
to convey
parameters information to
Problem sets present three types of problems: written exercises, programming exer-
cises, and programming projects. Written exercises do not require programming but
may require a computer to read documentation. Programming exercises and projects
both require programming, but to a different degree. Exercises are short, such as
changes to existing code. Programming projects require considerably more effort.
Written Exercises
1.1 Figure 1-4 shows a class diagram for the Concert class. Create a similar class
diagram for a Book class. It’s part of a program at the local public library that
loans books to library patrons.
1.2 Trace the following program. In a table similar to Table 1-2, record karel’s
current street, avenue, direction, and contents of its backpack. If a run-time
error occurs, describe what went wrong and at which line. There are no
compile-time errors.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 47
47
48
OBJECTS
10
WITH
11 ƒƒƒƒkarel.move();
12 ƒƒƒƒkarel.move();ƒmove();
CHAPTER 1 | PROGRAMMING
13 ƒƒƒƒkarel.pickthing();
14 ƒƒƒƒkarel..turnLeft();ƒkarel.turnLeft();
15 ƒƒƒƒkerel.mve();
16 ƒƒƒƒkarel.turnLeft(3);
17 ƒƒƒƒkarel.move;();
18 ƒƒƒƒkarel.putThing();
19 ƒƒ}
20 }
1.7 The JButtonƒconstructor used in the FramePlay program in Listing 1-6 uses
a string as a parameter. JFrame has a constructor that also has a string as a
parameter. What is the effect of replacing line 7 with JFrameƒframeƒ=ƒnewƒ
JFrame(“Mystery”);? Which line of the existing program should also
change? How? (Hint: Consult the online documentation.)
Programming Exercises
1.8 Beginning with the program in Listing 1-1, deliberately introduce one compile-
time error at a time. Record the compiler error generated and the cause of the
error. If one error causes multiple messages, list only the first two. Find at least
six distinct compile-time errors.
1.9 Write a program that creates a robot at (1, 1) that moves north five times,
turns around, and returns to its starting point.
a. Run the program and describe what happens to the robot.
b. Describe a way to use the controls of the running program so you can watch
the robot the entire time it is moving.
c. Consult the online documentation. Describe a way to change the program
so you can watch the robot the entire time it is moving.
1.10 Make a copy of the FramePlay program in Listing 1-6. Add a new
JCheckBox component.
a. Run the program and describe the behavior of the new component.
b. The JCheckBox constructor can take a string as a parameter, just like
JButton. What happens if you use the string “Show All Items”?
1.11 Run the program in Listing 1-2. In your Java development software, choose
Save from the File menu, enter a filename, and click the Save button. Find the
file that was created.
a. Describe the contents of the file.
b. Search the documentation for the City class to find a use for this file.
Describe the use.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 49
49
Programming Projects
1.12 Write a program that begins with the initial situation shown in Figure 1-28.
Instruct the robot to go around the walls counter clockwise and return to its
starting position. Hint: Setting up the initial situation requires eight walls.
(figure 1-28) 0 1 2 3
0
Walking around the walls
1
1.13 Every morning karel is awakened when the newspaper, represented by a Thing,
is thrown on the front porch of its house. Instruct karel to retrieve the newspa-
per and return to “bed.” The initial situation is as shown in Figure 1-29; in the
final situation, karel is on its original intersection, facing its original direction,
with the newspaper.
(figure 1-29) 0 1 2 3
0
Fetching the newspaper
1
1.14 The wall sections shown in Figure 1-30 represent a mountain (north is up).
Write a program that constructs the situation, and then instructs the robot to
pick up a flag (a Thing), climb the mountain, and plant the flag at the top,
descending down the other side. The robot must follow the face of the moun-
tain as closely as possible, as shown by the path shown in the final situation.
1 Chapter C5743 40143.ps 11/30/06 1:14 PM Page 50
50
OBJECTS
0 1 2 3 4 5 0 1 2 3 4 5 (figure 1-30)
0 0
WITH
1 1
mountain
2 2
3 3
1.15 On the way home from the supermarket, karel’s bag rips slightly at the bot-
tom, spilling a few expensive items (Things) on the ground. Fortunately,
karel’s neighbor maria notices and calls to him as karel arrives home. This
initial situation is shown on the left in Figure 1-31. Write a program in which
karel and maria both begin picking up the items, meeting as shown in the
final situation. Use the setLabel service in Robot to label each robot.
0 1 2 3 0 1 2 3 (figure 1-31)
0 M 0
Initial and final situations
1 1 M K for robots picking up
dropped items
2 2
3 K 3
1.16 Write a program that begins with the initial situation and ends with the final sit-
uation shown in Figure 1-32. In the final situation, the robot originally at (0, 0)
is facing east and the robot originally at (0, 1) is facing west. Alternate the
actions of the two robots so they arrive at their destination at approximately the
same time.
0 1 2 0 1 2 (figure 1-32)
0 0
Initial and final situations
1 1 for two robots moving and
meeting each other
2 2
51
Chapter Objectives
After studying this chapter, you should be able to:
➤ Extend an existing class with new commands
➤ Explain how a message sent to an object is resolved to a particular method
➤ Use inherited services in an extended class
➤ Override services in the superclass to provide different functionality
➤ Follow important stylistic conventions for Java programs
➤ Extend a graphical user interface component to draw a scene
➤ Add new kinds of Things and Robots to the robot world
Sometimes an existing class already does almost all of what is needed—but not quite
all. For example, in the previous chapter, you may have found it unnatural to write
karel.turnLeft() three times just to make a robot turn right. By extending a class
we can add related services such as turnRight, allowing programmers to express
themselves using language that better fits the problem.
Other approaches to providing new functionality include modifying the class itself to
include the required functionality or writing a completely new class. These two
approaches will be considered in later chapters.
53
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 54
54
SERVICES
Let’s try an experiment. Find a watch or a clock that can measure time in seconds.
CHAPTER 2 | EXTENDING CLASSES
Measure the number of seconds it takes you to understand the program shown in
Listing 2-1. In particular, describe the path the robot takes, its final position, and its
direction.
Now, imagine that we had a new kind of robot with commands to turn around, turn
right, and move ahead three times. Time yourself again while you try to understand the
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 55
55
You probably found the second program easier—and faster—to read and understand.
The results shown in Table 2-1 are from a group of beginning Java programmers who
performed the same experiment.
KEY IDEA Why did we comprehend the second program more quickly? We raised the level of abstrac-
Adapt your language tion; the language we used (turnAround, turnRight, move3) matches our thinking more
to express your ideas closely than the first program. Essentially, we created a more natural programming lan-
clearly and concisely
guage for ourselves.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 56
56
SERVICES
Raising the level of abstraction with language that matches our thinking has a number
of benefits.
WITH
➤ Raising the level of abstraction makes it easier to write the program. It’s easier
CHAPTER 2 | EXTENDING CLASSES
for a programmer to think, “And then I want the robot to turn around” than
to think, “The robot should turn around so I need to tell it to turn left and
then turn left again.” We can think of a task such as turnAround, deferring
the definition of the task until later. Abstraction allows us to concentrate on
the big picture instead of getting stuck on low-level details.
➤ Raising the level of abstraction allows us to understand programs better. An
instruction such as turnAround allows the programmer to express her intent.
Knowing the intent, we can better understand how this part of the program
fits with the rest of the program. It’s easier to be told that the programmer
wants the robot to turn around than to infer it from two consecutive
turnLeft commands.
➤ When we know the intent, it is easier to debug the program. Figuring out LOOKING AHEAD
what went wrong when faced with a long sequence of service invocations is Quality software is
hard. When we know the intent, we can first ask if the programmer is intend- easier to understand,
ing to do the correct thing (validation), and then we can ask if the intent is write, debug, reuse,
and modify. We will
implemented correctly (verification). This task is much easier than facing the explore this further in
entire program at once and trying to infer the intent from individual service Chapter 11.
invocations.
➤ We will find that extending the language makes it easier to modify the pro-
gram. With the intent more clearly communicated, it is easier to find the places
in the program that need modification.
➤ We can create commands that may be useful in other parts of the program, or KEY IDEA
even in other programs. By reusing old services and creating new services that Work smarter by
will be easy to reuse in the future, we can save ourselves effort. We’re working reusing code.
smarter rather than harder, as the saying goes.
In the next section, we will learn how to extend an existing class such as Robot to add
new services such as turnAround. We’ll see that these ideas apply to all Java pro-
grams, not just those involving robots.
57
2.2 EXTENDING
(figure 2-1)
ExperimentRobot
Class diagram for the new int street
robot class, int avenue
Direction direction
THE ROBOT
ExperimentRobot
ThingBag backpack
ExperimentRobot(City aCity, int aStreet,
int anAvenue, Direction aDirection)
CLASS
void move( )
void turnLeft( )
void pickThing( )
void putThing( )
void turnAround( )
void turnRight( )
void move3( )
The class shown in Figure 2-1 is almost the same as Robot—but not quite. We would
like a way to augment the existing functionality in Robot rather than implementing it
again, similar to the camper shown in Figure 2-2.
(figure 2-2)
Van extended to be a
camper
This vehicle has a number of features for people who enjoy camping: a bed in the pop-
up top, a small sink and stove, a table, and so on. Did the camper’s manufacturer
design and build the entire vehicle just for the relatively few customers who want such
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 58
58
SERVICES
a vehicle for camping? No. The manufacturer started with a simple cargo van and then
added the special options for camping. The cargo van gave them the vehicle’s basic
WITH
frame, engine, transmission, driver’s seat, instrument panel, and so on. Using all this
CHAPTER 2 | EXTENDING CLASSES
infrastructure from an existing vehicle saved them a lot of work, so they could focus on
the unique aspects required by a camper. The same cargo van, by the way, is also
extended in different ways to carry more passengers.
Just as the camper manufacturer extended a van with additional features, we will extend KEY IDEA
Robot with additional services. Java actually uses the keyword extends for this purpose. Start with something
that does most of what
Figure 2-3 shows a class diagram in which ExperimentRobot extends Robot. Notice that you need. Then
the Robot class diagram is exactly the same as Figure 1-8. The ExperimentRobot class customize it for your
diagram shows only the attributes and services that are added to the Robot class. In the particular use.
(figure 2-3)
Robot
int street Class diagram showing
int avenue ExperimentRobot
Direction direction extending Robot
ThingBag backpack
Robot(City aCity, int aStreet,
int anAvenue, Direction aDirection)
void move( )
void turnLeft( )
void pickThing( )
void putThing( )
ExperimentRobot
When communicating about extending a class, we say that the Robot class is the superclass KEY IDEA
and the ExperimentRobot class is the subclass. Unless you’re familiar with the language The class that is
of mathematical sets or biology, this use of “sub” and “super” may seem backwards. In extended is called the
“superclass.” The new
these settings, “super” means a more inclusive set or category. For example, an
class is called the
ExperimentRobot is a special kind of Robot. We will also define other special kinds of “subclass.”
Robots. Robot is the more inclusive set, the superclass.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 59
59
2.2 EXTENDING
We might also say that ExperimentRobot inherits from Robot or that
ExperimentRobot extends Robot.
If you think of a superclass as the parent of a class, that child class can have a grandparent
THE ROBOT
and even a great-grandparent because the superclass may have its own superclass. It is,
therefore, appropriate to talk about a class’s superclasses (plural) even though it has only
one direct superclass.
CLASS
In a class diagram such as Figure 2-3, the superclass is generally shown above the sub-
class, and the arrow always points from the subclass to the superclass.
The import statement is the same here as in the Java Program pattern. Line 3 estab-
lishes the relationship between this class and its superclass using the keyword
Extended Class extends. For an ExperimentRobot, for example, this line would read as follows:
publicƒclassƒExperimentRobotƒextendsƒRobot
Lines 5, 6, and 7 of the code template are slots for attributes, constructors, and services.
In the next section, we will implement a constructor. In the following sections, we will
implement the services turnAround, turnRight, and move3. We will not be adding
attributes to our classes until Chapter 6. Until then, we will use only the attributes inher-
ited from the superclass.
The purpose of the constructor is to initialize each object that is constructed. That is, when
the statement Robotƒkarelƒ=ƒnewƒRobot(austin,ƒ1,ƒ1,ƒDirection.SOUTH) is
executed, the constructor for the Robot class ensures that the attributes street,ƒavenue,
direction, and backpack are all given appropriate values.
We are not adding any attributes to the ExperimentRobot. So what is there to initial-
ize? Is a constructor necessary? Yes. Because ExperimentRobot extends the Robot
class, each ExperimentRobot object can be visualized as having a Robot object inside
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 60
60
SERVICES
and backpack.
CHAPTER 2 | EXTENDING CLASSES
(figure 2-4)
ExperimentRobot
Visualizing an
Robot ExperimentRobot as
street: containing a Robot
avenue:
direction:
backpack:
Robot(City aCity, int aStreet, int anAvenue, Direction aDirection)
void move( )
void turnLeft( )
void pickThing( )
void putThing( )
The constructor for ExperimentRobot is only four lines long—three if all the para-
meters would fit on the same line:
1 publicƒExperimentRobot(CityƒaCity,ƒintƒaStreet,ƒ
2 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒintƒanAvenue,ƒDirectionƒaDirection)
3 {ƒsuper(aCity,ƒaStreet,ƒanAvenue,ƒaDirection);
4 }
Line 3 passes on the information received from the parameters to the Robot-inside- KEY IDEA
the-ExperimentRobot. Object initialization is performed by a constructor, so you The constructor must
would think that line 3 would call the constructor of the superclass: ensure the superclass
is properly initialized.
Robot(aCity,ƒaStreet,ƒanAvenue,ƒaDirection);ƒƒƒƒƒ// doesn’t work!
However, the designers of Java chose to use a keyword, super, instead of the name of LOOKING AHEAD
the superclass. When super is used as shown in line 3, Java looks for a constructor in Section 2.6 explains
the superclass with parameters that match the provided arguments, and calls it. The another use for the
keyword super.
effect is the same as you would expect from using Robot, as shown earlier.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 61
61
2.2 EXTENDING
LOOKING AHEAD When an ExperimentRobot is constructed with the following statement, the values
We will explore passed as arguments (austin, 3, 2, and Direction.SOUTH) are copied into the para-
parameters further in meters (aCity, aStreet, anAvenue, and aDirection) in the ExperimentRobot
Section 4.6 in the
THE ROBOT
constructor.
context of writing
services with ExperimentRobotƒlisaƒ=ƒnewƒExperimentRobot(austin,ƒ
parameters. ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ3,ƒ2,ƒDirection.SOUTH);
CLASS
Then, in line 3, those same values are passed as arguments to the parameters in the
superclass’s constructor.
Two other details about the constructor are that it must have the same name as the
class and it does not have a return type—not even void. If a constructor has a name
different from the class, the compiler considers it a service without a return type, and
issues a compile-time error. If a constructor has a return type, the compiler considers it
a service, and may not display an error until a client tries to use the constructor. Then
the compiler will complain that it can’t find it—because the constructor is being inter-
preted as a service.
Listing 2-3 contains the first steps in defining the ExperimentRobot class. It has a
number of the template slots filled in, including imported classes, the class name, and
the extended class’s name. It also includes a constructor, but none of the new services.
Just like the programs we wrote in Chapter 1, this class should be placed in its own file
named ExperimentRobot.java.
ch02/experiment/ Listing 2-3: The ExperimentRobot class with a constructor but no services
1 importƒbecker.robots.*;
2
3 publicƒclassƒExperimentRobotƒextendsƒRobot
4 {
5 ƒƒpublicƒExperimentRobot(CityƒaCity,ƒintƒaStreet,ƒ
6 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒintƒanAvenue,ƒDirectionƒaDirection)
7 ƒƒ{ƒsuper(aCity,ƒaStreet,ƒanAvenue,ƒaDirection);
8 ƒƒ}
9
10 ƒƒ// The new services offered by an ExperimentRobot will be inserted here.
11 }
With this modest beginning, we can write a program that includes the following statement:
ExperimentRobotƒlisaƒ=ƒnewƒExperimentRobot(austin,ƒ
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ3,ƒ2,ƒDirection.SOUTH);
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 62
62
SERVICES
The robot lisa can do all things any normal robot can do. lisa can move, turn KEY IDEA
left, pick things up, and put them down again. An ExperimentRobot is a kind of
WITH
Listing 2-3, is
Robot in line 7 of Listing 2-1 could be replaced with an ExperimentRobot. Even
complete and can be
with no other changes, the program would execute as it does with a Robot. used in a program. It
However, an ExperimentRobot cannot yet respond to the messages turnAround, just doesn’t add any
turnRight, or move3. new services.
A service is an idea such as “turn around.” To actually implement this idea, we add a KEY IDEA
method to the class, which contains code to carry out the idea. When we want a robot Services are ideas.
to turn around, we send a message to the robot naming the turnAround service. This Methods contain code
to implement the
message causes the code in the corresponding method to be executed.
idea. Services are
An analogy may help distinguish services, messages, and methods. Every child can eat. invoked with a
message. The object
This is a service provided by the child. It is something the child can do. A message from responds by
a parent, “Come and eat your supper” causes the child to perform the service of eating. executing a method.
The particular method the child uses to eat, however, depends on the instructions he or
she has received while growing up. The child may use chopsticks, a fork, or a fork and
a knife. The idea (eating) is the service. The message (“eat your supper”) causes the ser-
vice to be performed. How the service is performed (chopsticks, fork, and so on) is
determined by the instructions in the method.
publicƒvoidƒturnAround()
{ƒthis.turnLeft();
ƒƒthis.turnLeft();
}
Now the robot lisa can respond to the turnAround message. When a client says
lisa.turnAround(), the robot knows that turnAround is defined as turning left
twice, once for each turnLeft command in the body of turnAround.
Flow of Control
Recall the Sequential Execution pattern from Chapter 1. It says that each statement is
executed, one after another. Each statement finishes before the next one in the sequence
begins. When a program uses lisa.turnAround() we break out of the Sequential
Execution pattern. The flow of control, or the sequence in which statements are exe-
cuted, does not simply go to the next statement (yet). First it goes to the statements con-
tained in turnAround, as illustrated in Figure 2-5.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 63
63
2.2 EXTENDING
(figure 2.5) public void turnAround()
public ... main(...)
Flow of control when { ... { this.turnLeft();
executing methods lisa.turnAround(); this.turnLeft();
THE ROBOT
lisa.move(); }
... public void move()
} { ...
}
CLASS
When main sends the message lisa.turnAround(), Java finds the definition of
turnAround and executes each of the statements it contains. It then returns to the
statement following lisa.turnAround(). This is an example of a much more gen-
eral pattern that occurs each time a message is sent to an object:
KEY IDEA ➤ The method implementing the service named in the message is found.
Calling a method ➤ The statements contained in the method are executed. Unless told otherwise,
temporarily interrupts the statements are executed sequentially according to the Sequential Execution
the Sequential
Execution pattern.
pattern.
➤ Flow of control returns to the statement following the statement that sent the
message.
Look again at Figure 2-5. This same pattern is followed when lisa is sent the move
message, although we don’t know what the statements in the move method are, so they
are not shown in the figure. Similarly, when turnAround is executed, each turnLeft
message it sends follows the same pattern: Java finds the method implementing
turnLeft, executes the statements it contains, and then it returns, ready to execute
the next statement in turnAround.
When we are considering only main, the Sequential Execution pattern still holds.
When we look only at turnAround, the Sequential Execution pattern holds there, too.
But when we look at a method together with the methods it invokes, we see that the
Sequential Execution pattern does not hold. The flow of control jumps from one place
in the program to another—but always in an orderly and predictable manner.
One vital piece of information turnAround needs is which robot it should turn around.
When a client says lisa.turnAround(), the method must turn lisa around, and if a
client says karel.turnAround(), the method must turn karel around. Clearly the
method must know which object it is to act upon.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 64
64
SERVICES
This piece of information is needed so often and is so vital that the designers of Java
made accessing it extremely easy for programmers. Whenever a method is invoked
WITH
The implicit parameter is accessed within a method with the keyword this. The state-
ment this.turnLeft() means that the same robot that called turnAround will be
instructed to turn left. If the client said lisa.turnAround(), then lisa will be the
implicit parameter and this.turnLeft() will instruct lisa to turn left.
Sometimes when a person learns a new activity with many steps they will mutter
instructions to themselves: “First, I turn left. Then I turn left again.” Executing a
method definition is like that, except that “I” is replaced by “this robot.” You can
think of the ExperimentRobot as muttering instructions to itself: “First, this robot
turns left. Then this robot turns left again.”
The two remaining keywords in the method definition are public and void. The key-
word public says that this method is available for any client to use. In Section 3.6, we
will learn about situations for which we might want to prevent some clients from using
certain methods. In those situations, we will use a different keyword.
The keyword void distinguishes a command from a query. Its presence tells us that
turnAround does not return any information to the client.
Implementing the move3 method is similar to turnAround, except that we want the LOOKING AHEAD
robot to move forward three times. The complete method follows. Like turnAround, Eventually we will
it is placed inside the class, but outside of any constructor or method. learn how to write a
method with a
publicƒvoidƒmove3() parameter so
{ƒthis.move(); we can say
ƒƒthis.move(); lisa.move(50)—
ƒƒthis.move(); or any other
} distance.
As with turnAround, we want the same robot that is executing move3 to do the moving.
We therefore use the keyword this to specify which object receives the move messages.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 65
65
2.2 EXTENDING
2.2.6 Implementing turnRight
To tell a robot to turn right, we could say “turn left, turn left, turn left.” We could also
THE ROBOT
say “turn around, then turn left.” Both work. The first approach results in the follow-
ing method:
CLASS
publicƒvoidƒturnRight()
{ƒthis.turnLeft();
ƒƒthis.turnLeft();
Parameterless
ƒƒthis.turnLeft();
Command
}
KEY IDEA The second approach is more interesting, resulting in this method:
An object can send
messages to itself, publicƒvoidƒturnRight()
invoking its own {ƒthis.turnAround();
methods. ƒƒthis.turnLeft();
}
LOOKING AHEAD The second version works by asking the ExperimentRobot object to execute one of
Methods calling other its own methods, turnAround. The robot finds the definition of turnAround and
methods is a core executes it (that is, it turns left twice as the definition of turnAround says it should).
idea of Stepwise
When it has finished executing turnAround, it is told to turnLeft one more time.
Refinement, the topic
of Chapter 3. The robot has then turned left three times, as desired.
This discussion completes the ExperimentRobot class. The entire program is shown
in Listing 2-4.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 66
66
SERVICES
ch02/experiment/
CHAPTER 2 | EXTENDING CLASSES
1 importƒbecker.robots.*;
2
3 // A new kind of robot that can turn around, turn right, and move forward
4 // three intersections at a time.
5 // author: Byron Weber Becker
6 publicƒclassƒExperimentRobotƒextendsƒRobot
7 {
8 ƒƒ// Construct a new ExperimentRobot.
9 ƒƒpublicƒExperimentRobot(CityƒaCity,ƒintƒaStreet,ƒ
10 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒintƒanAvenue,ƒDirectionƒaDirection)
11 ƒƒ{ƒsuper(aCity,ƒaStreet,ƒanAvenue,ƒaDirection);
12 ƒƒ}
13
14 ƒƒ// Turn this robot around so it faces the opposite direction.
15 ƒƒpublicƒvoidƒturnAround()
16 ƒƒ{ƒthis.turnLeft();
17 ƒƒƒƒthis.turnLeft();
18 ƒƒ}
19
20 ƒƒ// Move this robot forward three times.
21 ƒƒpublicƒvoidƒmove3()
22 ƒƒ{ƒthis.move();
23 ƒƒƒƒthis.move();
24 ƒƒƒƒthis.move();
25 ƒƒ}
26
27 ƒƒ// Turn this robot 90 degrees to the right by turning around and then left by 90 degrees.
28 ƒƒpublicƒvoidƒturnRight()
29 ƒƒ{ƒthis.turnAround();
30 ƒƒƒƒthis.turnLeft();
31 ƒƒ}
32 }
2.2.7 RobotSE
You can probably imagine other programs requiring robots that can turn around and
turn right. DeliverParcel (Listing 1-1) could have used turnRight in one place,
while GoAroundRoadBlock (Listing 1-2) could have used it twice. Several of the
programming projects at the end of Chapter 1 could have used either turnAround
or turnRight or both.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 67
67
2.3 EXTENDING
LOOKING AHEAD When we write methods that are applicable to more than one problem, it is a good idea
You learn how to to add that method to a class where it can be easily reused. The becker library has a
build your own class containing commonly used extensions to Robot, including turnRight and
package of useful
THE THING
turnAround. It’s called RobotSE, short for “Robot Special Edition.” In the future,
classes in Chapter 9.
you may want to extend RobotSE instead of Robot so that you can easily use these
additional methods.
CLASS
2.2.8 Extension vs. Modification
LOOKING AHEAD Another approach to making a robot that can turn around and turn right would be to
The turnRight modify the existing class, Robot. Modifying an existing class is not always possible,
method in RobotSE and this is one of those times. The Robot class is provided in a library, without source
actually turns right
code. Without the source code, we have nothing to modify. We say that the Robot class
instead of turning left
three times. is closed for modification.
Section 3.6
explains how. There are other reasons to consider a class closed for modification, even when the
source code is available. In a complex class, a company may not want to risk introduc-
ing errors through modification. Or the class may be used in many different programs,
with only a few benefiting from the proposed modifications.
As we’ve seen, however, the Robot class is open for extension. It is programmed in such
a way that those who want to modify its operation can do so via Java’s extension
mechanism. When a class is open for extension it can be modified via subclasses with-
out fear of introducing bugs into the original class or introducing features that aren’t
generally needed.
To demonstrate extending a class other than Robot, let’s extend Thing to create a Lamp
class. Each Lamp object will have two services, one to turn it “on” and another to turn it
“off.” When a lamp is “on” it displays itself with a soft yellow circle and when it is “off”
it displays itself with a black circle. Because Lamp extends Thing, lamps behave like
things—robots can pick them up, move them, and put them down again.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 68
68
SERVICES
Before attempting to extend the Thing class we should become more familiar with it. LOOKING AHEAD
CHAPTER 2 | EXTENDING CLASSES
The beginning of its online documentation is shown in Figure 2-7. Part of the informa- The Light subclass
tion it provides is the classes Thing extends, also called the inheritance hierarchy. In of Thing may be
this case, Thing extends a class named Sim, and Sim extends Object. The Sim class useful for defining
lamps. We’ll explore
defines core methods inherited by everything displayed in a city, including intersec- this idea later, in
tions, robots, and things. Below the inheritance hierarchy we are told that Thing is Section 2.3.6.
extended by at least two classes: Light and Wall.
(figure 2-7)
Inheritence hierarchy
The summaries for Thing’s four constructors are shown in Figure 2-8. The constructor
we have used so far provides a “default appearance,” which we know from experience is
a yellow circle. The second and third constructors tell us that Thing objects have other
properties such as whether they can be moved (presumably by a robot) and an orienta-
tion. The class overview (not shown in Figure 2-8) refers to these properties as well.
The methods listed in the documentation are mostly queries and don’t seem helpful for
implementing a lamp object. They are listed online, of course, and also in Appendix E.
However, the Thing documentation also includes a section titled “Methods Inherited
from Class becker.robots.Sim.” This section lists a setIcon method. Its descrip-
tion says “Set the icon used to display this Sim.”
Icons determine how each robot, intersection, or thing object appears within the city.
By changing the icon, we can change how something looks. For example, the following
few lines of code replace deceptiveThing’s icon to make the Thing look like a Wall:
69
2.3 EXTENDING
(figure 2-8)
Additional documentation
for the Thing class
THE THING
CLASS
We will do something similar for our Lamp class. When the lamp is turned on we will
give it a new appearance using setIcon, passing it an icon that shows a soft yellow cir-
cle. When the lamp is turned off we will pass setIcon an icon showing a black circle.
We will implement our Lamp class by extending Thing. Our experience with extending
Robot tells us that to extend a class we must complete the following tasks:
➤ Use the class and extends keywords to specify the class’s name and the
name of the class it extends. (See Section 2.2.2.)
➤ Write a constructor that will initialize the superclass appropriately. (See
Section 2.2.3.)
➤ Write methods implementing each of the services offered by the class. (See
Section 2.2.4.)
Knowing these three things, we can write the beginnings of our Lamp class as shown in
Listing 2-5. We still need to replace each ellipsis (…) with additional Java code.
70
SERVICES
3 publicƒclassƒLampƒextendsƒThing
4 {
5 ƒƒ// Construct a new lamp object.
Extended Class
6 ƒƒpublicƒLamp(...)
7 ƒƒ{ƒsuper(...);
8 ƒƒ}
9
10 ƒƒ// Turn the lamp on.
11 ƒƒpublicƒvoidƒturnOn()
12 ƒƒ{ƒ...
13 ƒƒ}
14
15 ƒƒ// Turn the lamp off.
16 ƒƒpublicƒvoidƒturnOff()
17 ƒƒ{ƒ...
18 ƒƒ}
19 }
The Lamp constructor must ensure that the attributes in its superclass, Thing, are com-
pletely initialized when a lamp is created. Recall that attributes are initialized by calling
one of the constructors in the Thing class using the keyword super. The arguments
passed to super must match the parameters of a constructor in the superclass. The doc-
umentation in Figure 2-8 confirms that one of the constructors requires a city, initial
street, and initial avenue as parameters. As with the ExperimentRobot class, this infor-
mation will come via the constructor’s parameters. Putting all this together, lines 6, 7,
and 8 in Listing 2-5 should be replaced with the following code:
publicƒLamp(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue)
{ƒsuper(aCity,ƒaStreet,ƒanAvenue);
}
Constructor
Implementing turnOn and turnOff
When the turnOn service is invoked, the lamp should display itself as a soft yellow cir-
cle. As we discovered earlier, the appearance is changed by changing the lamp’s icon
using the setIcon method inherited from a superclass.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 71
71
2.3 EXTENDING
LOOKING BACK The robot documentation (in the becker.robots.icons package) describes a num-
Finding robot ber of classes that include the word “Icon” such as RobotIcon, ShapeIcon,
documentation was WallIcon, FlasherIcon, and CircleIcon. The last one may be able to help display
discussed in
THE THING
a yellow circle. According to the documentation, constructing a CircleIcon requires
Section 1.4.8.
a Color object to pass as an argument. We’ll need a Color object before we construct
the CircleIcon object.
CLASS
In summary, to change the appearance of our Lamp we must complete the following steps:
create a Color object named “onColor”
create a CircleIcon named “onIcon” using “onColor”
call setIcon to replace this lamp’s current icon with “onIcon”
KEY IDEA We can learn how to construct a Color object by consulting the online documentation at
Documentation is http://java.sun.com/j2se/1.5.0/docs/api/ or, if you have already found the documentation
useful. Bookmark it in for CircleIcon, click on the link to Color found in the constructor’s parameter list.
your browser to make The documentation describes seven Color constructors. The simplest one takes three
it easy to access.
numbers, each between 0 and 255, that specify the red, green, and blue components of
the color. Using 255, 255, and 200 produces a soft yellow color appropriate for a lamp’s
light. A color chooser is a dialog that displays many colors and can help choose these
three values. Most drawing programs have a color chooser and Problem 1.18 provides
guidance for writing your own color chooser.
We can now convert the preceding steps to Java. Inserting them in the turnOn method
results in the following five lines of code:
publicƒvoidƒturnOn()
{ƒƒColorƒonColorƒ=ƒnewƒColor(255,ƒ255,ƒ200);
ƒƒƒCircleIconƒonIconƒ=ƒnewƒCircleIcon(onColor);
Parameterless
ƒƒƒthis.setIcon(onIcon);
Command
}
The turnOff method is identical except that onColor and onIcon should be appro-
priately named offColor and offIcon, and offColor should be constructed with
newƒColor(0,ƒ0,ƒ0).
We have now completed the Lamp class. Listing 2-6 shows it in its entirety. Notice that
it includes two new import statements in lines 2 and 3. The first one gives easier1 access
to CircleIcon; it’s in the becker.robots.icons package. The second one gives
easier access to Color.
1 It is possible to access these classes without the import statement. Every time the class is used, include the
72
SERVICES
ch02/extendThing/
CHAPTER 2 | EXTENDING CLASSES
1 importƒbecker.robots.*;
2 importƒbecker.robots.icons.*;ƒƒƒƒƒ// CircleIcon
3 importƒjava.awt.*;ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// Color
4
5 publicƒclassƒLampƒextendsƒThing Extended Class
6 {
7 ƒƒ// Construct a new lamp object.
8 ƒƒpublicƒLamp(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue)
9 ƒƒ{ƒsuper(aCity,ƒaStreet,ƒanAvenue);
10 ƒƒ}
11
12 ƒƒ// Turn the lamp on.
13 ƒƒpublicƒvoidƒturnOn()
14 ƒƒ{ƒColorƒonColorƒ=ƒnewƒColor(255,ƒ255,ƒ200);
15 ƒƒƒƒCircleIconƒonIconƒ=ƒnewƒCircleIcon(onColor);
16 ƒƒƒƒthis.setIcon(onIcon);
17 ƒƒ}
18
19 ƒƒ// Turn the lamp off.
20 ƒƒpublicƒvoidƒturnOff()
21 ƒƒ{ƒColorƒoffColorƒ=ƒnewƒColor(0,ƒ0,ƒ0);
22 ƒƒƒƒCircleIconƒoffIconƒ=ƒnewƒCircleIcon(offColor);
23 ƒƒƒƒthis.setIcon(offIcon);
Parameterless
24 ƒƒ} Command
25 }
This example illustrates that many classes can be extended, not just the Robot class.
To extend a class, we perform the following tasks:
➤ Create a new class that includes the following line:
publicƒclassƒ«className»ƒextendsƒ«superClass»ƒ
where «superClass» names the class you want to extend. In this example
the superclass was Thing; in the first example the superclass was Robot.
➤ Create a constructor for the new class that has the same name as the class.
Make sure it calls super with parameters appropriate for one of the construc-
tors in the superclass.
➤ Add a method for each of the services the new class should offer.
A short test program that uses the Lamp class is shown in Listing 2-7. It instantiates
two Lamp objects, turning one on and turning the other off. A robot then picks one up
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 73
73
2.3 EXTENDING
and moves it to a new location. The left side of Figure 2-9 shows the initial situation
after lines 7–14 have been executed. The right side of Figure 2-9 shows the result after
lines 17–21 have been executed to move the lit lamp to another intersection. The
THE THING
actual running program is more colorful than what is shown in Figure 2-9.
(figure 2-9) 0 1 2 3 0 1 2 3
CLASS
0 0
Program with two Lamp
objects, one “on” at (1, 1) 1 1
and one “off ” at (2, 1) in
the initial situation 2 2
Listing 2-7: A main method for a program that uses the Lamp class
ch02/extendThing/
1 importƒbecker.robots.*;
2
3 publicƒclassƒMain
4 {
5 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
6 ƒƒ{ƒ// Construct the initial situation.
7 ƒƒƒƒCityƒparisƒ=ƒnewƒCity();
8 ƒƒƒƒLampƒlamp1ƒ=ƒnewƒLamp(paris,ƒ1,ƒ1);
9 ƒƒƒƒLampƒlamp2ƒ=ƒnewƒLamp(paris,ƒ2,ƒ1);
10 ƒƒƒƒRobotƒlampMoverƒ=ƒnewƒRobot(paris,ƒ1,ƒ0,ƒDirection.EAST);
11
12 ƒƒƒƒ// Turn one lamp on and the other off.
13 ƒƒƒƒlamp1.turnOn();
14 ƒƒƒƒlamp2.turnOff();
15
16 ƒƒƒƒ// Use the robot to move one of the lamps.
17 ƒƒƒƒlampMover.move();
18 ƒƒƒƒlampMover.pickThing();
19 ƒƒƒƒlampMover.move();
20 ƒƒƒƒlampMover.putThing();
21 ƒƒƒƒlampMover.move();
22 ƒƒ}
23 }
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 74
74
SERVICES
In Listing 2-7, the main method instantiates two Lamp objects in lines 8 and 9, and
CHAPTER 2 | EXTENDING CLASSES
then explicitly turns one on and one off in lines 13 and 14. Suppose that lines 13 and
14 were omitted, so that the lamps were turned neither on nor off explicitly. How
would they appear? Unfortunately, they would appear just like any other Thing—as a
medium-sized, bright yellow circle. It seems wrong that a Lamp object should appear to
be a Thing just because the client forgot to explicitly turn it on or off.
The problem is that the Lamp constructor did not completely initialize the object. A
complete initialization for a lamp not only calls super to initialize the superclass, it
also sets the icon so the lamp appears to be either on or off.
A constructor can execute statements other than the call to super. It could, for
example, call setIcon. But to follow the Service Invocation pattern of
«objectReference».«serviceName»(…), we need to have an object. The object
we want is the object the constructor is now creating.
The methods we have written often use the implicit parameter, this, to refer to the object LOOKING BACK
executing the method. The implicit parameter, this, is also available within the construc- The implicit
tor. It refers to the object being constructed. We can write this.setIcon(…); within the parameter was
discussed in
constructor. Think of this as referring to this object, the one being constructed.
more depth in
The new version of the constructor is then as follows. It initializes the superclass with the Section 2.2.4.
call to super, and then finishes the initialization by replacing the icon with a new one.
1 publicƒLamp(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue)
2 {ƒsuper(aCity,ƒaStreet,ƒanAvenue);
3 ƒƒColorƒoffColorƒ=ƒnewƒColor(0,ƒ0,ƒ0);
4 ƒƒCircleIconƒoffIconƒ=ƒnewƒCircleIcon(offColor);
5 ƒƒthis.setIcon(offIcon);
6 }
Calling super must be the first statement in the constructor. It ensures that the Thing- KEY IDEA
inside-the-Lamp is appropriately initialized so it can handle the call to this.setIcon. Initializing the
superclass is the first
You may recognize lines 3–5 as being identical to the body of turnOff. Do we really thing to do inside a
need to type in the code twice, and then fix it twice if we discover a bug or want to constructor.
change how a Lamp looks when it is off? No. Recall that an object can call its own
methods. We saw this concept when ExperimentRobot called turnAround from the
turnRight method. Similarly, the constructor can call turnOff directly, a much bet-
ter solution.
1 publicƒLamp(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue)
2 {ƒsuper(aCity,ƒaStreet,ƒanAvenue);
3 ƒƒthis.turnOff();
Constructor
4 }
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 75
75
2.3 EXTENDING
2.3.4 Fine-Tuning the Lamp Class (optional)
The Lamp class can be fine-tuned in several ways to be more visually pleasing.
THE THING
Changing the Size of an Icon
CLASS
A turned-off Lamp appears as large as the yellow circle of light cast by a lamp that is
on, which is not realistic. It’s also unrealistic to represent a lamp with an icon that is as
large as an intersection.
To solve this problem, we need a way to make a smaller icon. The documentation for
CircleIcon includes a method, setSize, for this purpose. Its parameter is a number
between 0.0 and 1.0. A value of 1.0 makes it full size, 0.001 makes it extremely small,
and 0.5 makes it half size. The size must be larger than 0.0.
publicƒvoidƒturnOff()
{ƒColorƒoffColorƒ=ƒnewƒColor(0,ƒ0,ƒ0);
ƒƒCircleIconƒoffIconƒ=ƒnewƒCircleIcon(offColor);
Parameterless
ƒƒoffIcon.setSize(0.25);
Command
ƒƒthis.setIcon(offIcon);
}
The only change in this code, compared to Listing 2-6, is to add the extra method call.
Transparency
To make the lamp more realistic, the light from a lamp should be semi transparent to
let the intersection show through. With the previous change to turnOff and a small
change to turnOn, the initial situation will look like Figure 2-11 instead of Figure 2-
10. Unfortunately, the difference is not as striking in print as it is on-screen in full color.
To obtain a transparent color, we can again use a service CircleIcon inherits from its
superclass. setTransparency takes a number between 0.0 and 1.0 where a value of
0.0 is completely opaque and 1.0 is completely transparent. For the lamp icon a value
of about 0.5 works well. The new version of turnOn follows:
publicƒvoidƒturnOn()
{ƒColorƒonColorƒ=ƒnewƒColor(255,ƒ255,ƒ200);
ƒƒCircleIconƒonIconƒ=ƒnewƒCircleIcon(onColor);
ƒƒonIcon.setSize(0.75);
ƒƒonIcon.setTransparency(0.5);
ƒƒthis.setIcon(onIcon);
}
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 76
76
SERVICES
0 1 2 3 0 1 2 3 (figure 2-10)
0 0
WITH
Original initial
1 1 situation (left)
CHAPTER 2 | EXTENDING CLASSES
(figure 2-11)
2 2
New initial
situation (right)
The becker.robots package includes several subclasses of Thing that have already KEY IDEA
been defined. They are similar to Lamp, except that someone else did the programming, The Thing class
and they have been put into the becker.robots package. Interested students may can be extended
enjoy using them to give additional variety to their programs. in many ways.
One subclass of Thing is called Flasher. It represents the flashing lights used by road
maintenance crews to mark hazards. A Flasher is like a Lamp except that when it is
turned on, it flashes. An example is shown in Figure 2-12. The flasher at the origin is
turned on. The flasher beside it is turned off.
0 1 2 (figure 2-12)
Flasher turned on
0
Flasher turned off The appearance of
1 Flashers and
Both streetlights Streetlights
2 are turned on
Another provided subclass of Thing is Streetlight. Two instances are shown at (2, 1)
and (2, 2). Like walls, streetlights can occupy different positions on an intersection.
These streetlights occupy the southwest and northwest corners of their respective inter-
sections. They were created with the following code:
StreetlightƒsLight1ƒ=ƒ
ƒƒƒƒƒƒnewƒStreetlight(prague,ƒ2,ƒ1,ƒDirection.SOUTHWEST);
StreetlightƒsLight2ƒ=ƒ
ƒƒƒƒƒƒnewƒStreetlight(prague,ƒ2,ƒ2,ƒDirection.NORTHWEST);
The streetlights shown in Figure 2-12 are turned on. Streetlights that are turned off
show only the pole in the corner of the intersection. An intersection may have more
than one streetlight.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 77
77
2.3 EXTENDING
2.3.6 Fun with Lights (optional)
We have already seen how lamps can be turned off and turned on in the main method
THE THING
(see Listing 2-7). The same can be done with Flashers and Streetlights. It is also
possible for robots to turn lights on or off, provided the robot and the light are on the
same intersection. This requires programming concepts that won’t be fully explained
CLASS
until Section 12.1.5, but using them follows a simple pattern and offers more possibil-
ities for creatively using robots.
The key is the Light class that we noticed in the documentation shown in Figure 2-7.
It has two methods named turnOn and turnOff and is the superclass for Flasher
and Streetlight. If we extend Light when we write the Lamp class, all three sub-
classes are, in some sense, lights that can be turned on and off.
LOOKING AHEAD In a subclass of Robot we can use an inherited method, examineLights. It examines
This is an example of the robot’s current intersection for a Light object. If it finds one, it makes it accessible
polymorphism. Learn to the robot. If a light does not exist on the intersection, an error will be printed and
more in Chapter 12.
the program will stop. Listing 2-8 shows most of a subclass of Robot that makes use of
examineLights. A SwitchBot’s switchLights method may be used where there
are four intersections in a row, each one with a light on it.
78
SERVICES
Notice that lines 11, 13, 15, and 17 have three method calls. This is permitted when a
method returns an object. That method call may then be followed with a call to
WITH
another method. The second method call must be appropriate for the object returned
CHAPTER 2 | EXTENDING CLASSES
The Robot class also has methods named examineThings and examineRobots that
can be used similarly. The Intersection and City classes have similar methods
available.
2.4 Style
As programs become more complex, presenting them clearly to anyone who reads KEY IDEA
them (including ourselves) becomes vitally important. Attention to presentation, Everyone, especially
choosing names wisely, indenting, and commenting code all contribute to a program’s you, benefits
from good
clarity. In the course of writing and debugging your programs, you will be studying
programming style.
them more thoroughly than anyone else. It’s to your advantage to make your programs
as understandable as possible.
White space is the empty space between the symbols in a program. The Java compiler KEY IDEA
ignores white space, but its presence (or absence) is important to people reading the Use white space to
program. Consider the program in Listing 2-9. It is identical to the ExperimentRobot highlight the logical
structure of your
class in Listing 2-4, except that the white space and comments have been removed.
program.
Both classes execute in exactly the same way, but one is considerably easier to read and
understand than the other. In particular, Listing 2-9 makes it difficult to see the struc-
ture of the class: that there is one constructor and three methods, where each method
begins, what the method names are, and so on.
1 importƒbecker.robots.*;ƒpublicƒclassƒExperimentRobotƒextends
2 Robotƒ{ƒpublicƒExperimentRobot(CityƒaCity,ƒintƒaStreet,ƒintƒ
3 anAvenue,ƒDirectionƒaDirection)ƒ{ƒsuper(aCity,ƒaStreet,ƒ
4 anAvenue,ƒaDirection);}ƒpublicƒvoidƒturnAround()ƒ{
5 this.turnLeft();ƒthis.turnLeft();ƒ}ƒpublicƒvoidƒmove3(){ƒ
6 this.move();ƒthis.move();ƒthis.move();ƒ}ƒpublicƒvoidƒ
7 turnRight()ƒ{ƒthis.turnAround();ƒthis.turnLeft();ƒ}}
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 79
79
2.4 STYLE
The following recommendations concerning white space are considered good pro-
gramming practice:
➤ Begin each statement on a new line.
➤ Include at least one blank line between blocks of code with different purposes.
For instance, Listing 1-2 includes blank lines between the statements that con-
struct the required objects and the statements that direct the robot mark
around the road block. There is another blank line between the instructions to
mark and the instructions to ann.
➤ Line up curly braces so that the closing brace is directly beneath the open-
ing brace.
➤ Indent everything inside a pair of braces by a consistent number of spaces (this
book uses two).
Many conventions govern indenting programs and lining up braces. None of them are
right or wrong, but are subject to personal preference. Some people like the preceding
style shown through this textbook because it is easy to ensure that braces match.
Nevertheless, your instructor or future employer may have other conventions, com-
plete with reasons to support their preference. As an employee (or student), you will
need to accommodate their preferences.
Programs are available that can reformat code to make it adhere to a specific set of
conventions. This book’s Web site contains references to at least one such program. It
is often possible to configure your IDE so that using a code reformatter is as easy as
clicking a button.
2.4.2 Identifiers
The symbols that make up a Java program are divided into three groups: special sym-
bols, reserved words, and identifiers. Special symbols include the braces { and }, peri-
ods, semicolons, and parentheses. Reserved words, also called keywords, have a special
meaning to the compiler. They include class, package, import, public, and int. A
complete list of reserved words is shown in Table 2-2. Finally, identifiers are names: the
names of variables (karel), the names of classes (Robot), the names of services
(move), and the names of packages (becker.robots).
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 80
80
SERVICES
Programmers have lots of choice in the names they use as identifiers. Wise choices
make a program much easier to read and understand, thereby increasing the likelihood
that it is correct. Programs with well-chosen identifiers are self-documenting and need
fewer explanatory comments.
A well-chosen identifier clearly indicates the purpose of the thing it names. It is better KEY IDEA
to name a class Robot than R, for instance. Naming an icon onIcon is much better A name should clearly
than on or icon or even just i. reveal the purpose of
what it names.
Balanced with the need for a clear intent are readability and brevity for the
sake of the person who must type the name over and over. Naming a robot
robotThatMovesTheThingFrom2_1To3_2 is clearly overkill.
When naming parts of their program, most Java programmers use conventions estab- KEY IDEA
lished by Java’s creators. The name of a class, for example, should be a descriptive, sin- Naming conventions
gular noun such as Robot, Wall, or Lamp. Class names begin with an uppercase letter make it easier to
recognize what an
followed by lowercase letters. If the name is composed of two or more words, such as
identifier represents.
CircleIcon, then capitalize each word.
81
2.4 STYLE
A method name should be a verb or verb phrase that describes what the method does.
Like a variable name, a method name begins with a lowercase letter. In names com-
posed of two or more words, the subsequent words are capitalized. Examples we have
seen so far include move, turnLeft, turnAround, and setLocation.
Capitalization matters in identifiers. westWall is not the same as westwall. The Java
compiler notices the difference between the uppercase W and the lowercase w—even if
we don’t—and treats them as two different identifiers. Table 2-3 summarizes the Java
naming conventions.
2.4.3 Comments
KEY IDEA Comments are annotations inserted by the programmer to help others understand how
Comments do to use the code she is writing, how it works, or how to modify it. The comments help
not affect the establish the context of a statement or block of code so that readers can more quickly
execution of the understand the code. Comments are for people; they do not affect the execution of the
program.
program. In this way, they are like white space.
An excellent practice is to first write a comment that states what this section of your
program must do. Then write the code to implement your comment. Clearly state in
English what you are trying to do, then explain how in Java. This two-part practice
helps keep things clear in your mind, minimizing errors and speeding debugging.
Java has three different kinds of comments: single-line comments, multi-line comments,
and documentation comments.
Single-Line Comments
A single-line comment begins with two consecutive slashes and extends to the end of
the line. Single-line comments have already been used in the programs in Chapter 1 to
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 82
82
SERVICES
document the purpose of a block of code. The first line in the following block of code
is a single-line comment:
WITH
lampMover.move();
lampMover.pickThing();
lampMover.move();
lampMover.putThing();
lampMover.move();
The comment explains the intent of the code; it does not repeat how the code works. A
reader may then consider whether the intent is appropriate at this point in the pro-
gram, and whether the code correctly carries out the intent.
It is also possible to put a single-line comment at the end of a line of code, as shown in
the following line from the FramePlay program in Chapter 1:
importƒjavax.swing.*;ƒƒƒ// use JFrame, JPanel, JButton, JTextArea
Multi-Line Comments
If you have more to say than will fit on a single line consider using a multi-line comment.
A multi-line comment begins with /* and extends, possibly over many lines, until the
next */. The following example is a multi-line comment extending over three lines.
/* Set up the initial situation to match the figure given for problem 5.
It consists of eight walls positioned to form a 2x2 square.
*/
Such a comment should go immediately before the first line of code that implements
what is described.
Another use of a multi-line comment is to temporarily remove some lines of code, per-
haps so another approach can be tested without losing previous work. For example, in
Section 2.2.6, we explored two ways to implement turnRight. The programmer
could have started with the solution that uses turnLeft three times. Perhaps she then
thought of the other solution, which turns around first. If she wasn’t quite sure the sec-
ond solution would work, her code might have looked like this:
publicƒvoidƒturnRight()
{ƒ/*
ƒƒthis.turnLeft();
ƒƒthis.turnLeft();
ƒƒthis.turnLeft();
ƒƒ*/
ƒƒthis.turnAround();
ƒƒthis.turnLeft();
}
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 83
83
2.4 STYLE
KEY IDEA This practice is called commenting out code. One of the consequences of using the next
Use single-line */ to end a comment is that a multi-line comment can not include another (nested)
comments to multi-line comment. That is, you can’t comment out multi-line comments. For this rea-
annotate code and
son, some programmers reserve multi-line comments for commenting out code, using
multi-line
comments to single-line comments for actual annotations.
comment out
existing code.
Documentation Comments
Single-line and multi-line comments explain the intent of the code for people reading
the code. Documentation comments are used to generate Web-based documentation
like that shown in Figure 1-15 through Figure 1-18. They describe the purpose of a
class, constructor, or method and provide information useful to people who want to
use them without understanding how they work internally.
KEY IDEA Each class, constructor, and method should have a documentation comment. The com-
Document the ment must appear immediately before the class, method, or constructor is declared;
purpose of each that is, immediately before the line containing publicƒclassƒ«className»
class, constructor,
extendsƒ«superClass» or publicƒvoidƒ«methodName»().
and method.
A documentation comment is similar to a multi-line comment, except that it begins
with /** rather than /*. Another difference is that a documentation comment may
contain tags to identify specific information that should be displayed distinctively on a
Web page. For example, the documentation comment for a class may contain the
@author tag to identify the person who wrote the class.
One of the most important tags is @param. It allows you to document each parameter’s
purpose. The @param tag is followed by the name of the parameter and a description
of that parameter.
Listing 2-10 shows the ExperimentRobot listing again, this time with appropriate
documentation comments.
1 importƒbecker.robots.*;
2
3 /** A new kind of robot that can turn around, turn right, and move forward three intersections
4 * at a time.
5 * @author Byron Weber Becker */
6 publicƒclassƒExperimentRobotƒextendsƒRobot
7 {
8 ƒƒ/** Construct a new ExperimentRobot.
9 ƒƒ* @param aCity The city in which the robot will be located.
10 ƒƒ* @param aStreet The robot’s initial street.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 84
84
SERVICES
The tool that extracts the documentation comments and formats them for the Web is
known as javadoc. Your development environment might have a simpler way to run
this tool; if it does not, you can perform the following steps on a computer running
Windows (with analogous steps on other computers).
➤ Open a window for a command-prompt.
➤ Change directories to the directory containing the Java source code.
➤ Run javadoc, specifying the Java files to process and where the output should be
placed.
➤ View the result with a Web browser.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 85
85
2.4 STYLE
For example, suppose we want to generate documentation for the three classes used in
the experiment at the beginning of the chapter and that the files reside in the directory
E:\experiment. In the command-prompt window, enter the following commands
(the >ƒis called a prompt and is displayed by the system to indicate that it is ready to
accept your input).
>ƒE:
>ƒcdƒexperiment
>ƒjavadocƒ–dƒdocƒ–classpathƒe:/robots/becker.jarƒ*.java
The first two commands change the focus of the command prompt, first to the correct
disk drive (E:), and then to the correct directory on that disk (experiment). The last
command starts the program that produces the Web pages. This javadoc command
has three sets of parameters:
➤ -dƒdoc specifies that the documentation should be placed in a directory
named doc.
➤ -classpathƒe:/robots/becker.jar indicates where to find the becker
library. It allows javadoc to include relevant information about robot classes.
You will need to replace the path given here with the location of the library on
your computer.
➤ *.java means that javadoc should process all the files in the current direc-
tory that end with .java.
Depending on how your software is installed, the system may not find the javadoc pro-
gram. In that case, you need to find javadoc’s location on the disk drive and provide the
complete path to it. For example, on my system, I used the search feature of the Windows
Explorer program to search for javadoc. The results told me that the program was in
C:\java\jdk1.5\bin\javadoc.exe. I could then modify the last line as follows:
>ƒC:\java\jdk1.5\bin\javadocƒ–dƒdocƒ–classpathƒe:/robots/becker.jarƒ*.java
Alternatively, you may be able to set your system’s path variable to include the direc-
tory containing javadoc.
publicƒvoidƒmove3()
{ƒthis.turnLeft();
ƒƒthis.pickThing();
Parameterless ƒƒthis.turnLeft();
Command }
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 86
86
SERVICES
If we defined the move3 method this way, someone else reading our program would be
confused and surprised. Over time, we could confuse ourselves, introducing errors into
WITH
The meaning of a command is the list of commands contained in its body, not its name.
When a program is executed, the command does exactly what the commands in the
body instruct it to do. There is no room for interpretation.
A good programmer gives each command a meaningful name. Another person should KEY IDEA
be able to make a reasonable guess about what the command does from its name. Use meaningful
There should be no surprises such as the robot picking something up in the middle of a names.
command whose name does not imply picking things up.
When we write new programs, it is common to trace the program by hand to verify
how it behaves. Because the command name only implies what it does, it is important
to trace the actual instructions in each command. The computer cannot and does not
interpret the names of commands when it executes a program; we shouldn’t either
when we trace a program manually.
The correctness of a command is determined by whether it fulfills its specification. The KEY IDEA
specification is a description of what the method is supposed to do. The specification Correct methods meet
might be included in the method’s documentation or in the problem statement given by their specifications.
your instructor. A command may be poorly named, but still correct. For instance, the
specification of ExperimentRobot at the beginning of the chapter requires a com-
mand to turn the robot around. It could have been given the idiotic name of doIt. As
long as doIt does, indeed, turn the robot around (and nothing else) the specification is
met and the command is correct.
Because the move3 command is simple, it is easy to convince ourselves that it is cor-
rect. Many other commands are much more complex, however. The correctness of
these commands must be verified by writing test programs that execute the com-
mand, checking the actual result against the expected result. This practice is not fool-
proof, however. Conditions in which the command fails may not be tested and go
undetected.
A correct command, such as move3, can be used incorrectly. For example, a client can
place an ExperimentRobot facing a wall. Instructing this robot to move3 will result
in an error when the robot attempts to move into the wall. In this case, we say the com-
mand’s preconditions have not been met. Preconditions are conditions that must be
true for a command to execute correctly.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 87
87
To override the definition of a method, you create a new method with the same name,
return type, and parameters in a subclass. These constitute the method’s signature. As an
example, let’s create a new kind of robot that can turn left quickly. That is, we will over-
ride the turnLeft method with a new method that performs the same service differently.
You may have noticed that the online documentation for Robot includes a method
named setSpeed, which allows a robot’s speed to be changed. Our general strategy
will be to write a method that increases the robot’s speed, turns, and then returns the
speed to normal. Turning quickly doesn’t seem to be something we would use often, so
it has not been added to RobotSE. On the other hand, it seems reasonable that fast-
turning robots need to turn around and turn right, so our new class will extend
RobotSE.
As the first step in creating the FastTurnBot class, we create the constructor and the
shell of the new turnLeft method, as shown in Listing 2-11.
88
SERVICES
12 ƒƒpublicƒFastTurnBot(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue,
13 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒDirectionƒaDirection)
14 ƒƒ{ƒsuper(aCity,ƒaStreet,ƒanAvenue,ƒaDirection);
Constructor
15 ƒƒ}
16
17 ƒƒ/** Turn 90 degrees to the left, but do it more quickly than normal. */
18 ƒƒpublicƒvoidƒturnLeft()
19 ƒƒ{
20 ƒƒ}
21 }
When this class is instantiated and sent a turnLeft message, it does nothing. When
the message is received, Java starts with the object’s class (FastTurnBot) and looks
for a method matching the message. It finds one and executes it. Because the body of
the method is empty, the robot does nothing.
How can we get it to turn again? We cannot write this.turnLeft(); in the body of LOOKING AHEAD
turnLeft. When a turnLeft message is received, Java finds the turnLeft method Recursion occurs
and executes it. The turnLeft method then executes this.turnLeft, sending when a method calls
itself. Although
another turnLeft message to the object. Java finds the same turnLeft method and
recursion causes
executes it. The process of executing it sends another turnLeft message to the object, problems in this case,
so Java finds the turnLeft method again, and repeats the sequence. The program it is a powerful
continues sending turnLeft messages to itself until it runs out of memory and technique.
crashes. This problem is called infinite recursion.
What we really want is the turnLeft message in the FastTurnBot class to execute KEY IDEA
the turnLeft method in a superclass. We want to send a turnLeft message in such a Using super instead
way that Java begins searching for the method in the superclass rather than the object’s of this causes Java to
search for a method in
class. We can do so by using the keyword super instead of the keyword this. That is,
the superclass rather
the new definition of turnLeft should be as follows: than the object’s class.
publicƒvoidƒturnLeft()
{ƒsuper.turnLeft();
}
We have returned to where we started. We have a robot that turns left at the normal
speed. When a FastTurnBot is sent a turnLeft message, Java finds this turnLeft
method and executes it. This method sends a message to the superclass to execute its
turnLeft method, which occurs at the normal speed.
To make the robot turn faster, we add two calls to setSpeed, one before the call to
super.turnLeft() to increase the speed, and one more after the call to decrease the
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 89
89
The default speed of a robot is two moves or turns per second. The following method
uses setSpeed so the robot turns 10 times as fast as normal, and then returns to the
usual speed.
publicƒvoidƒturnLeft()
{ƒthis.setSpeed(20);
ƒƒsuper.turnLeft();
ƒƒthis.setSpeed(2);
}
The FastTurnBot class could be tested with a small program such as the one in
Listing 2-12. Running the program shows that speedy does, indeed, turn quickly
when compared to a move.
90
SERVICES
So far, we have glossed over how Java finds the method to execute, a process called
CHAPTER 2 | EXTENDING CLASSES
method resolution. Consider Figure 2-13, which shows the class diagram of a
FastTurnBot. Details not relevant to the discussion have been omitted, including con-
structors, attributes, some services, and even some of the Robot’s superclasses (repre-
sented by an empty rectangle). The class named Object is the superclass, either
directly or indirectly, of every other class.
When a message is sent to an object, Java always begins with the object’s class, looking
for a method implementing the message. It keeps going up the hierarchy until it either
finds a method or it reaches the ultimate superclass, Object. If it reaches Object
without finding an appropriate method, a compile-time error is given.
To execute the move method, Java begins with speedy’s class, FastTurnBot, in the
search for the method. When Java doesn’t find a method named move in
FastTurnBot, it looks in RobotSE and then in Robot, where a method matching the
move method is found and executed.
The search for a move method begins with RobotSE, the class that instantiated special.
It doesn’t matter that RobotSE has been extended by another class; what matters is that
when special was constructed, the programmer used the constructor for RobotSE.
Therefore, searches for methods begin with RobotSE.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 91
91
Robot
void turnLeft( )
void move( )
...
RobotSE
void turnAround( )
void turnRight( )
FastTurnBot
void turnLeft( )
Once again, consider speedy. What happens if speedy is sent the turnAround message?
The search for the turnAround method begins with speedy’s class, FastTurnBot. It’s
found in RobotSE and executed. As it is executed, it calls turnLeft. Which turnLeft
method is executed, the one in FastTurnBot or the one in Robot?
KEY IDEA The turnLeft message in turnAround is sent to the implicit parameter, this. The
Overriding a method implicit parameter is the same as the object that was originally sent the message, speedy.
can affect other So Java begins with speedy’s class, searching for turnLeft. It finds the method that turns
methods that call it,
quickly and executes it. Therefore, a subclass can affect how methods in a superclass are
even methods in a
superclass. executed.
KEY IDEA Now the search for turnLeft begins with the superclass of the class containing the
The search for the method, or Robot. Robot contains a turnLeft method. It is executed, and the robot
method matching a turns around at the normal pace.
message sent to
super begins in the Suppose you occasionally want speedy to turn left at its normal speed. Can you some-
method’s superclass. how skip over the new definition of turnLeft and execute the normal one, the one
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 92
92
SERVICES
that was overridden? No. If we really want to execute the original turnLeft, we
should not have overridden it. Instead, we should have simply created a new method,
WITH
FastTurnBot has a problem, however. Suppose that Listing 2-12 contained the state-
ment speedy.setSpeed(20); just before line 12. This statement would speed
speedy up dramatically. Presumably, the programmer wanted speedy to be speedier
than normal all of the time. After its first turnLeft, however, speedy would return to
its normal pace of 2 moves per second.
This phenomenon is called a side effect. Invoking turnLeft changed something it KEY IDEA
should not have changed. Our programmer will be very annoyed if she must reset the Not only are side
speed after every command that turns the robot. Ideally, a FastTurnBot returns to its effects annoying, they
can lead to errors.
previous speed after each turn.
Avoid them where
possible; otherwise,
The programmer can use the getSpeed query to find out how long the robot currently
document them.
takes to turn. This information can be used to adjust the speed to its original value after the
turn is completed. The new version of turnLeft should perform the following steps:
The query this.getSpeed() obtains the current speed. Multiplying the speed by 10 LOOKING AHEAD
and using the result as the value to setSpeed increases the speed by a factor of 10. Another approach is
After the turn, we can do the reverse to decrease the speed to its previous value, as to remember the
shown in the following implementation of turnLeft: current speed. When
the robot is finished
publicƒvoidƒturnLeft() turning, set the speed
to the remembered
{ƒthis.setSpeed(this.getSpeed()ƒ*ƒ10);
value. More in
ƒƒsuper.turnLeft();
Chapters 5 and 6.
ƒƒthis.setSpeed(this.getSpeed()ƒ/ƒ10);
}
Using queries and doing arithmetic will be discussed in much more detail in the fol-
lowing chapters.
93
Container
void add(Component c)
Window JComponent
JFrame
One new aspect of this class diagram is that some classes have two or more subclasses. For
example, Container is the superclass for both Window and JComponent. The effect is
that Window objects and JComponent objects (and their subclasses) have much in com-
mon—the set of services they inherit from the Container and Component classes.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 94
94
SERVICES
➤ When we implemented the FramePlay program in Listing 1-6, we sent six differ-
ent messages to a JFrame object: setContentPane, setTitle,
CHAPTER 2 | EXTENDING CLASSES
In this section we will write a program that paints a picture. Figure 2-15 shows a sim-
ple stick figure. When viewed in color, the pants are blue, the shirt is red, and the head
is yellow.
(figure 2-15)
2 There is, unfortunately, some fine print. The statements above are true, but in some circumstances you
can’t see the results. For example, setting the background color of a JFrame doesn’t appear to have an
effect because the content pane completely covers the JFrame, and you see the content pane’s color.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 95
95
Listing 2-13 shows the beginnings of the StickFigure class. It provides a parameter-
less constructor and nothing more. The constructor doesn’t need parameters because
JComponent has a constructor that does not need parameters. Our constructor calls
JComponent’s constructor by invoking super without parameters.
The constructor performs one important task: in lines 13–14 it specifies a preferred
size for the stick figure component. The preferred size says how many pixels wide and
high the component should be, if possible. Line 13 creates a Dimension object 180
pixels wide and 270 pixels high. The next line uses this object to set the preferred size
for the stick figure.
this.setPreferredSize(newƒDimension(180,ƒ270));
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 96
96
SERVICES
This creates the object and passes it to setPreferredSize without declaring a vari- KEY IDEA
able. We can avoid declaring the variable if we don’t need to refer to the object in the
WITH
Sometimes we don’t
future (as with Wall and Thing objects), or we can pass it to the only method that need a variable to
CHAPTER 2 | EXTENDING CLASSES
store object
requires it as soon as it’s created, as we do here.
references.
Now would be a good time to implement the main method for the program. By com-
piling and running the program early in the development cycle, we can often catch
errors in our thinking that may be much more difficult to change later on. Listing 2-14
shows a program for this purpose. Running it results in an empty frame as shown in
Figure 2-16. It follows the Display a Frame pattern and consequently it is similar to the
FramePlay program in Listing 1-6.
One difference between this program and the FramePlay program and the pattern is
in how the frame is sized. The previous program explicitly set the size of the frame
using the setSize method. This version uses the method pack in line 22. This method
uses the preferred sizes of all the components to calculate the best size for the frame.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 97
97
(figure 2-16)
LOOKING AHEAD The superclass’s implementation of paintComponent may have important work to
We are practicing do, and so it should be called with super.paintComponent(g). It requires a
incremental Graphics object as an argument, and so we pass it g, the Graphics object received as
development: code a
a parameter. Doing so results in the following method. The method still has not added
little, test a little,
code a little, test a any functionality, but adding it to Listing 2-13 between lines 14 and 15 still results in a
little. For more on running program.
development
strategies, see publicƒvoidƒpaintComponent(Graphicsƒg)
Chapter 11. {ƒsuper.paintComponent(g);
}
98
SERVICES
determined by the most recent setColor message sent to g. The color specified is used
until the next setColor message.
WITH
CHAPTER 2 | EXTENDING CLASSES
All of the draw and fill methods require parameters specifying where the shape is to
be drawn and how large it should be. Like positioning a frame, measurements are
given in relation to an origin in the upper-left corner, and are in pixels.
Figure 2-17 shows the relationship between the parameters and the figure that is
drawn. For drawRect and drawOval, the first two parameters specify the position of
the upper left corner of the figure, while the third and fourth parameters specify the
width and height. For an oval, the width and height are of the smallest box that can
contain the oval. This box is called the bounding box and is shown in Figure 2-17 as a
dashed line. Of course, the bounding box is not actually drawn on the screen.
(figure 2-17)
100
100
140 140
g.drawRect(30, 50, 140, 100); g.drawOval(30, 50, 140, 100);
50
50
100
30 30
140
In each of these methods, the order of the arguments is x before y and width before height.
The parameters for a line are different from the parameters for rectangles and ovals.
The first two parameters specify one end of the line in relation to the origin, while the
last two parameters specify the other end of the line in relation to the origin.
The drawString method takes a string as the first parameter and the position of the
first letter as the second and third parameters.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 99
99
100
SERVICES
You may have noticed that the paintComponent method is not called from any-
CHAPTER 2 | EXTENDING CLASSES
where in Listing 2-15 or the client code shown in Listing 2-14. Look all through the
code, and you will not find an instance of the Command Invocation pattern
stickFig.paintComponent(g);. Yet we know it is invoked because it paints the
stick figure. How?
When a JFrame is instantiated, a second thread begins. This is not a normal occur- LOOKING AHEAD
rence when an object is instantiated; JFrame’s authors deliberately set up the new We will use this
thread. JFrame’s thread monitors the frame and detects when it has been damaged and capability in
Section 3.5.2 to make
must be repainted. A frame can be damaged in many ways. It is damaged when the user
two or more robots
resizes it by dragging a border or clicking the minimize or maximize buttons. It’s dam- move simultaneously.
aged when it is first created because it hasn’t been drawn yet. It’s also damaged if
another window is placed on top of it and then moved again. In each of these cases, the KEY IDEA
second thread of control calls paintComponent, providing the Graphics object that paintComponent is
paintComponent should draw upon. called by “the system.”
We don’t call it.
We learned in Section 2.3.1 that Icon is the class used to represent images of things in
the robot world—robots, intersections, things, flashers, walls, and so on—all use icons
to display themselves. As you might expect, Icon has been extended a number of times
to provide different icons for different kinds of things. The documentation references
classes named FlasherIcon, RobotIcon, WallIcon, and so on.
You, too, can extend the Icon class to create your own custom icons. The example
shown in Figure 2-18 was produced by the code shown in Listing 2-16.
As with any other subclass, it gives the name of the class it extends (line 5). Before that are
the packages it relies on. In this case, it imports the Icon class from the
becker.robots.icons package and the Graphics class from java.awt.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 101
101
An icon is always painted in a standard 100 × 100 pixel space facing north. Lines 14–23
in Listing 2-16 draw the robot in this position. Other parts of the robot system scale and
rotate the icons, as necessary.
102
SERVICES
Use the setIcon method to change the icon used to display a robot. One way to call
setIcon is to create a new class of robots, as shown in Listing 2-17.
WITH
CHAPTER 2 | EXTENDING CLASSES
2.8 Patterns
In this chapter we’ve seen patterns to extend a class, write a constructor, and imple-
ment a parameterless command. These are all extremely common patterns; so com-
mon, in fact, that many experienced programmers wouldn’t even recognize them as
patterns. We’ve also seen a much less common pattern to draw a picture.
Context: You need a new kind of object to provide services for a program you are writ-
ing. An existing class provides objects with closely related services.
Solution: Extend the existing class to provide the new or different services required.
For example, the following listing illustrates a new kind of robot that provides a ser-
vice to turn around.
importƒbecker.robots.*;
publicƒclassƒTurnAroundBotƒextendsƒRobot
{
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 103
103
2.8 PATTERNS
ƒƒƒpublicƒTurnAroundBotƒ(CityƒaCity,ƒintƒaStreet,
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒintƒanAvenue,ƒDirectionƒaDirection)
ƒƒƒ{ƒsuper(aCity,ƒaStreet,ƒanAvenue,ƒaDirection);
ƒƒƒ}
ƒƒƒpublicƒvoidƒturnAround()
ƒƒƒ{ƒthis.turnLeft();
ƒƒƒƒƒthis.turnLeft();
ƒƒƒ}
}
This listing also makes use of the Constructor and Method patterns. More generally, a
Java class uses the following code template:
publicƒclassƒ«className»ƒextendsƒ«superClass»
{ƒ«listƒofƒattributesƒusedƒbyƒthisƒclass»
ƒƒ«listƒofƒconstructorsƒforƒthisƒclass»
ƒƒ«listƒofƒservicesƒprovidedƒbyƒthisƒclass»
}
The Java Program pattern can be seen as a special version of the Class pattern, which
has no constructors or attributes and contains only the specialized service named main.
It should make sense for the client using the subclass to also use any of the methods in
its superclass. If not, think carefully about the superclass; it may have been chosen
incorrectly. If there is no class to serve as the superclass, use Object, a class that con-
tains the minimal set of methods required of every Java class.
Related Patterns:
➤ The Constructor pattern is always applied within an instance of the Extended
Class pattern.
➤ The Parameterless Command pattern is always applied within an instance of
the Extended Class pattern.
Name: Constructor
104
SERVICES
Solution: Add a constructor to the class. A constructor has the same name as the class
and is usually preceded by the keyword public. It often has parameters so that the
WITH
client constructing the object can provide initialization details at run time. The con-
CHAPTER 2 | EXTENDING CLASSES
structor must also ensure that the object’s superclass is appropriately initialized, using
the keyword super. The types of the parameters passed to super should match the
types required by one of the constructors in the superclass. Constructors and their
parameters should always have a documentation comment.
Following is an example of a constructor that simply initializes its superclass with val-
ues received via its parameters:
More generally, a constructor makes a call to super and may then execute other Java
statements to initialize itself.
/**«Descriptionƒofƒwhatƒthisƒconstructorƒdoes.»
*ƒ@paramƒ«parameterName»ƒ«Descriptionƒofƒparameter»
*/
publicƒ«className»(«parameterƒlist»)
{ƒsuper(«arguments»);
ƒƒ«listƒofƒJavaƒstatements»
}
If the parameter list is empty, the documentation comment does not contain any
@param tags. Otherwise, the documentation comment contains one @param tag for
each parameter.
Consequences: A constructor should ensure that each object it creates is completely and
consistently initialized to appropriate values. Doing so leads to higher quality software.
In some circumstances the compiler supplies a missing constructor, but don’t rely on
the compiler to do so. If you always supply a constructor, you increase your chances of
remembering to initialize everything correctly. You also minimize the possibility that
future changes will break your software.
Related Patterns: The Constructor pattern always occurs within a pattern for a class.
The Extended Class pattern is one such pattern.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 105
105
2.8 PATTERNS
2.8.3 The Parameterless Command Pattern
Context: You are writing or extending a class and need to provide a new service to
clients. The service does not require any information other than the object to act upon
(the implicit parameter) and does not return any information.
Consequences: The new service is available to any client of objects instantiated from
this class. In future chapters we will see replacements for the public keyword that
make the command’s use more restricted.
Related Patterns: The Parameterless Command pattern always occurs within a pattern
for a class. The Extended Class pattern is one such pattern.
Context: You want to show an image to the user that is constructed from ovals, rectan-
gles, lines, and strings.
importƒjava.awt.*;
importƒjavax.swing.*;
publicƒclassƒ«className»ƒextendsƒJComponent
{
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 106
106
SERVICES
ƒƒƒpublicƒ«className»()
ƒƒƒ{ƒsuperƒ();
WITH
ƒƒƒƒƒthis.setPreferredSize(
CHAPTER 2 | EXTENDING CLASSES
ƒƒƒƒƒƒƒƒƒƒnewƒDimension(«prefWidth»,ƒ«prefHeight»));
ƒƒƒ}
ƒƒƒƒƒ«statementsƒusingƒgƒtoƒdrawƒtheƒimage»
ƒƒƒ}
}
Related Patterns:
➤ The extended component can be displayed using the Display a Frame pattern.
➤ The Draw a Picture pattern is a specialization of the Extended Class pattern
and contains an example of the Constructor pattern.
The subclass inherits all of the methods from the superclass. New methods added to
the subclass may call methods in the superclass or other methods in the subclass. When
a message is sent to an object, the process of determining which method to execute is
called method resolution. Method resolution always starts with the class used to
instantiate the object, unless the method is called using super in which case method
resolution begins with the superclass of the class making the call.
A method in the subclass overrides an existing method when it has the same name and
parameter types as a method in one of the superclasses. The overridden method may be
called using the keyword super.
The components used to display graphical user interfaces make extensive use of inher-
itance and overriding. For example, one overrides the paintComponent method to
alter the appearance of a component.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 107
107
initialize objects
constructors
r
” fo
plate
“tem
has
is a
inherits attributes from its
is a
inherits services from its
may
a
from services
ods
conta
meth
de
v erri
in
o
may ment
imple
should
be form
atted w
ith app
ropriate
style
of
whitespace, t elements
indentation, are importan
comments
Written Exercises
2.1 Based on what you now know about the getSpeed and setSpeed services
from Section 2.6.1, revise the Robot class diagram shown in Figure 1-8.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 108
108
SERVICES
2.2 Consider a robot that implements turnRight as shown in Listing 2-4 and
implements turnAround by calling turnRight twice.
WITH
b. How much time does this version of turnAround require compared to the
version in Listing 2-4?
2.3 Write a new constructor for the RobotSE class. Robots constructed with this
new constructor will always be placed at the origin of the city facing EAST.
2.4 Add arrows to Figure 2-19, which is similar to Figure 2-6, showing the follow-
ing method calls:
a. Method calls resulting from bob.turnLeft()
b. Method calls resulting from lisa.turnLeft()
c. Method calls resulting from lisa.turnAround1()
d. Method calls resulting from lisa.turnAround2()
To keep the diagrams uncluttered, answer each part of the question on a sepa-
rate copy of the diagram and omit arrows the second time a method is called
from the same place (for example, do not draw arrows for the second call to
turnLeft in turnAround1).
… int getSpeed()
{
}
}
2.5 The change from the initial situation to the final situation shown in Figure 2-20 is
accomplished by sending the robot exactly one move message. Ordinarily such a
stunt would cause the robot to crash. There are at least three fundamentally differ-
ent approaches to solving this seemingly impossible problem. Explain two of them.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 109
109
2 2
Programming Exercises
2.6 Write a new class, MileMover, that includes two methods: moveMile moves a
robot forward 10 intersections, and move1000Miles which moves the robot for-
ward 1,000 miles. Your solution should be much shorter than 1,000 lines of code.
2.7 Instances of the BackupBot class can respond to the backup message by mov-
ing to the intersection immediately behind it, facing their original direction.
a. Create the BackupBot class by extending Robot.
b. Arrange for the backup method to take the same amount of time as the
move method.
c. Create the BackupBot class by extending RobotSE and taking advantage of
the methods it contains.
2.8 Extend RobotSE to create a LeftDancer class. A LeftDancer, when sent a
move message, ends up at the same place and facing the same direction as a
normal robot. But it gets there more “gracefully.” A LeftDancer first moves
to the left, then forward, and then to the right.
2.9 Extend RobotSE to create a TrailBot class. A TrailBot leaves behind a trail
of “crumbs” (Thing objects) whenever it moves. Arrange for instances of
TrailBot to always start with 100 Things in its backpack. (Hint: Check out
the Robot constructors in the documentation.)
a. Add a trailMove method. When called, it leaves a “crumb” on the current
intersection and moves forward to the next intersection. move still behaves
as usual.
b. Arrange for a TrailBot to always leave a “crumb” behind when it moves.
2.10 Extend Robot to make a new class, PokeyBot. PokeyBots ordinarily make one
move or turn every two seconds (that is, 0.5 moves per second). However, the
statement pokey.setSpeed(3) makes a robot named pokey go faster until a
subsequent setSpeed command is given. Write a program that instantiates a
PokeyBot and verifies that it moves more slowly than a standard Robot. Also
verify that setSpeed works as described. Hint: You do not need to override
move or turnLeft.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 110
110
SERVICES
describe what happens, using a diagram similar to Figure 2-5 to illustrate what
CHAPTER 2 | EXTENDING CLASSES
happened. (Hint: You may not be able to read the first line of the resulting
error message. It probably says something about “Stack Overflow,” which
means the computer ran out of memory. A little bit of memory is used each
time a method is called until that method is finished executing.)
Programming Projects
2.12 karel the robot has taken up diving. Write a program that sets up the follow-
ing situation with karel at the top of the diving board. The single message
dive should cause it to do a triple front flip (turn completely around three
times) at the location shown while it dives into the pool (see Figure 2-21).
karel is an instance of the Diver class.
2.13 You would like to send a greeting to astronauts in the International Space
Station orbiting Earth. Create a WriterBot class that can be used to “write”
the message “Hello” by lighting bonfires (Things). The final situation is
shown in Figure 2-22. The WriterBot class will contain a method to write
each letter.
a. Write a main method that uses a single WriterBot to write the entire mes-
sage. Instantiate the robot with at least 48 Things in its backpack. (Check the
documentation for the Robot or RobotSE class for an alternate constructor.)
b. Write a main method that uses five WriterBots, one for each letter.
Instantiate each robot with enough Things to write its assigned letter.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 111
111
Friendly message
2.14 Write a program where robots practice playing soccer, as shown in Figure 2-23.
Your program will have four SoccerBot robots. Each has methods to
advance, advanceLeft, and advanceRight. Each of these methods begins
with picking up the “ball” (a Thing), moving it in the pattern shown, and then
dropping it.
a. Write a main method that sets up the city as shown and directs the four
players to move the ball along the path shown by the arrows.
b. Write a subclass of City, SoccerField, that arranges the “goals” as
shown. Add the ball and soccer players in the main method, as usual. (Hint:
The Wall constructor requires a City. Which city? “this” city.)
(figure 2-23)
advanceRight
2.15 When a Lamp is on all that is visible is a soft yellow circle representing the
“light.” The “lamp” itself doesn’t show unless it is off. Read the documenta-
tion for CompositeIcon. Then modify Listing 2-6 to make Lamp objects show
both the “lamp” and the “light” when they are on.
2.16 In Section 2.3.2, we extended the Thing class, and in Section 2.2.4, we saw
that this can be used to invoke methods inherited from the superclass.
a. Use these techniques to extend JFrame to obtain CloseableJFrame,
which sets its default close operation, automatically opens to a default
position and size of your choice, and is visible. Write a test program to
ensure your new class works.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 112
112
SERVICES
b. Modify your solution so the client creating the frame can specify its position
and size via parameters.
WITH
2.17 Extend the functionality of JFrame so that a simple program containing the
CHAPTER 2 | EXTENDING CLASSES
following code:
publicƒstaticƒvoidƒmain(String[]ƒargs)
{ƒColorChooserFrameƒccfƒ=ƒnewƒColorChooserFrame();
}
will cause a frame containing a JColorChooser to appear on the screen,
appropriately sized. See Programming Project 1.18 for background.
2.18 Sketch a scene on graph paper that uses a combination of several rectangles,
ovals, lines, and perhaps strings (text). Write a program that displays your scene.
2.19 Extend the Icon class as shown in Figure 2-24. The grid is to aid you in paint-
ing; it is not intended to be displayed by your icons.
a. Choose one of the icons shown in Figure 2-24 to implement. Instantiate a
Thing and use its setIcon method to display your icon.
b. Introduce a Car class to the robot world. A Car is really a Robot, but uses
a CarIcon, as shown in Figure 2-24.
c. Write a TreeIcon class that appears approximately as shown in Figure 2-24,
using at least three shades of green. Extend Thing to make a Tree class and
construct a city with several trees in it. Robots should not be able to pick up
and move trees.
d. Create a Lake class that extends Thing and is displayed with a LakeIcon
as shown in Figure 2-24. Robots should not be able to pick up and move
lakes, and if they try to enter a lake, they break. Research the Thing class to
discover how to implement these features.
e. Write an R10Icon for an advanced type of Robot, as shown in Figure 2-24.
Research the setFont method in the Graphics class and the Font class to
label the robot. Construct a city with at least one robot that uses your icon.
f. Extend Wall to create a Building class. Use a BuildingIcon, as shown
in Figure 2-24, to display your buildings. Robots should not be able to pass
through or over your buildings.
g. Write a DetourIcon. Use it in DetourSign, an extension of Thing. You
will need to research the Polygon class and then use the fillPolygon
method in the Graphics class. Research the Thing class to learn how to
make your sign block robots from entering or exiting the intersection on the
NORTH side. You may assume that the sign will always be placed on the
north side of the intersection.
2 Chapter C5743 40143.ps 11/30/06 1:17 PM Page 113
113
Chapter Objectives
After studying this chapter, you should be able to:
➤ Use stepwise refinement to implement long or complex methods
➤ Explain the advantages to using stepwise refinement
➤ Use pseudocode to help design and reason about methods before code is written
➤ Use multiple objects to solve a problem
➤ Use inheritance to reduce duplication of code and increase flexibility
➤ Explain why some methods should not be available to all clients and how to
appropriately hide them
In Chapter 2, we wrote new services such as turnRight and turnAround. These ser-
vices were very simple, consisting of only a few steps to accomplish the task.
In this chapter, we will examine techniques for implementing much more complex ser-
vices that require many steps to accomplish the task.
115
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 116
116
CHAPTER 3 | DEVELOPING METHODS
We find many algorithms in our lives. A recipe for lasagna is an algorithm, as are the
directions for assembling a child’s wagon. Even bottles of shampoo have algorithms
printed on them:
While people may have no trouble interpreting this algorithm, it is not precise enough
for computers. How much warm water? How much shampoo? What does it mean to
“gently work in?” How many times should it be repeated? Once? A hundred times?
Indefinitely? Is it necessary to wet the hair (again) for the repeated applications?
Not all algorithms are equally effective. Good algorithms share five qualities. Good
algorithms are:
➤ correct
➤ easy to read and understand
➤ easy to debug
➤ easy to modify to solve variations of the original task
➤ efficient2
117
When algorithms are expressed as computer programs, algorithms are encoded in meth-
ods. Stepwise refinement encourages us to write each method in terms of other methods
that implement one logical step in solving the problem. In this way, we can write pro-
grams that are more likely to be correct, simple to read, and easy to understand.
It may appear natural to define all the new classes and services needed for a task first,
and then write the program using these services. But how can we know what robots
and which new services are needed before we write the program? Stepwise refinement
tells us to first write the program using any robots and service names we desire, and
then define these robots and their services. That is, we write the main method first, and
then we write the definitions of the new services we used. Finally, we assemble the class
containing main and any new classes we wrote into a complete program.
We will explore this process more concretely by writing a program for the task shown
in Figure 3-1. The initial situation represents a harvesting task that requires one or
more robots to pick up a rectangular field of Things. The robot(s) may start and finish
wherever is most convenient.
(figure 3-1) 0 1 2 3 4 5 6 0 1 2 3 4 5 6
0 0
Harvesting task
1 1
2 2
3 3
4 4
5 5
6 6
7 7
The first step is to develop an overall plan to guide us in writing a robot program to
perform the given task. Planning is often best done as a group activity. Sharing ideas
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 118
118
CHAPTER 3 | DEVELOPING METHODS
in a group allows members to present different plans that can be thoughtfully exam-
ined for strengths and weaknesses. Even if we are working alone, we can think in a
question-and-answer pattern, such as the following:
Answer We could do it with one robot that walks back and forth over all of the
rows to be harvested, or we could do it with a team of robots, where each
robot picks some of the rows.
Answer Let’s try it with just one robot, named mark, for now. That seems simpler LOOKING AHEAD
than using several robots. In Section 3.5, we’ll
find that these are not
Question Where should mark start? well-founded
assumptions.
Answer Probably at one of the corners. Then it doesn’t need to go back to harvest
rows behind it. Let’s pick intersection (1, 0), facing the first row it will pick.
With these decisions made about how many robots to use and where to start, we can be
more definite about the initial situation. The revised version appears in Figure 3-2.
0 1 2 3 4 5 6 0 1 2 3 4 5 6 (figure 3-2)
0 0
Revised situations
1 1
2 2
3 3
4 4
5 5
6 6
7 7
Now that the initial situation is complete, we turn our attention to identifying the ser-
vices mark must offer.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 119
119
Answer Well, the initial situation doesn’t actually put mark in the field. We could
either adjust the initial situation so it starts at (1, 1) or simply call move
before it harvests the field. Other than that, harvestField seems to be
the only service required.
Once the services required have been identified, we can make use of them in writing the
main method. At this point, we won’t worry about the fact that they don’t exist yet.
We briefly move from planning to implementing our plan. We will call the new class of
robots Harvester and implement the main method in a class named HarvestTask.
Defining a city with 30 Things would clutter Listing 3-1 significantly. To avoid this,
the City class has a constructor, used in line 10, that can read a file to determine where
Things are positioned. The requirements for such a file are described in the online doc-
umentation for the City class.
120
CHAPTER 3 | DEVELOPING METHODS
We now know that the Harvester class must offer a service named harvestField
that harvests a field of things. As we develop this service, we will follow the same pat-
tern as before—asking ourselves questions about what it must do and what services we
want to use to implement the harvestField service.
publicƒvoidƒturnRight()
{ƒthis.turnAround();
ƒƒthis.turnLeft();
}
When we implemented turnRight, we noticed that turnAround, a method we had KEY IDEA
already written, would be useful. However, to implement harvestField, we are turn- Write a long or
ing that process around. We need to write a method, harvestField, and begin by complex method
using helper
asking which methods we need to help make writing harvestField easier. These
methods.
methods are called helper methods. We will write harvestField as if those methods
already existed. Helper methods are used frequently enough to qualify as a pattern.
Eventually, of course, we will have to write each of the helper methods. It may be that
we will have to follow the same technique for them as well: defining the helper meth-
ods in terms of other services that we wish we had. Each time we do so, the helper Helper Method
methods should be simpler than the method we are writing. Eventually, they will be
simple enough to be written without helper methods.
We must be realistic when imagining which helper methods would be useful to imple-
ment harvestField. Step 2 in Figure 3-3—“then a miracle occurs”—would not be
an appropriate helper method.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 121
121
Expert So, what does a Harvester robot need to do to pick a field of things?
Novice Harvest all the things in each of the rows of the field.
Novice It could move west to east across the northern-most unharvested row of
things, picking each thing as it moves.
KEY IDEA Novice At the end of each row, the robot could turn around and move back to the
Use a helper method western side of the field, move south one block, face east, and repeat the
when doing the actions listed earlier. It could do so for each row of things in the field. Since
same thing
the field has six rows, the robot needs to repeat the actions six times.
several times.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 122
122
CHAPTER 3 | DEVELOPING METHODS
Expert If you were to write this down in an abbreviated form, what would it look like?
Performing the actions in these nine lines would harvest the first three rows of
the field. They need to be repeated to harvest the last three rows.
Before we continue with this plan, we should analyze it, looking at its strengths and
weaknesses. Are we asking for the right helper methods? Are there other ways of
solving the problem that might work better? Our analysis might proceed as follows:
Novice The plan simplifies the harvestField method by defining three simpler meth-
ods, using each one several times.
Novice The same three lines are repeated over and over. Maybe we should have
harvestField defined as
and so on. The method to harvest one row could be defined using the helper
methods mentioned earlier.
Expert That’s easy enough to do. Any other weaknesses in this plan?
Novice The robot returns to the starting point on the row that was just harvested.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 123
123
Novice It seems like a better solution to have the robot doing productive work (as
opposed to just moving) in both directions. I know that if I were picking that
field personally, I’d look for every efficiency I could find!
Instead of harvesting only one row and then turning around and returning to
the start, the Harvester robot could pick all the things in one row, move
south one row, and come back to the west, harvesting a second row. It could
then move one row south to begin the entire process over for the next two
rows. If mark repeats these steps one more time, the entire field of things will
be harvested, as shown in Figure 3-4.
(figure 3-4) 0 1 2 3 4 5 6
0
Harvesting the field in two
directions 1
Again we analyze this new plan for its strengths and weaknesses.
Expert What advantage does this offer over the first plan?
Novice Now the robot makes only six trips across the field instead of 12. There are
no empty trips.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 124
124
CHAPTER 3 | DEVELOPING METHODS
Novice The robot harvests two rows at a time. If the field had an odd number of
rows, we would have to think of something else.
When we are planning solutions, we should be very critical and not just accept the first
plan as the best. We now have two different plans, and you can probably think of sev-
eral more. Let’s avoid the empty trips and implement the second plan.
Implementing harvestField
Let’s turn each of these statements into invocations of methods named harvestTwoRows
and positionForNextHarvest. We can then begin implementation of the Harvester
class and harvestField in particular, as shown in Listing 3-2. Helper Method
➤ Stubs serve as placeholders for work that must still be completed. The associ-
ated documentation records our ideas for what the methods should do, help-
ing to jog our memory when we come back to actually implement the
methods. In large programs with many methods, a span of days or even
months might elapse before you have a chance to complete the method. If you
are part of a team, perhaps someone else can implement the method based on
the stub and its documentation.
➤ A stub allows the program to be compiled even though it is not finished. When
we compile the program, the compiler may catch errors that are easier to find
and fix now rather than later. Waiting to compile until the entire program is
written may result in so many interrelated errors that debugging becomes very
difficult.
➤ A compiled program can be run, which may allow some early testing to be
performed that validates our ideas (or uncovers bugs that are easier to fix now
rather than later). We might run the program to verify that the initial situation
is correctly set up, for instance.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 125
125
We must now begin to think about planning the instructions harvestTwoRows and
positionForNextHarvest.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 126
126
CHAPTER 3 | DEVELOPING METHODS
Our plan contains two subtasks: one harvests two rows and the other positions the
robot to harvest two more rows. The planning of these two subtasks must be just as
thorough as the planning was for the overall task. Let’s begin with harvestTwoRows.
Novice harvestTwoRows must harvest two rows of things. One is harvested as the
Harvester robot travels east and the second is harvested as it returns to the west.
Novice It must pick things and move as it travels east. At the end of the row of things,
it must move south one block, face west, and return to the western edge of the
field, picking things as it travels west. In an abbreviated form, it must com-
plete the following tasks:
Novice Possibly one—we have two different instructions that harvest a single row
of things.
Novice We need one for going east and one for going west.
Novice Harvesting is just a series of pickThings and moves. The direction the robot
is moving does not matter. If we plan goToNextRow carefully, we can use one
instruction to harvest a row of things when going east and the same instruc-
tion for going west.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 127
127
Translating this idea to Java, we arrive at the following method and stubs, which
should be added to the code in Listing 3-2.
28 /** Harvest two rows of the field, returning to the same avenue but one street
29 * farther south. The robot must be facing east. */
30 publicƒvoidƒharvestTwoRows()
31 {ƒthis.harvestOneRow();
32 ƒƒthis.goToNextRow();
33 ƒƒthis.harvestOneRow();
34 }
35
36 /** Harvest one row of five things. */
37 publicƒvoidƒharvestOneRow()
38 {ƒ// incomplete
Helper Method
39 }
40
41 /** Go one row south and face west. The robot must be facing east. */
42 publicƒvoidƒgoToNextRow()
43 {ƒ// incomplete
44 }
This doesn’t look good! Every time we implement a method, we end up with even
more methods to implement. We now have three outstanding methods,
positionForNextHarvest, harvestOneRow, and goToNextRow, all needing to be
finished. Rest assured, however, that these methods are getting more and more specific.
Eventually, they will be implemented only in terms of already existing methods such as
move, turnLeft, and pickThing. Then the number of methods left to implement will
begin to decrease until we have completed the entire program.
KEY IDEA We have a choice of which of the three methods to refine next. One good strategy is to
Implement the choose the first uncompleted method we enter while tracing the program. This strategy
methods in allows us to run the program to verify that the work done thus far is correct. Applying
execution order. this strategy indicates that we should work on harvestOneRow next.
128
CHAPTER 3 | DEVELOPING METHODS
Novice Starting on the first thing and facing the correct direction, the robot must har-
vest each of the intersections that it encounters, stopping on the location of
the last thing in the row.
Novice It must harvest the intersection it’s on and then move to the next intersection.
It needs to do that five times, once for each thing in the row.
harvest an intersection
move
harvest an intersection
move
harvest an intersection
move
harvest an intersection
move
harvest an intersection
move
Expert Are you sure? It seems to me that it moves right out of the field.
Novice Right! The last time it doesn’t need to move to the next intersection. It can just
go to the next row of the field.
129
Novice It moves the Harvester robot south one block to the next row and faces it in
the opposite direction. I think we can implement this one without creating any
new helper methods, like this:
turn right
move
turn right
Novice It moves the Harvester robot south one block to the next row.
Expert Didn’t we do that already? Why can’t we use the instruction goToNextRow?
Novice The robot isn’t in the correct situation. When executing goToNextRow, the
robot is on the eastern edge of the field facing east. When it executes
positionForNextHarvest, it has just finished harvesting two rows and is
on the western edge of the field facing west.
Novice It must turn left, not right, to face south, move one block, and turn left again
to face east.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 130
130
CHAPTER 3 | DEVELOPING METHODS
/** Position the robot for the next harvest by moving one street south and facing west. */
publicƒvoidƒpositionForNextHarvest()
{ƒthis.turnLeft();
ƒƒthis.move();
ƒƒthis.turnLeft();
}
Because we have spread this class out over several pages, the complete program is
printed in Listing 3-3 so that you will find it easier to read and study.
1 importƒbecker.robots.*;
ch03/harvest/
2
3 /** A class of robot that can harvest a field of things. The field must be 5 things wide
4 * and 6 rows high.
5 *
6 * @author Byron Weber Becker */
7 publicƒclassƒHarvesterƒextendsƒRobotSE
8 {
9 ƒƒ/** Construct a new Harvester robot.
10 ƒƒ* @param aCity The city where the robot will be created.
11 ƒƒ* @param str The robot's initial street.
12 ƒƒ* @param ave The robot's initial avenue.
13 ƒƒ* @param dir The initial direction, one of Direction.{NORTH, SOUTH, EAST, WEST}. */
14 ƒƒpublicƒHarvester(CityƒaCity,ƒ
15 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒintƒstr,ƒintƒave,ƒDirectionƒdir)
16 ƒƒ{ƒsuper(aCity,ƒstr,ƒave,ƒdir);
17 ƒƒ}
18
19 ƒƒ/** Harvest a field of things. The robot is on the northwest corner of the field. */
20 ƒƒpublicƒvoidƒharvestField()
21 ƒƒ{ƒthis.harvestTwoRows();
22 ƒƒƒƒthis.positionForNextHarvest();
23 ƒƒƒƒthis.harvestTwoRows();
24 ƒƒƒƒthis.positionForNextHarvest();
25 ƒƒƒƒthis.harvestTwoRows();
26 ƒƒ}
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 131
131
27
28 ƒƒ/** Harvest two rows of the field, returning to the same avenue but one
29 ƒƒ* street farther south. The robot must be facing east. */
30 ƒƒpublicƒvoidƒharvestTwoRows()
31 ƒƒ{ƒthis.harvestOneRow();
32 ƒƒƒƒthis.goToNextRow();
33 ƒƒƒƒthis.harvestOneRow();
34 ƒƒ}
35
36 ƒƒ/** Harvest one row of five things. */
37 ƒƒpublicƒvoidƒharvestOneRow()
38 ƒƒ{ƒthis.harvestIntersekction();
39 ƒƒƒƒthis.move();
40 ƒƒƒƒthis.harvestIntersection();
41 ƒƒƒƒthis.move();
42 ƒƒƒƒthis.harvestIntersection();
43 ƒƒƒƒthis.move();
44 ƒƒƒƒthis.harvestIntersection();
45 ƒƒƒƒthis.move();
46 ƒƒƒƒthis.harvestIntersection();
47 ƒƒ}
48
49 ƒƒ/** Go one row south and face west. The robot must be facing east. */
50 ƒƒpublicƒvoidƒgoToNextRow()
51 ƒƒ{ƒthis.turnRight();
52 ƒƒƒƒthis.move();
53 ƒƒƒƒthis.turnRight();
54 ƒƒ}
55
56 ƒƒ/** Go one row south and face east. The robot must be facing west. */
57 ƒƒpublicƒvoidƒpositionForNextHarvest()
58 ƒƒ{ƒthis.turnLeft();
59 ƒƒƒƒthis.move();
60 ƒƒƒƒthis.turnLeft();
61 ƒƒ}
62
63 ƒƒ/** Harvest one intersection. */
64 ƒƒpublicƒvoidƒharvestIntersection()
65 ƒƒ{ƒthis.pickThing();
66 ƒƒ}
67 }
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 132
132
CHAPTER 3 | DEVELOPING METHODS
Stepwise refinement can be viewed as an approach to bridging the gap between the
method we need (harvestField) and the methods we already have (move,
pickThing, and so on). The methods we already have available are sometimes called
the primitives. For drawing a picture, the primitives include drawRect and drawLine.
Figure 3-5 shows the situation near the beginning of the design process. We know we
want a method to harvest a field and we know that robots can move, pick things up,
turn left, and so on. The question is, how do we bridge the gap between them?
Stepwise refinement helps fill in intermediate methods, as shown in Figure 3-6, in an
orderly manner to help solve the problem.
(figure 3-5)
harvestField( )
Gap between the
method we need and
the primitives we have
available
?
pickThing( ) move( ) turnRight( ) turnLeft( )
Design is best performed starting at the top of the diagram and working down. This
approach is often called top-down design. Stepwise refinement is simply another name
for top-down design.
(figure 3-6)
harvestField( )
Bridging the gap between
the method we need and
harvestTwoRows( ) positionForNextHarvest the primitives we have
available
Top-down
Bottom-up
harvestOneRow( ) goToNextRow ( )
harvestIntersection( )
133
3.3 ADVANTAGES
Sometimes we may have a flash of intuition and realize that harvesting one row would
be a useful step in harvesting a field and that such a method could be easily constructed
with the move and pickThing methods. When such an insight occurs before being
derived in a top-down design, it’s called bottom-up design. Bottom-up design happens
OF
within the context of top-down design.
STEPWISE REFINEMENT
It is also useful to make a distinction between top-down and bottom-up implementation.
A top-down design may be done only on paper using pseudocode or even a diagram such
as Figure 3-6. When actually writing the methods, we can start at the top and work
down (as we did in this section of the book) or we can start at the bottom and work up.
One advantage of the top-down approach is that it matches the design process. A signif-
icant advantage of the bottom-up approach is that methods can be implemented and
tested before the entire program is complete. Testing methods as they are written almost
always improves the correctness of the overall program.
All of these advantages follow from a few simple facts. First, as we noted in Section 1.1.1,
most people can only manage about seven pieces of information at once. By breaking each
problem into a small number of subproblems, the stepwise refinement technique helps us
avoid information overload.
Finally, by identifying each of these related parts (methods) with well-chosen names,
we can think at a higher level of abstraction; we can think about what the part does
rather than how it does it.
Writing understandable programs is as important as writing correct ones; some say that
it is even more important, since most programs initially have a few errors, and under-
standable programs are easier to debug. Successful programmers are distinguished from
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 134
134
CHAPTER 3 | DEVELOPING METHODS
ineffective ones by their ability to write clear and concise programs that someone else can
read and quickly understand. What makes a program easy to understand? We present
three criteria.
➤ Each method, including the main method, is composed of a few easily under-
stood statements, including method calls.
➤ Each method has a single, well-defined purpose, which is succinctly described
by the method’s name.
➤ Each method can be understood by examining the statements it contains and
understanding the purpose of the methods it calls. Understanding the method
should not depend on knowing how other methods work. It should only
depend upon the methods’ purposes.
Each of these criteria help limit the number of details a person must keep in mind at
one time.
If a method cannot correctly accomplish its purpose unless it begins in a certain situa-
tion, that fact should be documented. For example, an instruction directing a robot to
always pick something up should indicate in a comment where that thing must appear:
publicƒclassƒCollectorƒextendsƒRobot
{
ƒƒ/** Collects one thing from the next intersection. Breaks the robot if nothing is present. */
ƒƒpublicƒvoidƒcollectOneThing()
ƒƒ{ƒthis.move();ƒ
ƒƒƒƒthis.pickThing();ƒ
ƒƒ}
}
Many novices think that all of the planning, analyzing, tracing, and simulating of pro-
grams shown in the Harvester example take too much time. They would rather start
typing their programs into a computer immediately, without planning first.
What really takes time is correcting mistakes. These mistakes fall into two broad KEY IDEA
categories. A T-shirt slogan: Days
of programming can
The first category is planning mistakes. They result in execution and intent errors and save you hours of
happen when we write a program without an adequate plan. Planning mistakes can planning.
waste a lot of programming time. They are usually difficult to fix because large seg-
ments of the program may have to be modified or discarded. Careful planning and
thorough analysis of the plan can help avoid planning mistakes.
The second category is programming mistakes. They result in compile-time errors and
happen when we actually write the program. Programming mistakes can be spelling,
punctuation, or other similar errors. Compiling the program each time we complete a
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 135
135
3.3 ADVANTAGES
method helps find such errors so that they can be fixed. If we write the entire program
before compiling it, we will undoubtedly have many errors to correct, some of which
may be multiple instances of the same error. By using stubs and compiling often, we
can both reduce the overall number of errors introduced at any one time and help pre-
OF
vent multiple occurrences of the same mistake.
STEPWISE REFINEMENT
Stepwise refinement is a tool that allows us to plan, analyze, and implement our plans
in a way that should lead to a program containing a minimum of errors.
Removing programming errors is easier in a program that has been developed using
stepwise refinement. Removing errors has two components: identifying errors, and fix-
ing the errors. Stepwise refinement helps in both steps.
LOOKING AHEAD First, each method can be independently tested to identify errors that may be present.
In Section 7.1.1, we When writing a program, we should trace each method immediately after it is written
will learn to write until we are convinced that it is correct. Then we can forget how the method works
small programs
and just remember what it does. Remembering should be easy if we name the method
designed to test
single methods. accurately, which is easiest if the method does only one thing.
Errors that are found by examining a method independently are the easiest ones to fix
because the errors cannot have been caused by some other part of the program. When
testing an entire program at once, this assumption cannot be made. If methods have
not been tested independently, it is often the case that one has an error that does not
become obvious until other methods have executed—that is, the signs of an error can
first appear far from where the error actually occurs, making debugging difficult.
Second, stepwise refinement imposes a structure on our programs, and we can use
this structure to help us find bugs in a completed program. When debugging a pro-
gram, we should first determine which of the methods is malfunctioning. Then we
can concentrate on debugging that method, while ignoring the other parts of the pro-
gram, which are irrelevant to the bug. For example, suppose our robot makes a
wrong turn and tries to pick up a thing from the wrong place. Where is the error? If
we use helper methods to write our program, and each helper method performs one
specific task (such as positionForNextHarvest) or controls a set of related tasks
(such as harvestTwoRows), then we can usually determine the probable location of
the error easily and quickly.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 136
136
CHAPTER 3 | DEVELOPING METHODS
Programs are often modified because the task to perform has changed in some way or
there is an additional, related task to perform. Programs that have been developed using
stepwise refinement are easier to modify than those that are not for the following reasons:
➤ The structure imposed on the program by stepwise refinement makes it easier
to find the appropriate places to make modifications.
➤ Methods that have a single purpose and minimal, well-defined interactions
with the rest of the program can be modified with less chance of creating a bug
elsewhere in the program.
➤ Single-purpose methods can be overridden in subclasses to do something
slightly different.
We can illustrate these points with an example modifying the Harvester class. Figure 3-7
shows two situations that differ somewhat from the original harvesting task. In the first
one, each row has six things to harvest instead of just five. In the second, there are eight
rows instead of six.
Obviously, this problem is very similar to the original harvesting problem. It would be LOOKING AHEAD
much simpler to modify the Harvester program than to write a completely new program. Repetition and
parameters
How difficult would it be to modify the Harvester class to accomplish the new har- (Chapters 4 and 5)
vesting tasks? We have two different situations to consider. will help our code
adapt to variations
The first situation is one in which the original task has really changed, and it there- of the same problem.
fore makes sense to change the Harvester class itself. In this case, harvesting
longer rows can be easily accommodated by adding the following statements to the
harvestOneRow method:
this.move();
this.harvestIntersection();
A similar change to the harvestField method would solve the problem of harvesting
additional rows.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 137
137
3.3 ADVANTAGES
(figure 3-7) 0 1 2 3 4 5 6
0
Two variations of the
0 1 2 3 4 5 6
harvesting task 0 1
OF
1 2
STEPWISE REFINEMENT
2 3
3 4
4 5
5 6
6 7
7 8
Our use of stepwise refinement in developing the original program aids this change
tremendously. Stepwise refinement led us to logical subproblems. By naming them
appropriately, it was easy to find where to change the program and how to change it.
Furthermore, because the interactions between the methods were few and well defined,
we could make the changes without creating a bug elsewhere in the program.
A second situation to consider is where we still need to solve the original problem—
that is, it is inappropriate to change the original Harvester class, because it is still
needed. We can then use inheritance to solve the new problem. By overriding
harvestOneRow, we can make modifications to harvest longer rows, and by overrid-
ing harvestField, we can harvest more (or fewer) rows. A new robot class to harvest
longer rows is shown in Listing 3-4.
138
CHAPTER 3 | DEVELOPING METHODS
Listing 3-4: An extended version of Harvester that harvests longer rows (continued)
12
13 ƒƒ/** Override the harvestOneRow method to harvest the longer row. */
14 ƒƒpublicƒvoidƒharvestOneRow()
15 ƒƒ{ƒsuper.harvestOneRow();ƒƒƒƒƒƒƒƒƒ// harvest first 5 intersections
16 ƒƒƒƒthis.move();ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// harvest one more
17 ƒƒƒƒthis.harvestIntersection();
18 ƒƒ}
19 }
3.4 Pseudocode
Sometimes it is useful to focus more on the algorithm than on the program implementing
it. When we focus on the program, we also need to worry about many distracting details,
such as placing semicolons appropriately, using consistent spelling, and even coming up
with the names of methods. Those details can consume significant mental energy—
energy that we would rather put into thinking about how to solve the problem.
Pseudocode is a technique that allows us to focus on the algorithms. Pseudocode is a KEY IDEA
blending of the naturalness of our native language with the structure of a programming Pseudocode is a
language. It allows us to think about an algorithm much more carefully and accurately blend of natural and
than we would with only natural language, the language we use in everyday speech, but programming
languages.
without all the details of a full programming language such as Java. Think of it as your
own personal programming language.
We’ve been using pseudocode for a long time without saying much about it. When
planning our first program in Chapter 1, we presented the pseudocode for the algo-
rithm before we wrote the program:
Looking back for text set in this distinctive font, you’ll also see that we used
pseudocode in Chapter 2 when we developed the Lamp class and overrode turnLeft
to make a faster-turning robot. We’ve also used it extensively in this chapter.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 139
139
3.5 VARIATIONS
There are several advantages to using pseudocode:
➤ Pseudocode helps us think more abstractly. As we discussed briefly in
Section 1.1.1, abstractions allow us to “chunk” information together into
ON THE
higher level pieces so that we don’t need to remember as much. In this case,
pseudocode enables us to chunk together many lower-level steps into a single
THEME
higher-level step, such as pick all the things in one row. Such higher-
level thinking, however, comes at a cost: less precision. This lack of precision
may allow us to accidentally slip in a “miracle” (see the cartoon in Figure 3-3),
but overall, the benefits of using pseudocode outweigh the costs.
➤ Pseudocode allows us to simulate, or trace, our program very early in its
development. We can trace the program after only scratching out a few lines
on paper. If we find a bug, it is much easier to change and fix it than if we
had invested all the time and energy into obeying the many details of the
Java language.
➤ If we are working with other people, even nontechnical users, pseudocode can
provide a common language. With it, we can describe the algorithm to others.
They might see a special case we missed or a more efficient approach, or even
help implement it in a programming language.
➤ Algorithms expressed with pseudocode can be converted into any computer
programming language, not just Java.
Pseudocode’s usefulness increases as the complexity of the algorithm you are designing
increases. In the next chapter, we will introduce Java constructs that allow us to choose
whether to execute some statements. Other constructs allow us to repeat statements.
These constructs are very powerful and vital to writing interesting programs—but they
also add complexity, a complexity that pseudocode can help manage in the early stages
of programming.
140
CHAPTER 3 | DEVELOPING METHODS
One approach to solving the harvesting problem is to use several robots, which we
briefly considered early in the process. In this approach, each robot harvests only a
part of the field. For example, our main method could be modified to instantiate three
robots, each of which harvests two rows. The initial situation is shown in Figure 3-8
and in the program in Listing 3-5. mark will harvest the first two rows; lucy the mid-
dle two rows; and greg the last two rows. Of course, the work does not need to be
divided evenly. If there were only two robots, one could harvest two rows, and the
other could harvest four rows.
0 1 2 3 4 5 6 (figure 3-8)
0
Harvesting a field with
1 three robots each
harvesting two rows
2
Listing 3-5: The main method for harvesting a field with three robots
ch03/
1 importƒbecker.robots.*;
harvestWithThree/
2
3 /** Harvest a field of things using three robots.
4 *
5 * @author Byron Weber Becker */
6 publicƒclassƒHarvestTaskƒextendsƒObject
7 {
8 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
9 ƒƒ{
10 ƒƒƒƒCityƒstLouisƒ=ƒnewƒCity(“Field.txt”);
11 ƒƒƒƒHarvesterƒmarkƒ=ƒnewƒHarvester(
12 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒstLouis,ƒ1,ƒ0,ƒDirection.EAST);
13 ƒƒƒƒHarvesterƒlucyƒ=ƒnewƒHarvester(
14 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒstLouis,ƒ3,ƒ0,ƒDirection.EAST);
15 ƒƒƒƒHarvesterƒgregƒ=ƒnewƒHarvester(
16 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒstLouis,ƒ5,ƒ0,ƒDirection.EAST);
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 141
141
3.5 VARIATIONS
Listing 3-5: The main method for harvesting a field with three robots (continued)
17
ON THE
18 ƒƒƒƒmark.move();
19 ƒƒƒƒmark.harvestTwoRows();
THEME
20 ƒƒƒƒmark.move();
21
22 ƒƒƒƒlucy.move();
23 ƒƒƒƒlucy.harvestTwoRows();
24 ƒƒƒƒlucy.move();
25
26 ƒƒƒƒgreg.move();
27 ƒƒƒƒgreg.harvestTwoRows();
28 ƒƒƒƒgreg.move();
29 ƒƒ}
30 }
In fact, the original problem does not specify the number of robots to use, where they
start, or where they finish. Perhaps the simplest solution is to have six robots each har-
ch03/harvestWithSix/ vesting one row, and ending on the opposite side of the field. The initial and final situ-
ations are shown in Figure 3-9. If we had chosen this solution, the Harvester class
would have consisted of only harvestOneRow and harvestIntersection—much
simpler than what we actually implemented.
(figure 3-9) 0 1 2 3 4 5 6 0 1 2 3 4 5 6
0 0
Harvesting with six robots
1 1
2 2
3 3
4 4
5 5
6 6
7 7
142
CHAPTER 3 | DEVELOPING METHODS
In the previous example, which uses six robots, one robot finishes its entire row before
the next one begins to harvest its row. The entire task takes about six times as long as
harvesting a single row, even though we have six robots.
If we were paying a group of people an hourly wage to perform this task, we would be
pretty upset with this strategy. We would want them working simultaneously so that
the entire job is done in about the same amount of time it takes one person to harvest
one row.
In this section, we’ll explore how to make the robots (appear to) do their work simul-
taneously. This material is normally considered advanced, but robots provide a clear
introduction to these ideas, and it’s a fun way to stimulate your thinking about other
ways to do things. Check with your instructor to find out if he or she expects you to
know this material.
Example: ThreadedRowHarvester
When you have several robots working simultaneously, each robot must be self-contained.
The main method will start each robot, after which your robots will perform their tasks
independently. This approach implies that each robot must be instantiated from a subclass
of Robot, which “knows” what to do without further input from the program. We’ll call
this subclass ThreadedRowHarvester.
The instructions each robot should execute after it’s started are placed in a specially KEY IDEA
designated method named run in the ThreadedRowHarvester. The run method is The run method
free to call other methods to get the job done. In our case, we call the HarvestOneRow contains the
instructions
and move methods, as shown in the following method. The run method should be
the thread will
inserted in the ThreadedRowHarvester class. harvestOneRow is defined as in the execute.
Harvester class.
143
3.5 VARIATIONS
do. A thread is started with two statements, one to create a Thread object and one to
call its start method. For a robot named karel, use the following statements:
ThreadedRowHarvesterƒkarelƒ=ƒnewƒThreadedRowHarvester(...);
ON THE
...
ThreadƒkarelThreadƒ=ƒnewƒThread(karel);
ch03/
THEME
karelThread.start();
harvestWithSix
Threads/
The start method in the last statement invokes the run method, which contains the
instructions for the robot. For this strategy to work, the Thread class must be assured
that the ThreadedRowHarvester class actually has a run method. You do so by
adding implements Runnable to the line defining the class:
publicƒclassƒThreadedRowHarvesterƒextendsƒRobotƒ
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒimplementsƒRunnable
In this example, each thread performs identical tasks, which need not be the case. We
could, for instance, set up two threads with robots harvesting two rows each, and two
more threads with robots harvesting one row each.
About Threads
A thread starts a new flow of control. We learned in the Sequential Execution pattern
that each flow of control is a sequence of statements, one after the other, where each
statement finishes before the next one begins.
The main method begins execution in its own thread. As long as we don’t start any new
threads, execution proceeds one statement after another, as shown in Figure 3-10. This
figure supposes that we have two robots named mark and lucy. The main method first
calls mark.harvestOneRow(); and then lucy.harvestOneRow();. Between these
calls, many other statements are executed, one after the other.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 144
144
CHAPTER 3 | DEVELOPING METHODS
(figure 3-10)
this.move(); this.move();
this.pickThing(); this.pickThing();
this.move(); this.move();
this.pickThing(); this.pickThing();
this.move(); this.move();
When we have two or more flows of control, execution switches among them. The
statements within each flow of control still execute in order with respect to each other,
but statements from a different thread might execute between them. This concept is
illustrated in Figure 3-11.
The main method’s flow of control starts a thread for mark and then for lucy, rep-
resented by the light arrow between the two left-most boxes.
But now that we have three threads of control (one for main, one for mark, and one for
lucy), the execution switches between all three threads, as represented by the heavier
arrows. Execution switches among the threads so quickly that it appears that all the
robots are moving simultaneously, though they are not (unless you are fortunate enough
to have a computer with at least as many processors as the program has threads). The
computer’s operating system ensures that each thread runs at least a little bit before stop-
ping it and starting another thread. It also ensures that every thread is eventually run.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 145
145
3.5 VARIATIONS
(figure 3-11) main’s thread mark’s thread lucy’s thread
One possible flow of markThread.start();
control with three threads this.move();
ON THE
this.pickThing();
THEME
lucyThread.start();
this.move();
this.pickThing();
this.move();
thread terminates
this.pickThing();
this.move();
this.pickThing();
this.move();
this.move();
(and so on...)
(and so on...)
thread terminates
thread terminates
Notice that although execution switches among the threads, the statements within each
thread are still executed in the same order as before. The only difference is that state-
ments from another thread might be executed between the statements.
Complexities
This simple example glosses over some complexities. For instance, each robot’s task in
these examples is independent of the tasks performed by the other robots. If a seventh
robot collected all the things harvested by the first six robots, it would need a way to
wait for those robots to finish their task before starting.
In the next chapter, we will explore ways that programs can make decisions. For exam-
ple, a robot can check if a Thing is present on the intersection. Suppose mark is pro-
grammed to check for a Thing on the current intersection. If there is one, mark picks
it up; otherwise, mark goes on to the next intersection. But the check is in one program
statement and the call to pickThing is in another. lucy, running in another thread,
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 146
146
CHAPTER 3 | DEVELOPING METHODS
might come along and snatch the thing between those two statements. So the thing
mark thought was there disappears, and mark breaks when it executes pickThing.
In spite of these and other complexities, threads are a useful tool in many applications. LOOKING AHEAD
For example, animations run in their own threads. Many word processors figure out In Section 10.7, we’ll
page breaks in a separate thread so that the user can continue typing at the same time. learn how to use a
thread to perform
Printing usually has a separate thread so that the user can do other work instead of
animation in a user
waiting for a slow printer. Graphical user interfaces usually run in one or more threads interface.
so that they can continue to respond to the user even while the program is carrying out
a time-consuming command.
Suppose that instead of picking one thing from each intersection in the field, we want
to plant a thing at each intersection. Other alternatives include picking two things or
counting the total number of things in the field.
Each of these programs is similar to the harvesting task. In particular, the part that con- KEY IDEA
trols the movement of the robot over the field is the same for all of these problems; it is Think about variations
only the task at each intersection that differs. The original task of harvesting things is of the problem early in
the design.
only one example of a much more general problem: traversing a rectangular area and
performing a task at each intersection.
If we started with this view of the problem, we might design the program differently.
Instead of solving the harvesting problem directly, we could design a
TraverseAreaRobot that traverses a rectangular area. At each intersection, it calls a
method named visitIntersection that is defined to do nothing, as follows:
publicƒvoidƒvisitIntersection()
{
}
By overriding this method in different subclasses, we can create robots that harvest
each intersection or plant each intersection, and so on. A class diagram illustrating this
approach is shown in Figure 3-12.
It may seem strange to include a method like visitIntersection that does nothing.
However, this method must be present in TraverseAreaRobot because other meth-
ods in that class call it. On the other hand, we don’t know what to put in the method
because we don’t know if the task is harvesting or planting the field, and so we simply
leave it empty, ready to be overridden to perform the appropriate action.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 147
147
TraverseAreaRobot(...)
void traverseArea( )
void traverseTwoStreets( )
void traverseOneStreet( )
void goSouthWhenFacingEast( )
void goSouthWhenFacingWest( )
void visitIntersection( )
Harvester Planter
Harvester(...) Planter(...)
void visitIntersection( ) void visitIntersection( )
Of course, we could have solved the planting problem by extending the Harvester
class and overriding harvestIntersection. The approach shown in Figure 3-12 dif-
fers from that in two ways. The first difference is that we planned for various tasks to
occur at each intersection and named the methods accordingly. It is confusing to over-
ride a method named harvestIntersection so that it plants something instead of
harvesting.
The second difference is that the TraverseAreaRobot class deliberately does nothing
when it visits an intersection. Instead, visitIntersection serves as an intentional
Template Method point where subclasses can modify the behavior of traverseArea. In fact, the docu-
mentation for visitIntersection and traverseArea should explicitly describe
the possibilities of overriding the method. In a sense, traverseArea is a template for
a common activity, which is modified by overriding visitIntersection.
148
CHAPTER 3 | DEVELOPING METHODS
Recall that a client is an object that uses the services of another object, called the server.
The client uses the server’s services by invoking its corresponding method with the
Command Invocation pattern described in Section 1.7.3:
«objectReference».«methodName»(«parameterList»);
The client is the class that contains code, such as karel.move(), joe.traverseArea(),
or even this.goSouthWhenFacingEast(). In these cases, karel, joe, and this are
the «objectReference»s.
Java has a set of access modifiers that control which clients are allowed to invoke a
method. The access modifier is placed as the first keyword before the method signature.
So far, we have used the access modifier public, as in public void traverseArea(). KEY IDEA
The keyword public allows any client to access the method. Like a public telephone, any- Public methods may
one who comes by can use it. be invoked by
any client.
The access modifier private is at the other end of the scale. It says that no one except
clients who belong to the same class, may invoke the method, and that the method may not KEY IDEA
be overridden. Staying private is what we want for many helper methods. Private methods can
goSouthWhenFacingEast, for example, was designed to help traverseTwoStreets only be invoked by
methods defined in
do its work; it should not be called from outside of the class where it was declared. It
the same class.
should therefore be declared as follows:
privateƒvoidƒgoSouthWhenFacingEast()
A middle ground is to use the protected access modifier. Protected methods may be KEY IDEA
invoked from clients that are also subclasses. Like all methods, protected methods can Protected methods
also be invoked from within the class defining them. can be used from
subclasses.
Using protected on the traverseOneStreet and visitIntersection methods
would be appropriate. It would allow us to override and use those methods in a subclass
to traverse longer streets. We also did this in Section 3.3.4 when we overrode
harvestOneRow to harvest a longer row. This approach is shown in Listing 3-6 and
Listing 3-7. Listing 3-8 shows code that does not compile because it attempts to use
protected and private methods.
1 publicƒclassƒTraverseAreaRobotƒextendsƒRobotSE
2 {ƒpublicƒTraverseAreaRobot(...)ƒƒƒƒƒƒƒƒƒƒ{ƒƒƒƒƒ...ƒƒƒƒƒ}
3
4 ƒƒpublicƒvoidƒtraverseArea()ƒƒƒƒƒƒƒƒƒƒƒƒƒ{ƒƒƒƒƒ...ƒƒƒƒƒ}
5
6 ƒƒprivateƒvoidƒtraverseTwoStreets()ƒƒƒƒƒƒ{ƒƒƒƒƒ...ƒƒƒƒƒ}
7
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 149
149
8 ƒƒprotectedƒvoidƒtraverseOneStreet()ƒƒƒƒƒ{ƒƒƒƒƒ...ƒƒƒƒƒ}
9
10 ƒƒprivateƒvoidƒgoSouthWhenFacingEast()ƒƒƒ{ƒƒƒƒƒ...ƒƒƒƒƒ}
11
12 ƒƒprivateƒvoidƒgoSouthWhenFacingWest()ƒƒƒ{ƒƒƒƒƒ...ƒƒƒƒƒ}
13
14 ƒƒprotectedƒvoidƒvisitIntersection()ƒƒƒƒƒ{ƒƒƒƒƒ...ƒƒƒƒƒ}
15 }
1 publicƒclassƒTraverseWiderAreaRobotƒextendsƒTraverseAreaRobot
2 {ƒpublicƒTraverseWiderAreaRobot(...)ƒƒƒƒƒ{ƒƒƒƒƒ...ƒƒƒƒƒ}
3
4 ƒƒprotectedƒvoidƒtraverseOneStreet()
5 ƒƒ{ƒsuper.traverseOneStreet(); // traverse first 5 intersections
6 ƒƒƒƒthis.move(); // traverse one more
7 ƒƒƒƒthis.visitIntersection();
8 ƒƒ}
9 }
Listing 3-8: A program that fails to compile because it attempts to use private and
protected methods
1 publicƒclassƒDoesNotWorkƒ
2 {ƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
3 ƒƒ{ƒ...
4 ƒƒƒƒTraverseAreaRobotƒkarelƒ=ƒnewƒTraverseAreaRobot(...);
5 ƒƒƒƒ...
6 ƒƒƒƒkarel.traverseArea(); // works—method is public
7 ƒƒƒƒkarel.traverseTwoStreets(); // compile error
8 ƒƒƒƒ // traverseTwoStreets is private
9 ƒƒƒƒkarel.visitIntersection(); // compile error
10 ƒƒƒƒƒ // visitIntersection is protected
11 ƒ}
12 }
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 150
150
CHAPTER 3 | DEVELOPING METHODS
It is also possible to omit the access modifier. The result is called “package” access. It
restricts the use of the method to classes in the same package. The becker.robots
package sometimes uses package access to make services available within all classes in
the package that should not be available to students. For example, Robot actually has
a turnRight method (contrary to what you read in Section 1.2.3), but it has package
access, so most clients can’t use it. RobotSE, however, is in the same package and thus
has access to it. It makes turnRight publicly available with the following method,
which overrides turnRight with a less restrictive access modifier.
publicƒvoidƒturnRight()
{ƒsuper.turnRight();
}
As a rule of thumb, beginning programmers should declare methods as private KEY IDEA
except in the following cases: Declare methods to
➤ The method is specifically designed to be a public service. In this case, you be private unless
you have a specific
should declare it as public. reason to do
➤ The method is used only by a subclass. In this case, you should declare it as otherwise.
protected.
Access modifiers are often shown in class diagrams with the symbols +, #, and –. They
stand for public, protected, and private access, respectively. Figure 3-13 shows a
class diagram for the Harvester class that includes these symbols.
Harvester Planter
+Harvester(...) +Planter(...)
#void visitIntersection( ) #void visitIntersection( )
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 151
151
(figure 3-14)
Listing 3-9: The code to draw a single stick figure at a predetermined location
152
CHAPTER 3 | DEVELOPING METHODS
Listing 3-9: The code to draw a single stick figure at a predetermined location (continued)
28 ƒƒg.setColor(Color.BLACK);
29 ƒƒg.drawLine(90,ƒ180,ƒ90,ƒ270);
30 }
The best solution is one helper method that uses parameters to specify the location of
the figure. We have already made extensive use of parameters. For example, consider
the method calls in lines 17 to 29 of Listing 3-9. They each pass arguments to the
method’s parameters indicating the location and size of the shape to draw. We will use
the same strategy except that instead of drawing a simple oval or rectangle, our
method will draw an entire stick figure. We will use parameters only for the location of
the stick figure. Using such a helper method, the paintComponent method is simpli-
fied to the following:
1 /** Paint two stick figures
2 * @param g The graphics context to do the painting. */
3 publicƒvoidƒpaintComponent(Graphicsƒg)
4 {ƒsuper.paintComponent(g);
5 ƒƒthis.paintStickFig(g,ƒ0,ƒ0);
6 ƒƒthis.paintStickFig(g,ƒ182,ƒ0);ƒ
7 }
Line 5 causes a stick figure to be drawn with its upper-left corner placed at (0, 0)—that
is, the upper-left corner of the component. Figure 3-14 is annotated with this location.
Line 6 causes the second figure to be painted at (182, 0), or 182 pixels from the left and
0 pixels down from the top. This location is also noted in Figure 3-14. The value of
182 was picked because each stick figure is 180 pixels wide, plus two pixels for a tiny
gap between them.
Lines 5 and 6 also pass g, the Graphics object used for painting, as an argument
because paintStickFig will need it to draw the required shapes.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 153
153
To use arguments such as g, 182, and 0 inside our helper method, we need to declare
corresponding parameters. These should look familiar because we have been declaring
Parameterized parameters in Robot constructors since the beginning of Chapter 2. The first line of the
Method paintStickFig method should be:
privateƒvoidƒpaintStickFig(Graphicsƒg2,ƒintƒx,ƒintƒy)
The first part of this line, private void paintStickFig, is the same as our
Parameterless Command and Helper Method patterns.
LOOKING BACK Next come the three parameters. Each specifies a type and a name, and is separated from
Type was defined in the next parameter with a comma. Graphics is the name of a class and specifies that the
Section 1.3.1 as first argument to paintStickFig must be a reference to a Graphics object. This is
specifying a valid set similar to our Robot constructors. There, the first parameter has a type of City; conse-
of values for an
attribute. Here it
quently, we always pass a City object as the first argument. The next two parameters
specifies the set of must always be passed integer arguments because they are declared with int.
values for the
parameter. Inside the method, the values passed as arguments will be given the name of the corre-
sponding parameter. If the method is called with this.paintStickFig(g, 182,
0), then inside paintStickFig, every time we use the name x it will be interpreted as
182—the value passed to it.
With this background, we can rewrite the method to use the parameters to specify the
stick figure’s position. Each time we refer to an x or a y location in drawing the stick
figure, we add the appropriate x or y parameter. This action offsets the figure, as
shown in Figure 3-15. Adding two numbers together uses the plus sign, and if one of
the “numbers” happens to be a parameter, Java will use the number it represents (in
this case, the number passed to it as an argument). The revised code for
paintStickFig appears in Listing 3-10.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 154
154
CHAPTER 3 | DEVELOPING METHODS
(figure 3-15)
y Offsetting the location of
x the stick figure with the x
and y parameters
y + 150
x + 60
60
Consider line 36 to paint the rectangle used for the pants. In the original code, we wrote
g.fillRect(60, 150, 60, 120) to draw a rectangle 60 pixels from the left side and
150 pixels down from the top. The last two arguments specify that it should be 60 pixels
wide and 120 pixels high. In line 36, this is changed to g2.fillRect(x+60, y+150,
60, 120). Now the rectangle starts 60 pixels to the right of x. If x is passed 0, the pants
are painted 60 pixels from the left side of the panel. If x is passed 182, the pants are
painted 242 (182 + 60) pixels from the left side.
Listing 3-10: A component that paints two stick figures, one beside the other
ch03/stickFigure/
1 importƒjava.awt.*;ƒƒƒƒƒƒƒƒƒƒƒƒ// Graphics, Dimension, Color
2 importƒjavax.swing.*;ƒƒƒƒƒƒƒƒƒ// JComponent
3
4 publicƒclassƒStickFigurePairƒextendsƒJComponent
5 {
6 ƒƒpublicƒStickFigurePair()
7 ƒƒ{ƒsuperƒ();
8 ƒƒƒƒDimensionƒprefSizeƒ=ƒnewƒDimension(2*180+5,ƒ270);
9 ƒƒƒƒthis.setPreferredSize(prefSize);
10 ƒƒ}
11
12 ƒƒ/** Paint two stick figures
13 ƒƒ* @param g The graphics context to do the painting. */
14 ƒƒpublicƒvoidƒpaintComponent(Graphicsƒg)
15 ƒƒ{ƒsuper.paintComponent(g);
16 ƒƒƒƒthis.paintStickFig(g,ƒ0,ƒ0);
17 ƒƒƒƒthis.paintStickFig(g,ƒ182,ƒ0);ƒ
18 ƒƒ}
19
20 ƒƒ/** Paint one stick figure at the given location.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 155
155
3.8 PATTERNS
Listing 3-10: A component that paints two stick figures, one beside the other (continued)
Using a helper method helps keep the paintComponent method to a reasonable size.
By adding parameters to the helper method, we allow the method to be used more flex-
ibly with the result that we only need one helper method instead of two.
3.8 Patterns
This chapter introduced four patterns: Helper Method, Multiple Threads, Template
Method, and Parameterized Method.
Context: You have a long or complex method to implement. You want your code to be
easy to develop, test, and modify.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 156
156
CHAPTER 3 | DEVELOPING METHODS
Solution: Look for logical steps in the solution of the method. Put the code to solve this
step in a well-named helper method. For example, if the problem is for a robot to
travel in a square pattern, the problem could be decomposed like this:
publicƒvoidƒsquareMove()
{ƒthis.sideMove();
ƒƒthis.sideMove();
ƒƒthis.sideMove();
ƒƒthis.sideMove();
}
privateƒvoidƒsideMove()
{ƒthis.move();
ƒƒthis.move();
ƒƒthis.move();
ƒƒthis.turnLeft();
}
Of course, the problem may involve writing several different helper methods. Because
helper methods are usually not services the class provides, they should generally be
declared private or at least protected, depending on whether subclasses need to
access or override them.
Consequences: Long or complex methods are easier to read, develop, test, and modify
when you break them into smaller steps and use helper methods.
Related Patterns: This pattern is almost identical to the Parameterless Command pattern
and other method-related patterns we will see in future chapters. The difference is in the
intent: helper methods designate methods that exist to perform a piece of a larger opera-
tion whereas the Parameterless Command, for example, does not have that connotation.
Context: You have multiple objects such as robots that should appear to carry out their
tasks simultaneously.
Solution: Start each of the tasks in its own thread of control. This requires three tasks:
➤ Write a method named run. It contains code to execute in a thread.
➤ Implement the Runnable interface so that Java knows your class is set up to
run as a thread.
➤ Start the thread.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 157
157
3.8 PATTERNS
The first two steps are expressed in code according to the following template:
publicƒclassƒ«className»ƒextendsƒ«superclassName»
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒimplementsƒRunnable
{ƒ...
ƒƒpublicƒvoidƒrun()
ƒƒ{ƒ«statementsƒtoƒexecuteƒinsideƒaƒseparateƒthread»
ƒƒ}
}
The third step is often included in an instance of the Java Program pattern but can also
be used in other contexts.
publicƒclassƒ«programClassName»
{ƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
ƒƒ{ƒƒ...
ƒƒƒƒƒ«className»ƒ«runnableObject»ƒ=ƒnewƒ«className»(...);
ƒƒƒƒƒThreadƒ«threadName»ƒ=ƒnewƒThread(«runnableObject»);
ƒƒƒƒƒ«threadName».start();
ƒƒƒƒƒ...
ƒƒ}
}
The three lines to create the object, create the thread, and start the thread are repeated
as many times as there are threads.
Related Patterns: This pattern makes use of common patterns, such as the following:
➤ Java Program
➤ Extended Class
➤ Object Instantiation
➤ Method Invocation
➤ Sequential Execution
Context: You have a set of similar classes. Each has a method that does almost the
same thing as a corresponding method in the other classes, but not quite. You would
like to avoid duplicating the common code so that you only need to write it, debug it,
and maintain it once.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 158
158
CHAPTER 3 | DEVELOPING METHODS
Solution: The method that shares the similar code among classes is called the template LOOKING AHEAD
method. Write it using helper methods for the parts that are different from one version Methods can be
to another. However, instead of putting these helper methods in the same class as the declared abstract
instead. We’ll learn
template method, put them in subclasses. The subclasses provide the variations in the
more in Section 12.1.5.
code that are used to solve the different problems.
To compile the template method, you need to include empty methods with the same
names as the helper methods.
Consequences: Writing the common code once helps reduce the effort to write, debug,
and maintain it. By explicitly identifying where the differences occur and writing meth-
ods for them, it’s easier to add a new class that solves another variation of the same
problem.
On the other hand, needing to look in a different class for part of the solution to the
problem can be confusing.
Related Pattern: This pattern is a specialization of the Extended Class pattern where
specific methods are provided for the express purpose of being overridden.
Context: A method might do many variations of its task if it only had some informa-
tion from its client to say which variation to perform. The different variations are often
quantified—how many pixels over to paint a figure, how many times to turn a robot,
or how much money to deposit in a bank account.
Solution: Use one or more parameters to communicate information from the client to
the method. Use this information to control which of many possible variations of the
task to perform.
In general, the method will declare one or more parameters, each having a type and a
name. Consecutive pairs are separated with commas, as shown in the following template:
publicƒvoidƒ«methodName»(«paramType1»ƒ«paramName1»,
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ«paramType2»ƒ«paramName2»,
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ...
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ«paramTypeN»ƒ«paramNameN»)
{ƒ«listƒofƒstatements,ƒatƒleastƒsomeƒofƒwhichƒuseƒparamName»
}
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 159
159
«objectReference».«methodName»(«arg1»,ƒ«arg2»,ƒ...,ƒ«argN»);
where the type of each argument is compatible with the type of the corresponding
parameter.
Consequences: The method is much more flexible than a similar method written with-
out parameters. Parameters give opportunities to the client to influence how the
method carries out its task.
LOOKING AHEAD Related Patterns: The Parameterized Method pattern is a variation of the other fol-
This pattern will be lowing patterns:
discussed more fully
➤ Parameterless Command
in Chapters 4 and 6.
➤ Helper Method
Stepwise refinement naturally results in helper methods: methods that exist to help
another method by solving a subproblem. Helper methods are often private, but may
also be protected. When they are protected, subclasses can often be used to easily
solve variations of the same problem using the Template Method pattern.
160
CHAPTER 3 | DEVELOPING METHODS
so lved using
a lso be subproblems
may
to
probl em in
stepwise divides a
refinement understandability,
advanta avoiding errors,
help ges inclu
s de easier testing/debugging,
con
stru easier modification
ct
algorithms
ent
lem
imp
correct
must be
methods should be easy to understand,
are debug, modify
desi often
gne
d us
ing
can pseudocode uses natural
be language
mor
e fl
ha exib
ve le w
ith
parameters
public, access
private are examples of modifiers
Written Exercises
3.1 In Listing 3-3, all of the methods have public access. Is this appropriate?
Should any of them have a different access modifier? If so, which ones?
3.2 Examine problem 2.13 again, in which karel wrote the message “Hello”
using Things. What helper methods would you suggest?
3.3 Suppose the TraverseAreaRobot class is extended to create the Harvester
class, as described in Section 3.5.3. What happens if the method in Harvester
is misspelled vistIntersection?
3.4 Consider the choice of access modifiers for TraverseAreaRobot suggested in
Listing 3-6. Explain their effects on the creation of a subclass to harvest every
other row of the field.
Programming Exercises
3.5 If necessary, download the source code for the examples and find ch03/
debugging/. It contains two kinds of robots, both of which perform the same
task. However, MonolithicBot contains a single method named doit.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 161
161
Programming Projects
3.6 Rewrite the harvestField method using a different stepwise refinement. In
particular, move the robot over the field in a spiral pattern, as shown in
Figure 3-16.
a. Write pseudocode to solve the problem using this idea.
b. Analyze the solution for strengths and weaknesses.
c. Write a program implementing your solution.
(figure 3-16) 0 1 2 3 4 5 6
0
Harvesting in a spiral
1
3.7 Program a robotic synchronized swimming team. The team has four members
that begin their routine as shown in Figure 3-17 in the middle of the pool. Each
swimmer goes through the same motions: a small counter-clockwise square, a
large counter-clockwise square, turn around, a small clockwise square, and
finally a large counter-clockwise square. Each square leaves the swimmer in the
same position as when it started the square. Small squares involve moving once
on each side; for large squares, the swimmers move twice. Start each swimmer
in its own thread (see Section 3.5.2).
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 162
162
CHAPTER 3 | DEVELOPING METHODS
(figure 3-17)
3.8 karel sometimes works as a pinsetter in a bowling alley. Examine the initial and
final situations shown in Figure 3-18, and then complete the following tasks:
a. Develop pseudocode for two different refinements of a method named
setPins.
b. Analyze both solutions for strengths and weaknesses.
c. Write a program that implements one of your solutions.
0 1 2 3 4 5 0 1 2 3 4 5 (figure 3-18)
0 0
Initial and final situations
1 1 for setPins
2 2
3 3
4 4
5 5
6 6
3.9 The CEO of a highly successful local software company has a plus-shaped wall
in her garden, as shown in Figure 3-19. She would like to use robots to plant
one and only one Thing at each location around the wall. Robots will always
start with enough Things to finish their task (look in the documentation for a
constructor to specify how many Things a robot starts with).
a. Use a single robot to do the planting. It begins and ends at (0, 0).
b. Use a team of four robots. You may choose their beginning and ending
positions.
c. Use a team of eight robots. You may choose their beginning and ending
positions.
d. Use threads so that a team of robots plants the garden simultaneously.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 163
163
2 2
3 3
4 4
5 5
3.10 Spiderman has a new superhero rival: Spiderbot. Just like Spiderman,
Spiderbot can climb tall buildings, as shown in Figure 3-20. However,
Spiderbot must stay as close to a building as possible as it climbs, and it can’t
jump between buildings.
a. Write a SpiderBot class that has a climbBuilding method. Use it to
instruct Spiderbot to climb over the three buildings. Use a file to place the
walls of the buildings. Consult the online documentation for the City con-
structors for the file format.
b. Extend the City class to make CityBuilder. The CityBuilder class has
a method named placeBuilding that takes one parameter: the avenue
where the building should be placed. Use it to build the city.
(figure 3-20)
0 1 2 3 4 5
0
Series of skyscrapers for
Spiderbot to climb
1
3.11 Section 3.5.3 describes how to use TraverseAreaRobot as a template for classes
that do variations of the same task. Implement the TraverseAreaRobot class.
a. Extend TraverseAreaRobot to create a class named Harvester. Instances of
Harvester will pick one Thing from each intersection of the area traversed.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 164
164
CHAPTER 3 | DEVELOPING METHODS
0 1 2 3 4 5 (figure 3-21)
0
King Java’s castle
1
3.13 King Java’s neighbor, King Caffeine, is impressed with the GuardBots devel-
oped in Problem 3.12. He wants to hire four GuardBots to patrol his castle.
However, his castle is larger, as shown in Figure 3-22.
a. Refer to the class diagram in Figure 3-12, which discusses the Template
Method pattern. Adapt it to this problem, showing the relationships and
methods needed for three classes: GuardBotTemplate, LongWallGuard,
and ShortWallGuard.
b. Using the Template Method pattern, develop three classes named
GuardBotTemplate, LongWallGuard, and ShortWallGuard. Write a
main method that creates castles for both King Caffeine and King Java and
then uses four LongWallGuards to patrol King Caffeine’s castle and four
ShortWallGuards to patrol King Java’s castle.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 165
165
3.14 Create a program that draws four copies of the Olympic rings, one in each cor-
ner of the component. The colors of the five rings, from left to right, are blue,
yellow, black, green, and red. The rings may simply overlap rather than inter-
lock, as in the official symbol.
3 Chapter C5743 40143.ps 11/30/06 1:18 PM Page 166
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 167
Chapter Objectives
After studying this chapter, you should be able to:
➤ Use an if statement to perform an action once or not at all.
➤ Use a while statement to perform an action zero or more times.
➤ Use an if-else statement to perform either one action or another action.
➤ Describe what conditions can be tested and how to write new tests.
➤ Write a method, called a predicate, that can be used in the test of an if or
while statement.
➤ Use parameters to communicate values from the client to be used in the execution
of a method.
➤ Use a while statement to perform an action a specified number of times.
In the preceding chapters, a robot’s exact initial situation was known at the start of a
task. When we wrote our programs, this information allowed robots to find things
and avoid running into walls. However, these programs worked only in their specific
initial situations. If a robot tried to execute one of these programs in a slightly differ-
ent initial situation, the robot would almost certainly fail to perform the task.
To address this situation, a robot must make decisions about what to do next. Should it
move or should it pick something up? In this chapter we will learn about programming
language statements that test the program’s current state and choose the next statement
to execute based on what they find. One form of this capability is the if statement: If
something is true, then execute a group of statements. If it is not true, then skip the group
of statements. Another form of this capability is the while statement: while something is
true, execute a group of statements.
167
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 168
168
CHAPTER 4 | MAKING DECISIONS
The if and while statements are different. As the program is running, they can ask a KEY IDEA
question. Based on the answer, they choose the next statement or group of statements if and while
to execute. In a robot program, the question asked might be, “Is the robot’s front statements choose the
next statement to
blocked by a wall?” or “Is there something on this intersection the robot can pick up?”
execute by asking a
In the concert hall program from Chapter 1, questions asked by an if or while state- yes/no question.
ment might include “Is the ticket for seat 22H still available?” or “Have all of the sold
tickets been processed yet?”
All of these questions have “yes” or “no” answers. In fact, if and while statements KEY IDEA
can only ask yes/no questions. Java uses the keyword true for “yes” and false for true means “yes”
“no.” These keywords represent Boolean values, just like the numbers 0 and 23 repre- and false
sent integer values. means “no.”
When the simplest form of an if statement asks a question and the answer is true, it
executes a group of statements once and then continues with the rest of the program. If
the answer to the question is false, that group of statements is not executed.
When a while statement asks a question and the answer is true, it executes a group
of statements (just like the if statement). However, instead of continuing with the rest
of the program, the while statement asks the question again. If the answer is still
true, that same group of statements is executed again. This continues until the answer
to the question is false.
The if statement’s question is “Should I execute this group of statements once?” The KEY IDEA
while statement’s question is “Should I execute this group of statements again?” This if statements
ability to ask a question and to respond differently based on the answer liberates our execute code once or
not at all. while
programs from always executing the same sequence of statements in exactly the order
statements might
given. For example, these two statements will allow us to generalize the Harvester execute the
class shown in Listing 3-3 in the following ways: statements
➤ Harvest any number of things from the intersection. repeatedly.
➤ Have a single goToNextRow method that works at both ends of the row. The
current solution has one method for the east end of the row and another
method for the west end.
➤ Harvest fields of varying sizes.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 169
169
One way to illustrate the flow of control through the if and while statements is with
a flowchart, as shown in Figure 4-1. The diamond represents the question that is
asked. The box represents the statements that are optionally executed. The arrows
show what the computer does next.
By tracing the arrows in the flowchart for if, you can easily verify that the statements
OF
in the box are executed once or not at all. Tracing the arrows for the while statement,
DECISIONS
however, shows that you can reach the statements in the box over and over again—or
that they might not be executed at all.
(figure 4-1)
KEY IDEA The key question you should ask when deciding whether to use an if statement or a
Decide between if while statement is “How many times should this code execute?” If the answer is once
and while by asking or not at all, choose the if statement. If the answer is zero or more times, then choose
how many times the
the while statement.
code should execute.
Suppose that a robot, karel, is in a city that has walls. If karel’s path is clear, it
should move and then turn left. Otherwise, karel should just turn left.
You can use an if statement to have karel make this decision. The if statement’s
question is “Is karel’s front clear of obstructions?” If the answer is true (yes), karel
should move forward and then turn left. If the answer is false (no), karel should
skip the move instruction and just turn left. This program fragment1 is written like this:
ifƒ(karel.frontIsClear())
{ƒkarel.move();
}
Once or Not at All
karel.turnLeft();
1 To conserve space, we will often demonstrate a programming idea without writing a complete pro-
gram or even a complete method. Instead, we will write only the necessary statements, which are
called a program fragment.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 170
170
CHAPTER 4 | MAKING DECISIONS
Consider two different initial situations. In Figure 4-2, the answer to the if statement’s
question is “Yes, karel’s front is clear of obstructions.” As a result, karel performs
the test, moves, and then turns left. These three actions are shown in the figure, where
the heavy arrows show the statements that are executed to produce the situation
shown on the right.
(figure 4-2)
if (karel.frontIsClear()) Execution of an if
{ karel.move(); statement when the
} robot’s front is initially
karel.turnLeft(); clear of obstructions
if (karel.frontIsClear())
{ karel.move();
}
karel.turnLeft();
if (karel.frontIsClear())
{ karel.move();
}
karel.turnLeft();
Suppose karel starts in the situation shown in Figure 4-3. Then the answer to the if KEY IDEA
statement’s question is “No, karel’s front is not clear of obstructions” and the state- The if statement
ment instructing karel to move is not executed. karel does not move, although it causes the robot to
does turn left because the turnLeft command is outside the group of statements con- behave differently,
depending on its
trolled by the if statement. situation.
(figure 4-3)
if (karel.frontIsClear()) Execution of an if
{ karel.move(); statement when the
} robot’s front is initially
karel.turnLeft(); obstructed
if (karel.frontIsClear())
{ karel.move();
}
karel.turnLeft();
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 171
171
karel.move();
karel.turnLeft();
In the first situation (shown in Figure 4-2), the result would be the same. However in
the second situation, karel would crash into the wall and break.
Use an if statement when you want statements to execute once or not at all.
OF
DECISIONS
4.1.3 Examining a while Statement
Let’s now consider a similar situation but control the move instruction with a while
statement:
whileƒ(karel.frontIsClear())
{ƒkarel.move();
}
Zero or More Times
karel.turnLeft();
KEY IDEA Recall that a while statement also asks a question. If the answer is true, the statements
The while statement inside the braces are executed and then the question is asked again. This continues until
repeatedly asks a the answer to the question is false. In the preceding code fragment, the question is “Is
question and performs
karel’s front clear of obstructions?”
an action until the
answer is “no.”
Let’s again consider karel in different initial situations. In Figure 4-4, karel’s front
is clear and the answer to the while statement’s question is “it is true, karel’s
front is clear.” karel moves and asks the question again—until the answer to the
question is finally false. The heavy arrows in the code show the statements that are
executed to reach the situation shown to the right of the code.
In this example, karel moves as many times as necessary to reach the wall. Then it
turns. In the situation shown in Figure 4-4, the wall happens to be only two intersec-
tions away. It could be 20 or 2 million intersections away—karel would still move to
the wall and then turn left with those same four lines of code.
If karel starts in a situation where its front is blocked, the answer to the question is imme-
diately false and the move does not occur. Execution continues with the turnLeft
instruction after the while statement. This situation is illustrated in Figure 4-5. Notice the
similarities to the last two illustrations in Figure 4-4.
The while statement’s test is always false after the statement finishes executing
because the loop continues until the test becomes false. In fact, if nothing inside the
while statement can make the test false, the statement will execute indefinitely.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 172
172
CHAPTER 4 | MAKING DECISIONS
(figure 4-4)
while (karel.frontIsClear())
{ karel.move();
}
karel.turnLeft();
while (karel.frontIsClear())
{ karel.move();
}
karel.turnLeft();
while (karel.frontIsClear())
{ karel.move();
}
karel.turnLeft();
(figure 4-5)
while (karel.frontIsClear())
{ karel.move();
}
karel.turnLeft();
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 173
173
publicƒvoidƒgotoWall()
{ƒwhileƒ(this.frontIsClear())
ƒƒ{ƒthis.move();
ƒƒ}
}
OF
DECISIONS
4.1.4 The General Forms of the if and while Statements
The general form of a statement marks the parts that can change, depending on the
needs of the situation, leaving the parts that are always the same clearly identified.
ifƒ(«test»)
{ƒ«listƒofƒstatements»
}
The reserved word if signals the reader of the program that an if statement is present.
The braces ({ and }) enclose a list of one or more statements, «listƒofƒstatements».
These statements are known as the then-clause. The statements in the then-clause are
indented to emphasize that «listƒofƒstatements»ƒis a component of the if state-
ment. Note that we do not follow the right brace of an if statement with a semicolon.
KEY IDEA The «test» is a Boolean expression such as a query that controls whether the state-
Boolean expressions ments in the then-clause are executed. A Boolean expression always asks a question
ask true/false that has either true or false as an answer.
questions.
whileƒ(«test»)ƒ
{ƒ«listƒofƒstatements»
}
The reserved word while starts this statement. Like the if statement, the «test» is
enclosed by parentheses and the «listƒofƒstatements» is enclosed by braces2. The
2 If the list of statements has only one statement, the braces can be omitted. More about this in
Section 5.6.3.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 174
174
CHAPTER 4 | MAKING DECISIONS
list of statements is called the body of the statement. The Boolean expressions that can
replace «test» are the same ones used in the if statements.
A statement that repeats an action, like a while statement, is often called a loop.
The if and while statements have similar syntax. That is, their structure, or the way
they look, is similar. On the other hand, they have different semantics. That is, the way
they behave is different. The if statement decides whether to execute a list of state-
ments or to skip over them. The while statement decides how many times to execute
a list of statements.
In Chapter 1, we briefly mentioned that one kind of service objects can provide is a
query—a service that answers a question. Robots offer queries that answer questions such
as “Which avenue are you on?”, “Which direction are you facing?”, “Can you pick up a
Thing from the intersection you are currently on?”, and “Is your front clear of obstruc-
tions?” The following class diagram, displayed in Figure 4-6, shows many of the queries
robots can answer.
(figure 4-6)
Robot
int street Class diagram showing
int avenue many of the queries a
Direction direction robot can answer
ThingBag backpack
+Robot(City aCity, int aStreet, int anAvenue,
Direction aDirection)
+boolean canPickThing( )
+int countThingsInBackpack( )
+boolean frontIsClear( )
+int getAvenue( )
+Direction getDirection( )
+String getLabel( )
+double getSpeed( )
+int getStreet( )
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 175
175
None of these queries change the state of the robot. The robot doesn’t change in any
way; it merely reports a piece of information about itself or its environment. This
information is used in expressions. Expressions may be used in many ways, such as
controlling if and while statements, passed as a parameter to a method, or saved in a
variable. In this chapter, we will focus almost exclusively on expressions used to con-
trol if and while statements.
Sometimes we want a robot to do something when a test is not true, as in the follow-
ing pseudocode:
The Robot class does not provide a predicate for testing if the robot cannot pick up a
Thing, only if it can.
KEY IDEA Fortunately, any Boolean expression may be negated, or given the opposite value, by
Give a boolean using the logical negation operator, “!”. In English, this is usually written and pro-
expression the nounced as “not”. The negation operator is placed immediately before the Boolean
opposite value
expression that is to be negated. Thus, the previous pseudocode could be coded as
with "!"
follows:
ifƒ(!karel.canPickThing())
{ƒkarel.putThing();
}
Once or Not at All
Negation is our first exploration of evaluating expressions. You already have experience
evaluating expressions from studying arithmetic. When you figure out that 5 + 3 * 2 is the
same as 5 + 6 or 11, you are evaluating an arithmetic expression. The expression often
includes an unknown, such as 5 + x * 2. When you know the value of x, you can substi-
tute it into the expression before evaluating it. For example, if x has the value 4, then the
expression 5 + x * 2 is the same as 5 + 4 * 2 or 13.
3 Boolean values are named after George Boole, one of the early developers of logic.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 176
176
CHAPTER 4 | MAKING DECISIONS
Evaluating a Boolean expression, an expression that uses values of true and false, is LOOKING AHEAD
similar to evaluating arithmetic expressions. The expression !karel.canPickThing() In Section 5.4.1, we
involves an unknown (karel.canPickThing()), similar to x in the arithmetic expres- will look at combining
expressions with
sion. Suppose the unknown has the value true (that is, karel is on the same intersection
“and” and “or”, much
as a Thing it can pick up). Then the expression evaluates to !true (“not true”) which is like “+” and “*”
the same as false. combine arithmetic
expressions.
The if and while statements always ask true-or-false questions. “Should I execute
this code, true or false?” This approach works well for queries that return a
boolean value, but how can we use queries that return integers? The solution is to
compare the query’s answer to another integer. For example, we could use the follow-
ing code to ask if the robot is on 1st Street:
ifƒ(karel.getStreet()ƒ==ƒ1)
{ƒ// what to do if karel is on 1st street
}
Once or Not at All
We could also use the following loop to make sure karel has at least eight things in its
backpack:
whileƒ(karel.countThingsInBackpack()ƒ<ƒ8)
{ƒkarel.pickThing();
}
Zero or More Times
A total of six comparison operators can be used to compare integers. They are shown in
Table 4-1.
< less than karel.getAvenue()ƒ<ƒ5 Evaluates to true if karel’s Java comparison operators
current avenue is strictly less
than 5; otherwise, evaluates
to false.
177
The examples in Table 4-1 always show an integer on only one side of the comparison
operator. Java is much more flexible than this, however. For example, it can have a
query on both sides of the operator, as in the following statements:
ifƒ(karel.getAvenue()ƒ==ƒkarel.getStreet())ƒ
{ƒ...
}
This test determines whether karel is on the diagonal line of intersections (0, 0), (1, 1),
(2, 2), and so on. It also includes intersections with negative numbers such as (-3, -3).
Java also allows a more complex arithmetic expression on either side. The following
code tests whether the robot’s avenue is five more than the street. Locations where this
test returns true include (0, 5) and (1, 6).
ifƒ(karel.getAvenue()ƒ==ƒkarel.getStreet()ƒ+ƒ5)ƒ
{ƒ...
}
KEY IDEA One common error is writing = instead of ==. The assignment statement, such as
Assignment (=) is Robotƒkarelƒ=ƒnewƒRobot(…) uses a single equal sign. Comparing integers, on the
not the same as other hand, uses two equal signs. Fortunately, Java usually catches this error and issues
equality (==). a compile-time error. Some other languages, such as C and C++, do not.
We again use the dialogue format introduced in Chapter 3 to reveal the thinking that
leads to the final solution.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 178
178
CHAPTER 4 | MAKING DECISIONS
Instead of harvesting a field, consider planting a field. Such a robot class, PlanterBot,
is the same as Listing 3-3 except for renaming the methods to use “plant” instead of
“harvest” and, in the plantIntersection method, using putThing instead of
pickThing.
Now, consider a minor variation: someone has already planted some intersections, but
not all. Our PlanterBot, karel, must go through the field and put a Thing on only
those intersections that don’t already have one. The initial and final situations are
shown in Figure 4-7. Of course, karel must either be created with a supply of Thing
objects in its backpack (see the documentation for an alternate constructor) or pick up
a supply before it starts.
0 1 2 3 4 5 6 0 1 2 3 4 5 6 (figure 4-7)
0 0
Initial and final situations
1 1 for planting intersections
that don’t have a Thing
2 2
3 3
4 4
5 5
6 6
7 7
Novice It must traverse the entire field, as before. Each time it comes to an intersection
it must ensure that the intersection has a Thing before the PlanterBot leaves.
Expert Does the PlanterBot perform its actions once or not at all? Or does it per-
form them zero or more times?
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 179
179
Novice I had a feeling that was coming…. Performing an action once or not at all uses
an if statement, as follows:
Expert How can you express the test for the if statement in Java?
Novice We haven’t seen a test for the absence of a Thing on an intersection. The clos-
est test we’ve studied is canPickThing—can the robot pick up a thing from
this intersection. If it can, there must be a Thing present. If it can’t, there isn’t
a Thing present.
I think the test we want is ifƒ(not a thing that can be picked up). “Not”, in
Java, is written with an exclamation point. Therefore, we want
!this.canPickThing.
The definition of PlantThing follows the pseudocode closely and is shown in Listing 4-1.
Suppose that instead of a single Thing, each of the field’s intersections may have many
Things. The original program in Listing 3-3 has a method, harvestIntersection,
declared as follows:
180
CHAPTER 4 | MAKING DECISIONS
In the revised version of the program, we want this method to pick up all of the Things
on the intersection.
Novice Pick up all the Thing objects that are on the same intersection as itself.
Expert Does the robot always perform the same actions to pick up all the Things?
Novice No. Its actions depend on how many Things are on the intersection. It must
use a test to decide what to do.
Expert Is the decision to do the action once or not at all? Or is the decision to repeat
the action zero or more times?
Novice The robot should repeat an action (picking up a Thing) zero or more times—
until there is nothing left to pick up.
Novice Sure:
This pseudocode can be expressed in Java and placed in a revised version of the
harvestIntersection method as shown in Listing 4-2.
Listing 4-2: A version of harvestIntersection that harvests all the Things there
ch04/harvest/
1 /** Harvest one intersection. */
2 publicƒvoidƒharvestIntersection()
3 {ƒwhileƒ(this.canPickThing())
4 ƒƒ{ƒthis.pickThing();
5 ƒƒ}
6 }
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 181
181
The original Harvester class has two methods moving the robot one street south.
One, goToNextRow, is used on the east side of the field. The other, positionFor-
NextHarvest, is used on the west side of the field. The existing definitions of these
two methods are as follows:
It would be preferable to have a single method that will work correctly at either end of the
row. Now, the distinction between goToNextRow and positionForNextHarvest is not
clear from the names of the methods. It would be easier for people reading and writing the
code to have only one descriptive name like goToNextRow.
Novice When it is at the east end of the row, it must turn right to move to the next
row. When it is at the west end it must turn left.
Expert So the robot must decide if it is at the east end of the row or the west end and
the action it carries out is to turn. Is the action performed once or not at all, or
is it performed zero or more times?
Novice The method is called many times—once at the end of each row. So in that
sense the action is performed many times.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 182
182
CHAPTER 4 | MAKING DECISIONS
Expert Hmm…. That’s not what I had in mind. Let’s focus on only one invocation of
goToNextRow. The robot is at the end of one particular row and needs to per-
form an action. Is that action performed once or not at all or is it performed zero
or more times?
Novice It’s once or not at all. It performs a group of actions (turn right, move, turn
right) once if it is at the east end of the row and not at all if it isn’t. Similarly, it
performs a group of actions once if it is at the west end and not at all if it isn’t.
Novice It’s the if statement that performs an action once or not at all. But I’m con-
fused, because it isn’t a single test. We need one test for the east end of the row
and another test for the west end of the row.
Expert Perhaps it’s not only two tests we need, but two complete if statements.
Expert Exactly. Now, how can you determine if the robot is at the east end of the row?
Novice Looking at Figure 3-2, the east end of the row is on Avenue 5 and the west end
of the row is on Avenue 1. In the first if statement, we can compare
this.getAvenue to 5 and in the second if statement we can compare
this.getAvenue to 1.
This pseudocode and the insight into the tests can be turned into the required Java
method, as shown in Listing 4-3.
Listing 4-3: A revised version of goToNextRow that will work at either end of the row.
ch04/harvest/
1 /** Go one row south. The robot must be on either Avenue 1 or Avenue 5. */
2 publicƒvoidƒgoToNextRow() LOOKING AHEAD
3 {ƒifƒ(this.getAvenue()ƒ==ƒ5) // at the east end of the row Written Exercise 4.3
4 ƒƒ{ƒthis.turnRight(); focuses on this
5 ƒƒƒƒthis.move(); method.
6 ƒƒƒƒthis.turnRight();
7 ƒƒ}
8 ƒƒifƒ(this.getAvenue()ƒ==ƒ1) // at the west end of the row
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 183
183
4.4 USING
Listing 4-3: A revised version of goToNextRow that will work at either end of the row. (continued)
THE IF-ELSE
9 ƒƒ{ƒthis.turnLeft();
10 ƒƒƒƒthis.move();
11 ƒƒƒƒthis.turnLeft();
STATEMENT
12 ƒƒ}
13 ƒ}
The form of the if-else statement is similar to the if statement, except that it
includes the keyword else, «statementList2» and another set of braces. Note the
absence of a semicolon before the word else and at the end.
An if-else is executed in much the same manner as an if. First, the «test» is eval-
uated to determine whether it is true or false in the current situation. If the «test»
is true, «statementList1» is executed; if the test is false, «statementList2» is
executed. Thus, depending on the current situation, either «statementList1»ƒor
«statementList2» is executed, but not both. The first statement list is called the
then-clause, just like an if statement. The second statement list is called the else-clause.
When the else-clause is empty, the if-else statement behaves just like the if state-
ment. In fact, the if statement is just a special case of the if-else statement.
(figure 4-8)
Flowchart for an
if-else statement
false
? true
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 184
184
CHAPTER 4 | MAKING DECISIONS
Let’s look at an example that uses the if-else statement. Suppose that we want to
program a Racer robot to run a hurdle race, where vertical wall sections represent
hurdles. The hurdles are only one block high and are randomly placed between any
two intersections in the race course. The finish line is marked with a Thing. One of the
many possible race courses for this task is illustrated in Figure 4-9. Figure 4-10 shows
the final situation and the path the robot should take for this particular race course.
Here we think of the city as being vertical, with down being south. To run the fastest
race possible, we require the robot to jump if, and only if, it is faced with a hurdle.
(figure 4-9)
Hurdle-jumping robot’s
initial situation
(figure 4-10)
Hurdle-jumping robot’s
final situation and the
path it took
We will assume that a stepwise refinement process is being used and that the Racer class
is partially developed, as shown in Listing 4-4. We need to continue the process by devel-
oping the raceStride method. It should move the robot forward by one intersection.
1 importƒbecker.robots.*;
2
3 /** A class of robots that runs a hurdles race (steeplechase).
4 *
5 * @author Byron Weber Becker */
6 publicƒclassƒRacerƒextendsƒRobotSE
7 {
8 ƒƒ/** Construct a new hurdle-racing robot. */
9 ƒƒpublicƒRacer(CityƒaCity,ƒintƒstr,ƒintƒave,ƒDirectionƒaDir)
10 ƒƒ{ƒsuper(aCity,ƒaStreet,ƒanAvenue,ƒaDir);
11 ƒƒ}
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 185
185
4.4 USING
Listing 4-4: A partially developed implementation of Racer (continued)
THE IF-ELSE
12
13 ƒƒ/** Run the race by repeatedly taking a raceStride until the finish line is crossed. */
14 ƒƒpublicƒvoidƒrunRace()
STATEMENT
15 ƒƒ{ƒwhileƒ(!this.canPickThing())
16 ƒƒƒƒ{ƒthis.raceStride();
17 ƒƒƒƒ}
18 ƒƒ}
19 }
We could easily develop a class of robots that run this race by jumping between every
pair of intersections. Although this strategy is simple to program, it doesn’t meet the
requirements of running the fastest race possible. Instead, we must program the robot
to move straight ahead when it can, and jump over hurdles only when it must.
Expert So, assume the Racer is on an intersection of the racetrack and ready to take
its next stride. What should it do?
Novice No, they depend on the situation. If there is a hurdle, it needs to be jumped. If
there isn’t a hurdle, the Racer can just move.
Expert You seem to be thinking that the robot should always move. The only ques-
tion is whether it jumps a hurdle first. Have you considered what would hap-
pen if there are two consecutive hurdles? The first pair of hurdles in Figure 4-9
shows that kind of a situation.
Novice Well, it would jump the first hurdle, landing right before the second one. Then
it would move… and crash into the hurdle. I guess we need a different plan.
186
CHAPTER 4 | MAKING DECISIONS
Novice It should either jump the hurdle or move (but not both), depending on
whether it is facing a hurdle. In pseudocode,
ifƒ(facing a hurdle)
{ƒjump the hurdle
}ƒelse
{ƒmove
}
Putting these ideas into a method results in the following code. It should be added to
Listing 4-4. The jumpHurdle method can be developed using the stepwise refinement
techniques found in Section 3.2.
publicƒvoidƒraceStride()ƒ
{ƒifƒ(!this.frontIsClear())ƒ
ƒƒ{ƒthis.jumpHurdle();
ƒƒ}ƒelseƒ Either This or That
ƒƒ{ƒthis.move();
ƒƒ}
}
ifƒ(!this.frontIsClear())
{ƒ// what to do if this robot's front is blocked
}
Simple Predicate
However, the following positive statement is easier to understand:
ifƒ(this.frontIsBlocked())
{ƒ// what to do if this robot's front is blocked
}
Fortunately, Java allows us to define our own predicates. Recall that a predicate is a
method that returns one of the Boolean values, either true or false. Returning a
value has two requirements:
➤ The method’s return type must be indicated in its declaration. The type
boolean is appropriate for predicates. It replaces the keyword void we have
used so far.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 187
187
«accessModifier»ƒbooleanƒ«predicateName»(«optParameters»)
{ƒreturnƒ«booleanExpression»;
Simple Predicate
}
where
➤ «accessModifier» is public, protected, or private.
➤ «predicateName» is the name of the predicate. Valid names are the same as
for any other method.
➤ «optParameters» provide additional information from the client.
Parameters are optional; many predicates do not have them.
➤ «booleanExpression» evaluates to either true or false and is the expres-
sion that could be placed in the test of an if or while statement.
On the other hand, consider the situation shown on the right side of Figure 4-11.
frontIsClear evaluates to true, but is negated by the !, resulting in the entire
expression evaluating to false—the robot’s front is not blocked.
(figure 4-11)
Evaluating
frontIsBlocked in two Robot’s front is blocked Robot’s front is not blocked
different situations
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 188
188
CHAPTER 4 | MAKING DECISIONS
When developing the goToNextRow method (see Listing 4-3) we included the follow-
ing if statement and comment:
3 {ƒifƒ(this.getAvenue()ƒ==ƒ5) // at the east end of the row
4 ƒƒ{ƒthis.turnRight();
5 ƒƒƒƒthis.move();
6 ƒƒƒƒthis.turnRight();
7 ƒƒ}
Predicates such as atRowsEastEnd can help us produce self-documenting code. The KEY IDEA
goal of self-documenting code is to make the code so readable that internal comments Appropriately
explaining the code are not needed. In this case, atRowsEastEnd tells us the intention named predicates
of the test nearly as well as the comment, enabling us to remove the comment. It’s a lead to self-
documenting code.
good idea to replace comments with self-documenting code because comments are
often overlooked as the code changes. When this happens comments can become
incomplete, misleading, or wrong.
Coding this predicate follows the same procedure as before: take the Boolean expres-
sion that would be included in the if or while statement and place it inside a method.
The method is as follows:
protectedƒbooleanƒatRowsEastEnd()
{ƒreturnƒthis.getAvenue()ƒ==ƒ5;
}
Simple Predicate
The query getDirection is similar to getAvenue except that it returns one of the KEY IDEA
special values such as Direction.NORTH or Direction.EAST. These values can be
Enumerated types
compared using == and !=, but not <, >, and so on. such as Direction
can be tested for
This fact can be used to create the predicate isFacingSouth as follows: equality only.
protectedƒbooleanƒisFacingSouth()
{ƒreturnƒthis.getDirection()ƒ==ƒDirection.SOUTH;
}
189
We have declared parameters every time we extended the Robot class and wrote a con-
structor, as well as in Section 3.7.1 where we used parameters to place a stick figure at a
precise location. From these contexts, we know that each parameter declaration has a
type, such as int, and a name. Parameter declarations are placed between the parentheses
following the method’s name. If there is more than one declaration, consecutive pairs are
separated with commas. These points are illustrated in Figure 4-12.
The type of the parameter determines what kind of values can be used as arguments. If
the parameter’s type is int, the arguments must be integers such as 15 or -23.
Similarly, only an object of type City can be passed as an argument to a parameter of
type City.
Inside the constructor or method, the name of the parameter can be used to reference the
value passed to it as an argument. Let’s use an example to understand how this works.
Suppose we want a subclass of Robot that can easily tell us if it has gone past a partic-
ular avenue, say Avenue 50. We could use the getAvenue method and compare it to
50, but our code is more self-documenting with a predicate, as follows:
ifƒ(this.isPastAvenue(50))
{ƒ// what to do when the robot has strayed too far
privateƒbooleanƒisPastAvenue(intƒanAvenue)
{ƒreturnƒthis.getAvenue()ƒ>ƒanAvenue;
}
Simple Predicate
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 190
190
CHAPTER 4 | MAKING DECISIONS
Inside the isPastAvenue method, anAvenue refers to the value passed as an argu- KEY IDEA
ment. In the preceding example, that value is 50 and the Boolean expression is evalu- The parameter refers
ated as this.getAvenue()ƒ>ƒ50. However, if the argument is 100, as in to the value passed
as an argument.
ifƒ(this.isPastAvenue(100)), then inside isPastAvenue the parameter
anAvenue will refer to the value 100. This one method can be used with any avenue—
a tremendous amount of flexibility compared to methods without parameters.
A parameter can also be used in the test controlling a while or if statement—and can
make the method more flexible, as well. For example, the following method moves a
robot east to Avenue 50:
/** Move the robot east to Avenue 50. The robot must already be facing east and
* must be on an avenue that is less than 50. */
publicƒvoidƒmoveToAvenue50()
{ƒwhileƒ(this.getAvenue()ƒ<ƒ50)
ƒƒ{ƒthis.move();
ƒƒ}
}
This method is extremely limited—it is only useful to move the robot to Avenue 50.
With a parameter, however, it can be used to move the robot to any avenue east of its
current location. The following method includes a parameter with an appropriate doc-
umentation comment:
/** Move the robot east to destAve. The robot must already be facing east and
* must be on an avenue that is less than destAve.
* @param destAveƒƒƒƒThe destination avenue to move to. */
publicƒvoidƒmoveToAvenue(intƒdestAve)
{ƒwhileƒ(this.getAvenue()ƒ<ƒdestAve)
ƒƒ{ƒthis.move();
ƒƒ}
}
191
publicƒvoidƒstep(intƒhowFar)
{ƒwhileƒ(howFarƒ>ƒ0)
ƒƒ{ƒthis.move();
ƒƒ}
}
KEY IDEA Why is it ill advised? Consider telling karel to step four times with karel.step(4).
Something in the The while statement evaluates the expression howFarƒ>ƒ0, concluding that it is
body of the loop must true—four is larger than zero. The statement executes the move method and evalu-
change how the test ates howFarƒ>ƒ0 again. howFar is still four, four is still greater than zero, and so the
is evaluated.
move method is executed again. The value of howFar does not change in the body of
the loop, the test will always be true, and the loop will execute “forever.”
Suppose, however, that we could decrease the value of howFar in the body of the loop,
as indicated by the following pseudocode:
publicƒvoidƒstep(intƒhowFar)
{ƒwhileƒ(howFarƒ>ƒ0)
ƒƒ{ƒthis.move();
Count-Down Loop
make howFar one less than it is now
ƒƒ}
}
That is, howFar starts with the value 4, then has the value 3, then 2, and so on—
assuming that step was called with an argument of 4, as in the preceding code. Now
we have a useful method, as illustrated in Figure 4-13. When howFar reaches the value
0, the loop stops and the robot has traveled four intersections. If we want karel to
take four steps, we write karel.step(4). If we want karel to take 400 steps, it’s as
easy as writing karel.step(400).
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 192
192
CHAPTER 4 | MAKING DECISIONS
(figure 4-13)
howFar
while (howFar > 0) Illustrating the execution
is 4
of a count-down loop
{ this.move();
howFar
while (howFar > 0) is 3
{ this.move();
howFar
while (howFar > 0) is 0
{ this.move();
A while statement that counts from a number down to zero is called a count-down loop.
We still need to explain, of course, how to make howFar be one less than it is now.
This change is accomplished with an assignment statement. An assignment statement
evaluates an expression and assigns the resulting value to a variable. A parameter is
one kind of variable.
The following is an assignment statement that decreases howFar’s value by one: LOOKING AHEAD
Other kinds of
howFarƒ=ƒhowFarƒ–ƒ1;
variables will be
discussed in
When this assignment statement is executed, it evaluates the expression on the right
Chapters 5 and 6.
side of the equal sign by subtracting one from the current value of howFar. When
howFar refers to the value 4, howFarƒ–ƒ1 is the value 3. The value 3 is then assigned
to howFar. The parameter will refer to this new value until we change it with another
assignment statement or the method ends. Parameters are destroyed when the method
declaring them completes its execution.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 193
193
A count-down loop can have a body that is more complex than a single move. The body
could contain more statements, or preferably, a call to a helper method. Furthermore, a
method can have more than one parameter and parameters can be passed as arguments
to helper methods. In this section we will develop a class of robots that illustrate all of
these principles. Our robots will plant Things in the shape of a hollow rectangle. The
width and height of the rectangle is specified with parameters when the plantRect
method is invoked. A sample initial and final situation is shown in Figure 4-14.
(figure 4-14 ) 0 1 2 3 4 0 1 2 3 4
0 0
One pair of many possible
initial and final situations 1 1
for planting a rectangle
2 2
Our class is called RectanglePlanter and has a single service, plantRect. We want
the robot to be able to plant many different sizes of rectangles, a kind of flexibility that is
well-suited for using parameters. Two parameters are needed—one for the rectangle’s
width and one for the height. This usage follows the setSize command for a JFrame
and the drawRect command in the Graphics class. The following code fragment
instructs karel to plant a rectangle five Things wide and three Things high, as shown
in Figure 4-14. One notable feature is that the constructor allows specifying the number
of things initially in the robot’s backpack. Here it is set to 50.
RectanglePlanterƒkarelƒ=ƒnewƒRectanglePlanter(
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒgarden,ƒ0,ƒ0,ƒDirection.EAST,ƒ50);
...
karel.plantRect(5,ƒ3);
Implementing plantRect
The plantRect method requires two integer parameters, one for the width and one for
the height, corresponding to the arguments 5 and the 3 in the previous code fragment.
The beginning of the class, including a stub for plantRect and the constructor allowing
the initial number of Things in the backpack to be set, is as shown in Listing 4-5.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 194
194
CHAPTER 4 | MAKING DECISIONS
Planting a side four times, with the appropriate lengths for each side, results in the
desired rectangle.
0 1 2 3 4 (figure 4-15)
0
Strategy for planting a
1 rectangle
2
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 195
195
publicƒvoidƒplantRect(intƒwidth,ƒintƒheight)
{ƒthis.plantSide(width);
ƒƒthis.plantSide(height);
ƒƒthis.plantSide(width);
ƒƒthis.plantSide(height);
}
Note that the parameters, width and height, are passed as arguments to the helper
method. However, the plantSide method only requires one parameter because it is
only concerned with the length of a side and not the overall dimensions of the rectangle.
Implementing plantSide
The strategy for plantSide was already determined when outlining the overall strat-
egy. We already know, from the way it was used in plantRect, that it has a single,
integer parameter. The parameter can have any name, but we will call it length
because it determines the length of the side.
plantSide plants a line that is one less than the length of the side and then turns right.
In pseudocode, this is as follows:
length = length - 1
plant a line of Things that is length Things long
turn right
/** Plant one side of the rectangle with Things, beginning with the next intersection.
* @param length The length of the line. */
protectedƒvoidƒplantSide(intƒlength)
{ƒlengthƒ=ƒlengthƒ-ƒ1;
ƒƒthis.plantLine(length);
ƒƒthis.turnRight();
}
Implementing plantLine
Planting a line of Things requires repeating actions zero or more times (a while state-
ment). It’s not a question of performing actions once or not at all (an if statement) or
performing either this action or that action (an if-else statement).
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 196
196
CHAPTER 4 | MAKING DECISIONS
What are the actions we must repeat? To plant a line of three things beginning with the
next intersection, for example, we must perform the following actions:
move
plant a thing
move
plant a thing
move
plant a thing
The actions that are repeated are moving and planting a thing. They form the body of
the while statement. We want them to be performed a specific number of times, as
specified by the parameter. This is an ideal application for a count-down loop. This
method is, in fact, identical to the step method developed earlier except that we also
need to plant a Thing on the intersection. The code for the method follows:
/** Plant a line of Things beginning with the intersection in front of the robot.
* @param length The length of the line. */
protectedƒvoidƒplantLine(intƒlength)
{ƒwhileƒ(lengthƒ>ƒ0) Count-Down Loop
ƒƒ{ƒthis.move();
ƒƒƒƒthis.plantIntersection();
ƒƒƒƒlengthƒ=ƒlengthƒ-ƒ1;
ƒƒ}
}
197
The code for the main method is shown in Listing 2-13. StickFigure is the class that
does the actual drawing. It was originally shown in Chapter 2 and is reproduced in
Listing 4-6. Notable points are that it sets the preferred size for the component in the
constructor at lines 8 and 9, and overrides paintComponent to draw the actual image.
198
CHAPTER 4 | MAKING DECISIONS
24
25 // pants
26 ƒƒƒƒg.setColor(Color.BLUE);
27 ƒƒƒƒg.fillRect(60,ƒ150,ƒ60,ƒ120);
28 ƒƒƒƒg.setColor(Color.BLACK);
29 ƒƒƒƒg.drawLine(90,ƒ180,ƒ90,ƒ270);
30 ƒƒ}
31 }
Now, suppose that we wanted the image to be a different size. The preferred size set in
the StickFigure constructor could be replaced with, for example, newƒ
Dimension(90,ƒ135) to make the image half as big in each dimension.
There is a problem, however. Making only this one change results in an image similar
to the one shown in Figure 4-17. Unfortunately, all of the calculations to draw the stick
figure were based on the old size of 180 pixels wide and 270 pixels high.
(figure 4-17)
We can make the image less sensitive to changes in size by drawing it relative to the cur-
rent size of the component. We can obtain the current size of the component, in pixels,
with the getWidth and getHeight queries. To paint a shape that covers a fraction of
the component, multiply the results of these queries by a fraction. For example, to spec-
ify an oval that is two-sixths of the width of the component and two-ninths of the
height, we can use the following statement:
g.fillOval(0,ƒ0,ƒthis.getWidth()*2/6,ƒthis.getHeight()*2/9);
The first two parameters will place the oval at the upper-left corner of the component.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 199
199
g.fillOval(this.getWidth()*2/6,ƒthis.getHeight()*0/9,ƒ
ƒƒƒƒƒƒƒƒƒƒƒthis.getWidth()*2/6,ƒthis.getHeight()*2/9);ƒ
The first pair of parameters says the head’s bounding box should start 2/6th of the com-
ponent’s width from the left edge and 0/9th of the component’s height from the top. The
second parameter could be replaced by 0.
Converting the remaining method calls to use the getWidth and getHeight queries
follows a similar pattern. It is tedious, however. Fortunately, there is a better approach.
Using getWidth and getHeight to scale an image follows a very predictable pattern,
as shown in the last section. Fortunately, the designers of Java have provided a way for
us to exploit that pattern with much less work on our part. They have provided a way
for the computer to automatically multiply by the width or the height of the compo-
nent and divide by the number of units on our grid. All we need to do is supply the
numerator of the fraction that places the image or says how big it is. For example, in
the previous section we wrote the following statements:
g.fillOval(this.getWidth()*2/6,ƒthis.getHeight()*0/9,ƒ
ƒƒƒƒƒƒƒƒƒƒƒthis.getWidth()*2/6,ƒthis.getHeight()*2/9);ƒ
Java’s drawing methods can be set up so that this method call is replaced with the fol-
lowing statement:
g.fillOval(2,ƒ0,ƒ2,ƒ2);
Using this approach requires three things: Using a more capable version of the
Graphics object, setting the scale to be used in drawing, and scaling the width of the
lines to use in drawing. All are easy and follow a pattern consisting of the following
four lines inserted at the beginning of paintComponent:
1 // Standard stuff to scale the image
2 Graphics2Dƒg2ƒ=ƒ(Graphics2D)g;
Scale an Image 3 g2.scale(this.getWidth()/6,ƒthis.getHeight()/9);
4 g2.setStroke(newƒBasicStroke(1.0F/this.getWidth()));
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 200
200
CHAPTER 4 | MAKING DECISIONS
Explaining this code in more detail requires advanced concepts; however, the overview
is as follows:
➤ Line 2 makes a larger set of capabilities in g available. More about this in
Chapter 12.
➤ Line 3 tells the Graphics object how to multiply values to scale our paintings
appropriately.
➤ Line 4 makes the width of a line, also called a stroke, proportional to the scal-
ing performed in line 3.
Whether or not we understand exactly what these lines of code do, using them is easy:
➤ Import the package java.awt.*.
➤ Copy these four lines to the beginning of your paintComponent method.
➤ Decide on the size of your grid, and change the “6” and “9” in the call to
scale accordingly. For a 50 x 100 grid, change the 6 to 50 and the 9 to 100.
➤ Use g2 instead of g to do the painting.
201
4.8 PATTERNS
4.8 Patterns
Context: You are in a situation where executing a group of one or more statements
may or may not be appropriate, depending on the value of a Boolean expression. If the
expression is true, the statements are executed once. If the expression is false, they are
not executed at all.
ifƒ(this.numStudentsInCourse()ƒ<ƒ100)
{ƒthis.addStudentToCourse();
}
ifƒ(«test»)
{ƒ«listƒofƒstatements»
}
If possible, state the test positively. Easily understood predicate names contribute to easily
understood code. For example, if the statement ifƒ(this.numStudentsInCourse()ƒ
<ƒthis.maxEnrollment()) is really checking if there is room in the course for one more
student to be added, then using a predicate such as ifƒ(this.roomInCourse())ƒmakes
the code easier to understand.
Related Patterns:
➤ The Either This or That pattern executes one of two actions. This pattern is a
special case of that one.
➤ The Zero or More Times pattern is useful if an action is to be executed repeat-
edly rather than once or not at all.
➤ The Simple Predicate pattern is often used to create more easily under-
stood «test»s.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 202
202
CHAPTER 4 | MAKING DECISIONS
Context: You are in a situation where a group of one or more statements must be exe-
cuted a (usually) unknown number of times. It might be as few as zero times, or possi-
bly many times. Whether to repeat the statements again can be determined with a
Boolean expression.
Solution: Use a while statement to control the execution of the statements. When the
test evaluates to true, the statements are executed and the test is performed again.
This continues until the test evaluates to false. The following example is an example
of the pattern:
whileƒ(this.frontIsClear())
{ƒthis.turnLeft();
}
This loop turns the robot until it is facing a wall. If there is no wall blocking one of the
four directions, it will turn forever.
whileƒ(«test»)
{ƒ«listƒofƒstatements»
}
As with the if statement, a while statement is easiest to read and understand if the
test is stated positively.
Consequences: The list of statements may be executed as few as zero times or they
may execute forever. Such infinite loops are not desirable and should be guarded
against.
Related Patterns:
➤ The Count-down Loop pattern is a special case of Zero or More Times.
➤ The Once or Not at All pattern executes an action either zero or one times
rather than zero or more times.
➤ The Simple Predicate pattern is often used to create more easily under-
stood «test»s.
Context: You have two groups of statements. Only one group should be executed and
which one depends on the result of a Boolean expression.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 203
203
4.8 PATTERNS
Solution: Use an if-else statement to perform the test and govern which group of
statements is executed, as in the following example:
ifƒ(this.frontIsClear())
{ƒthis.move();
}ƒelse
{ƒthis.turnLeft();
}
Consequences: The pattern allows programs to choose between two courses of action
by evaluating a Boolean expression.
Related Patterns:
➤ The Once or Not at All pattern is a special case of this pattern useful for when
there is only one action that may or may not be executed.
➤ If there are more than two actions, only one of which is executed, consider the
Cascading-If pattern, described in Section 5.8.6.
➤ The Simple Predicate pattern is often used to create more easily under-
stood «test»s.
Context: You are using a Boolean expression that is not as easy to read or understand
as is desirable. Perhaps it is a complicated expression or perhaps the names of the
queries don’t match the problem.
Solution: Define a new method that performs the processing to find the required result,
returning true or false to its client. Such methods are called predicates. For exam-
ple, the following code defines a predicate named frontIsBlocked:
publicƒbooleanƒfrontIsBlocked()
{ƒreturnƒ!this.frontIsClear();
}
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 204
204
CHAPTER 4 | MAKING DECISIONS
Related Patterns:
➤ The Predicate pattern is often used to define predicates used in the Once or Not
at All, Zero or More Times, and Either This or That patterns, among others.
➤ The Simple Predicate pattern is a specialization of the more general Predicate
pattern discussed in Section 5.8.5.
Context: You must perform an action a specified number of times. The number is often
given via a parameter.
Solution: Write a while statement that uses a variable, often a parameter variable, to
count down to zero. When the value reaches zero, the loop ends. The general form of
the count-down loop is as follows:
whileƒ(«variable»ƒ>ƒ0)
{ƒ«listƒofƒstatements»
ƒƒ«variable»ƒ=ƒ«variable»ƒ-ƒ1;
}
A concrete example of the count-down loop is the plantLine method which puts a
row of Things, the length of which is determined by the parameter.
publicƒvoidƒplantLine(intƒlength)
{ƒwhileƒ(lengthƒ>ƒ0)
ƒƒ{ƒthis.move();
ƒƒƒƒthis.putThing();
ƒƒƒƒlengthƒ=ƒlengthƒ–ƒ1;
ƒƒ}
}
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 205
205
4.8 PATTERNS
Consequences: The Count-Down Loop pattern gives programmers the ability to per-
form an action a specified number of times, even if the number is large. Putting the
count-down loop inside a method and using a parameter provides even more flexibility.
Related Patterns: The Count-Down Loop pattern is a special case of the Zero or More
Times pattern.
Solution: Draw the image based on a predefined grid for the coordinates. Then use the
following code template to use that coordinate grid while drawing.
publicƒvoidƒpaintComponent(Graphicsƒg)
{ƒsuper.paintComponent(g);
ƒƒ«statementsƒusingƒg2ƒtoƒdrawƒtheƒimage»
}
206
CHAPTER 4 | MAKING DECISIONS
determine which of two actions to execute and a count-down loop can execute an
action a specified number of times.
Using predicates in the tests used by if and while statements can make them easier to
understand, debug, test, and maintain, all of which increases the quality of programs.
scaling getWidth,
images getAvenue,
use of readInt
s mples
are exa
queries
return
s o ial
statements
rm ec
f
ith
fo sp
ers w
answ
are
use
give
can
side effects
predicates should not have
s wit
h
can
true, false
evaluate to
Boolean if
control statements
expressions
f
a tion o
neraliz
all b nce
ge
if-else are a
cont
o
y
statements
or no xecuted
rol
are
t at
be e
while are
statements may
may
zero b e
or m execute
ore t d
imes
by
forms ial
method statements
ec
of
calls
are sp
are
ma
y name and
us type
e w it h a
red
count-down are decla
loops refer to the value passed
parameters argument
in the corresponding
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 207
207
Written Exercises
4.1 Evaluate the following Boolean expressions for a Robot. Assume the robot is
on intersection (1, 5) facing north. There is a Wall immediately in front of it.
In each case your answer will be either true or false.
a. this.getAvenue()ƒ>ƒ0
b. this.getAvenue()ƒ<=ƒ5
c. this.getStreet()ƒ!=ƒ1
d. !(this.getStreet()ƒ==ƒ1)
e. this.frontIsClear()
f. !this.frontIsClear()
g. !!this.frontIsClear()
h. this.frontIsClear()ƒ==ƒfalse
4.2 Consider the following if-else statements. Do they behave the same way or
differently? Justify your answer.
ƒifƒ(this.canPickThing())ƒƒƒƒƒƒƒƒƒƒifƒ(!this.canPickThing())
ƒ{ƒthis.turnRight();ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ{ƒthis.turnLeft();
ƒ}ƒelseƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ}ƒelse
ƒ{ƒthis.turnLeft();ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ{ƒthis.turnRight();
ƒ}ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ}
4.3 Consider the goToNextRow method developed in Section 4.3.3 and shown in
Listing 4-3.
a. In a table similar to Table 1-2, trace the method when the robot is on (1, 1)
facing west, again when it is on (1, 3) facing west, and once again when the
robot is on (1, 5) facing east.
b. Describe what happens if the method is modified for rows of length 1. That is,
the west end of the row is on Avenue 1 and the east end is also on Avenue 1.
c. Rewrite the method using an if-else statement.
d. The current method requires the rows to start and end on specified
avenues. Rewrite the method using a different test to remove this restric-
tion. The new method will allow the method to be used in any size field
without modification.
4.4 Figure 4-13 illustrates the execution of a whileƒloop. Trace the loop using a
table similar to Table 1-2. Include columns for the robot’s street, avenue, direc-
tion, and the parameter howFar. Assume the robot begins on (2, 5) facing east
and that the step method was called with an argument of 4.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 208
208
CHAPTER 4 | MAKING DECISIONS
Programming Exercises
4.5 Write methods named turnLeft, pickThing, and putThing that allow the
client to specify how many times the robot turns, picks, and puts, respectively.
4.6 Write a pair of methods, as follows:
a. carryExactlyEight ensures a robot is carrying exactly eight things in its
backpack. Assume the robot is on an intersection with at least eight things
that can be picked up
b. Generalize carryExactlyEight to carryExactly. The new method will
take a parameter specifying how many Things the robot should carry.
4.7 Write a new robot method, faceNorth. A robot that executes faceNorth will
turn so that getDirection returns Direction.NORTH.
a. Write faceNorth so that the robot turns left until it faces north. Use several
if statements.
b. Write faceNorthƒso that the robot turns left until it faces north. Use a
while statement.
c. Write faceNorth so that the robot turns either left or right, depending on
which direction requires the fewest turns to face north.
4.8 Write a robot method named face. It takes a single direction as a parameter
and turns the robot to face in that direction. The robot does not need to use
the minimal number of turns.
4.9 Code and run brief examples of the following errors and report how your com-
piler handles them.
a. A method with a return type of void that includes the statement
returnƒ!this.canPickThing();.
b. A method with a return type of boolean that does not include a return
statement.
c. A method named experiment that takes a single integer argument. Call it
without an argument, with two arguments, and with Direction.NORTH.
Programming Projects
4.10 Finish the implementation of the Racer class shown in Listing 4-4.
Demonstrate your class with at least two different race courses.
4.11 Listing 3-3 is the complete implementation of the Harvester class, a class of
robots designed to harvest a field of things. Implement the class again using your
knowledge of if and while statements. Your new class of robots should be able
to harvest a rectangular field of any size provided that things cover the field com-
pletely and the field is bordered by intersections that do not have any things on it
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 209
209
4.13 Implement a Guard class of robots that can guard either King Java’s castle or
King Caffeine’s castle, plus other castles with similar layouts but different sizes.
See Programming Projects 3.12 and 3.13 for descriptions of these castles. You
may assume that the corner turrets are each one wall square and that the cen-
tral courtyard of the castle has at least one wall on each side. Create several
files specifying different sizes of castles to use in testing your program.
4 Chapter C5743 40143.ps 11/30/06 1:19 PM Page 210
210
CHAPTER 4 | MAKING DECISIONS
4.14 A method named goToOrigin could use the following algorithm to move a
robot to intersection (0, 0):
face avenue 0
move to avenue 0
face street 0
move to street 0
0 1 2 3 4 5 0 1 2 3 4 5 (figure 4-19)
0 0
Sample pair of initial and
1 1 final situations for a
3 HistogramBot
2 2
2
3 3
5
4 4
4
5 5
16. Sketch a scene on graph paper that uses a combination of ovals, rectangles,
lines, and perhaps strings (text). Write a program that paints your scene, using
the scaling techniques in Section 4.7.
Chapter 5 More Decision Making
Chapter Objectives
After studying this chapter, you should be able to:
➤ Follow a process for constructing while loops with fewer errors
➤ Avoid common errors encountered with while loops
➤ Use temporary variables to remember information within methods
➤ Nest statements inside other statements
➤ Manipulate Boolean expressions
➤ Perform an action a predetermined number of times using a for statement
➤ Write if and while statements with appropriate style
The if and while statements studied in Chapter 4 form the basis for making decisions
in programs. Any program you care to write can be written with using only the if and
while statements to change the flow of control. This chapter continues the discussion
with variations of the if statement and other ways to repeatedly execute statements
that can simplify our code even though they are not strictly required. It explores a
process for constructing while statements and points out errors to avoid. In short, this
chapter summarizes the accumulated wisdom of programming with if and while
statements.
Sometimes decisions are made based on what has happened in the past. Such decisions
are facilitated by temporary variables that can remember information for later use in
the same method.
211
CHAPTER 5 | MORE DECISION MAKING 212
If we want to build three fence sections, how many fence posts will we need? The obvi-
ous answer is three, but that is wrong. Look at Figure 5-1. The figure should help us to
understand why the correct answer is four.
1 2 3 (figure 5-1)
1 2 3 4 Fence-post problem
We can encounter the fence-post problem when using the while loop. For example,
consider the problem of clearing all the Things between a robot and a wall. The
robot’s starting intersection also contains a Thing, as shown in Figure 5-2.
(figure 5-2)
publicƒvoidƒclearThingsToWall()ƒ
{ƒwhileƒ(this.frontIsClear())ƒ
ƒƒ{ƒthis.pickThing();ƒ
ƒƒƒƒthis.move();ƒ
ƒƒ}
}
213
(figure 5-3)
After executing
clearThingsToWall
In this example, the Things are the fence posts and the moves are the fence sections.
The while loop executes the same number of pickThing and move statements.
Consequently, one Thing will be left when the loop finishes. We can handle this situa-
tion by adding an extra pickThing command after the while loop finishes executing,
as shown in the following code fragment:
publicƒvoidƒclearThingsToWall()ƒ
{ƒwhileƒ(this.frontIsClear())ƒ
ƒƒ{ƒthis.pickThing();ƒ
Loop-and-a-Half
ƒƒƒƒthis.move();ƒ
ƒƒ}
ƒƒthis.pickThing();
}
LOOKING AHEAD It is surprising how often the fence-post problem occurs in computer science. It is also
The while-true known as the loop-and-a-half problem because, in one sense, the loop executes an extra
loop is an elegant half iteration when the last thing is picked.
solution to the loop-
and-a-half problem.
See Section 5.5.3. Infinite Loops
You may have experienced a computer program that hangs. It appears to be running
fine and then mysteriously fails to respond to your commands. The entire program
appears “frozen.” Such a program is probably caught in an infinite loop. An infinite
loop is one that has no way of ending because the programmer has forgotten to include
a statement (or sequence of statements) whose execution allows the loop’s test to
become false. Here is an example:
whileƒ(this.isFacingNorth())ƒ
{ƒthis.pickThing();ƒ
ƒƒthis.move();ƒ
}
KEY IDEA Nothing within this loop will change the robot’s direction. As a result, the loop will
Every loop must have iterate zero times if the robot is initially facing any direction other than north.
a statement that can Unfortunately, if it is facing north, we condemn the robot to walk forever (unless, of
affect the test. course, it breaks because there is no Thing to pick up or it runs into a wall; it will also
CHAPTER 5 | MORE DECISION MAKING 214
stop if the computer itself crashes or the computer’s power supply is disrupted).1 We
must be very careful when we plan the body of the while loop to avoid the possibility
of an infinite loop.
The common errors discussed in the previous section, plus difficulties you probably
experienced constructing loops in the previous chapter, should motivate you to study a
formal process to construct while loops. The goal is to structure our thinking so that
our loops are more likely to be written correctly.
There are four steps. We first outline them briefly and then illustrate them with two
examples.
Step 1: Identify the actions that must be repeated to solve the problem.
Step 2: Identify the Boolean expression that must be true when the while statement
has completed executing. Negate it.
Step 3: Assemble the while loop with the actions from Step 1 as the body and the
Boolean expression from Step 2 as the test.
Step 4: Add additional actions before or after the loop to complete the solution.
We will now apply this four-step process to two examples. The first is the
clearThingsToWall problem discussed in Section 5.1.1. The second is a more com-
plicated problem.
Consider again the problem of clearing all the Things between (and including) the
robot’s intersection and a Wall, as shown in Figure 5-2. We don’t know how far away
the Wall is.
Step 1 is to identify the actions that must be repeated. One way to do this is to solve a
small example of the problem without a loop. The four Things in Figure 5-2 already
qualifies as a small problem (much smaller than, say, 400!). To solve it, we need to per-
form the following actions:
1 Of course, if we have overridden pickThing or move, then anything is possible. One of the new ver-
sions could change the direction, and then the robot would exit the while loop.
215
Clearly, two actions are repeated, pick a thing and move. In particular, note that the
two actions appear in groups—as shown by the brackets on the right—and that one of
the actions doesn’t appear in any of the groups. Because of that extra action, there are
actually two ways to group the repeated actions. The other grouping results in an extra
pick a thing at the beginning of the sequence.
Step 2 is to identify the Boolean expression that must be true when the loop finishes.
The robot should stop collecting things when it is blocked by a wall; that is, the loop
should stop when the test this.frontIsBlocked is true. But the test for a while
statement isn’t whether the loop should stop; the test is whether the loop should con-
tinue. Therefore, the test to use is the negation of this.frontIsBlocked():
!this.frontIsBlocked() or this.frontIsClear().
Step 3 assembles the while loop using a group of repeated actions from Step 1 and the
Boolean expression from Step 2. This yields the following code:
whileƒ(this.frontIsClear())
{ƒthis.pickThing();
ƒƒthis.move();
}
Finally, Step 4 cleans things up. Recall, for example, that there was one action in Step 1
that wasn’t included in any of the groups. This is the extra fence post from the loop-
and-a-half problem. The extra action was at the end of the preceding sequence and so it
Loop-and-a-Half is placed after the loop. The final solution is as follows:
whileƒ(this.frontIsClear())
{ƒthis.pickThing();
ƒƒthis.move();
}
this.pickThing();
A more difficult looping problem is a robot shifting a pile of Things from one inter-
section to the next, as shown in Figure 5-4.
CHAPTER 5 | MORE DECISION MAKING 216
(figure 5-4)
This problem clearly requires actions to be performed zero or more times rather than
once or not at all. Therefore a while loop is required, and we can apply the four-step
process.
Step 1 is to identify the steps that must be repeated. As before, we assume a typical ini- KEY IDEA
tial situation and solve the problem without a loop. In this case, assume the pile has Identify the repeating
four Things on it. (Remember, our final solution must handle a pile of any size. We are steps with a loopless
solution.
assuming four things only while we find the steps that repeat.) To move all four
Things to the next intersection, the robot must perform the following steps:
This sequence of actions has three actions that must be repeated: pick up one thing,
shift it to the next intersection, and go back to the original intersection. As before, we
group them with brackets, as shown in the preceding pseudocode. With two actions
left over in the sequence, however, determining what comes before and after the loop
will be trickier than in the previous example.
Step 2 identifies the test that must be true when the loop has finished executing. We can
consider several possible tests:
➤ the robot is on the next intersection—This can’t be the correct test to end the
loop because the robot is on the next intersection many times while moving
the Things.
217
In Java, this test can be expressed with the Boolean expression !this.canPickThing().
That is, the loop stops when the robot can’t pick up any more Things from the original
intersection. The test that determines when the while loop should continue is the nega-
tion of this, or this.canPickThing().
Step 3 assembles the loop. The test was identified in Step 2, giving us the following
structure:
whileƒ(this.canPickThing())
{ƒ...
}
The question is how to arrange the repeated action inside the loop. There are actually
three possibilities, as follows. In each case, Step 4 is anticipated and the leftover actions
are placed either before or after the loop, depending on how they appear in the loop-
less solution from Step 1.
Barring a flash of insight, the way to choose one of these options is to trace them. An
excellent situation to use for your first trace is the smallest possible problem: one
Thing on the original intersection. Try tracing the left-most loop for yourself. You
should convince yourself that it fails for two reasons. First, it tries to pick a Thing
from an empty intersection. Second, the problem specification says it can’t return to
the intersection after it is empty, which it does.
The right-most loop also has problems. On any sized problem it picks up a Thing and
shifts it to the next intersection. But then it determines if it can pick up the Thing it,
just shifted. It’s performing the test on the wrong intersection.
The middle loop executes correctly. It picks up a Thing from the intersection and then
asks if there is another Thing for the next trip. When the pile has just one Thing in it,
there is nothing left for another trip, and so it skips the loop body and shifts the Thing
it just picked up to the next intersection.
This is not an obvious solution. It takes a deep insight to realize that the test for pick-
ing up a Thing should be performed after picking one up and not before. There is no
CHAPTER 5 | MORE DECISION MAKING 218
algorithm for solving such a problem, but the four-step process provides significant
guidance in finding a solution.
We just demonstrated that, at least in pseudocode, our solution works for one particu-
lar instance of the problem. But what about all the other sizes of piles? We cannot test
all possible initial situations (there are infinitely many of them), but we can test several
and do a reasonable job of convincing ourselves that the solution is correct.
One method of informally reasoning about the statement has two steps. First, we must KEY IDEA
show that the statement works correctly when the initial situation results in the while Trace a situation
statement’s test being false. That is, in fact, the situation we just traced where the pile where the loop body
contained only one Thing. It picked that Thing up and then performed the test, which does not execute.
evaluated to false.
Second, we must show that each time the loop body is executed, the new situation is a KEY IDEA
smaller but similar version of the old situation. By smaller, we mean there is less work Demonstrate that
to do before finishing the loop. By similar, we mean that the situation has not radically executing the loop
changed while executing the loop body. (In this example, a non-similar change could body results in a
smaller but similar
be that the robot is facing a different direction.) version of the problem.
By tracing a few iterations of the loop, we see that after each iteration, the size of the pile
decreases by one. This gives us confidence that the situation will eventually reach the case
where the while loop becomes false, which we already checked for correctness.
A temporary variable, also called a local variable, is the core of one solution. A tempo- KEY IDEA
rary variable stores a value for later use within the same method. We have already used A temporary variable
temporary variables, starting with Chapter 1. sanDiego and karel are both tempo- stores a value for later
rary variables in the following code fragment: use in the method.
CityƒsanDiegoƒ=ƒnewƒCity();
Robotƒkarelƒ=ƒnewƒRobot(sanDiego,ƒ1,ƒ2,ƒDirection.EAST);
...
karel.move();
219
To count the number of Things on an intersection, we’ll use a temporary variable, but
one storing an integer rather than a reference to a Robot or City.
A temporary variable used to count something would typically be declared like this:
intƒcounterƒ=ƒ0;
Like the City and Robot declarations shown earlier, this declaration has a type and a
name followed by its initial value. The type is int and the name is counter. The type
Temporary Variable of int specifies that this variable will only store a particular kind of value, integers.
The initial value is zero, the first value assigned to the variable.
We have already worked with one kind of integer variable when we used parameters
in Section 4.6.1. In that case, we decremented the parameter howFar with the state-
ment howFarƒ=ƒhowFarƒ–ƒ1. Similarly, counter can be decremented with the state-
ment counterƒ=ƒcounterƒ–ƒ1. It’s not surprising that counterƒ=ƒcounterƒ+ƒ1
increments the value in counter by one.
With this background, let’s count the Things on an intersection with the idea that a robot
may be used to follow a trail, as described earlier. In pseudocode, we use the following:
intƒnumThingsHereƒ=ƒ0;
ifƒ(numThingsHereƒ==ƒ0)
{ƒthis.move();
}
CHAPTER 5 | MORE DECISION MAKING 220
ifƒ(numThingsHereƒ==ƒ1)
{ƒthis.turnLeft();
}
ifƒ(numThingsHereƒ==ƒ2)
{ƒthis.turnRight();
}
We can now focus on the remaining pseudocode to update numThingsHere. Our strat-
egy will be to pick up all of the Things on the intersection, increasing numThingsHere
by one each time a thing is picked up. There may be many things, so a while loop is
appropriate. In terms of the four-step process for writing a loop, the actions to repeat
(Step 1) are picking a Thing and incrementing the variable. The test for stopping (Step 2)
is when there is nothing left on the intersection. Therefore, the loop should continue while
canPickThing returns true. Assembling the loop (Step 3) yields the following code:
whileƒ(this.canPickThing())
{ƒthis.pickThing();
ƒƒnumThingsHereƒ=ƒnumThingsHereƒ+ƒ1;
}
For this problem, there is nothing to do before or after the loop (Step 4).
The completed code fragment for counting the number of Things on an intersection
and turning in the appropriate direction is shown in Listing 5-1.
Listing 5-1: A code fragment to count the number of Things on an intersection and move
appropriately
1 intƒnumThingsHereƒ=ƒ0;
2 whileƒ(this.canPickThing())
3 {ƒthis.pickThing();
4 ƒƒnumThingsHereƒ=ƒnumThingsHereƒ+ƒ1;
Counting
5 }
6
7 ifƒ(numThingsHereƒ==ƒ0)
8 {ƒthis.move();
9 }
10 ifƒ(numThingsHereƒ==ƒ1)
11 {ƒthis.turnLeft();
12 }
13 ifƒ(numThingsHereƒ==ƒ2)
14 {ƒthis.turnRight();
15 }
221
We can increase our understanding of the code in Listing 5-1 by tracing it. Doing so
will also increase our confidence in its correctness. As usual, we shall employ a table to
record the statements executed and the state of the program. To adequately trace the
state for this fragment, we need to record the Robot’s street, avenue, and direction; the
value of numThingsHere; and the number of Things on the intersection. It is also use-
ful to record the results of the tests performed by the if and while statements. A sin-
gle column will do for both tests, and we will only record the result in the line where it
is executed. We don’t need to record the number of Things in the robot’s backpack
because that information is not used in the code fragment.
(table 5-1)
Table 5-1 traces the situation in which the Robot is facing north on (3, 5). That inter-
Tracing the execution section has two Thing objects. The code should cause the Robot to turn right to face
of the code fragment east—which it does.
in Listing 5-1
1ƒƒintƒnumThingsHereƒ=ƒ0;
(3, 5) north 0 2
2ƒƒwhileƒ(this.canPickThing())
3ƒƒ{ƒthis.pickThing();
(3, 5) north 0 1
4ƒƒƒƒnumThingsHereƒ=ƒnumThingsHere + 1;
(3, 5) north 1 1
2ƒƒwhile (this.canPickThing())
3ƒƒ{ƒthis.pickThing();
(3, 5) north 1 0
4ƒƒƒƒnumThingsHere = numThingsHere + 1;
(3, 5) north 2 0
2ƒƒwhileƒ(this.canPickThing())
7ƒƒifƒ(numThingsHere == 0)
10ƒifƒ(numThingsHere == 1)
13ƒifƒ(numThingsHere == 2)
14ƒ{ƒthis.turnRight();
(3, 5) east 2 0
ifƒ(this.countThingsInBackpack()ƒ==ƒ0)
{ƒthis.move();
}
ifƒ(this.countThingsInBackpack()ƒ==ƒ1)
{ƒthis.turnLeft();
}
ifƒ(this.countThingsInBackpack()ƒ==ƒ2)
{ƒthis.turnRight();
}
Suppose you had your own backpack and performed this same task. You probably
wouldn’t count the number of things in the backpack three times—you would count
them once and then remember the answer long enough to decide whether to turn or
move. Using a temporary variable, a Robot can do the same thing. Instead of assigning
a value of 0 to the temporary variable when we declare it, we will assign whatever
value countThingsInBackpack returns, as shown in the following fragment:
1 intƒnumThingsƒ=ƒthis.countThingsInBackpack();
2 ifƒ(numThingsƒ==ƒ0)
3 {ƒthis.move();
4 }
5 ifƒ(numThingsƒ==ƒ1)
6 {ƒthis.turnLeft();
7 }
223
Suppose that the robot has one thing in its backpack. Then countThingsInBackpack
will return the value 1. The variable numThings will refer to that value until it is changed
or the method ends. In line 2, the value that numThings refers to (1) is compared to 0.
They are different, and so line 3 is not executed. In line 5, the value that numThings refers
to (1) is again compared, this time to the value 1. They are equal, and so the robot turns
left. In line 8, numThings is again compared, but the values are not equal and so the turn
in line 9 is not completed.
KEY IDEA Like predicates, a query such as countThingsHere (on the robot’s intersection) will
The value returned have a return type and a return statement. The return type in this case will be int
must match the because we expect this query to return an integer value. The return statement returns
query’s return type. a value whose type must match the query’s return type. In this particular query, the
return statement will return the value stored in the temporary variable at the end of
the method. See line 11 of Listing 5-2.
Listing 5-2: A method to count and return the number of Things on an intersection
Query
1 /** Count and return the number of things on this robot’s current intersection. Replace the
Temporary Variable
2 * things after counting them.
3 * @return the number of things on this robot’s current intersection. */
4 publicƒintƒcountThingsHere()
5 {ƒintƒnumThingsHereƒ=ƒ0;
6 ƒƒwhileƒ(this.canPickThing())
7 ƒƒ{ƒthis.pickThing();
8 ƒƒƒƒnumThingsHereƒ=ƒnumThingsHereƒ+ƒ1;
9 ƒƒ}
10 ƒƒthis.putThing(numThingsHere);
11 ƒƒreturnƒnumThingsHere;
12 }
CHAPTER 5 | MORE DECISION MAKING 224
It’s a good idea for a query to return information without changing the situation in
which it was called. If it needs to make changes to the program’s state—such as picking
Things up—the query should undo those changes before returning the answer. This
query does so in line 10 where it calls a helper method to put down a specific number
of Things. This helper method could be implemented using the Count-Down Loop
pattern. A query that changes the program’s state is said to have side effects.
Temporary variables, as well as parameters and other types of variables, can have one
of many different types. int is just one of the possibilities. Besides references to objects
like City and Robot, another possibility is the boolean type.
1 /** Determine whether the right side of this robot is blocked. The robot’s state doesn’t change. Temporary Variable
Predicate
2 * @return true if this robot’s right side is blocked; false otherwise. */
3 publicƒbooleanƒrightIsBlocked()
4 {ƒthis.turnRight();
5 ƒƒbooleanƒblockedƒ=ƒthis.frontIsBlocked();
6 ƒƒthis.turnLeft();
7 ƒƒreturnƒblocked;
8 }
This predicate uses a helper method to determine if its front is blocked. Line 5 could
also be written booleanƒblockedƒ=ƒ!this.frontIsClear(). The value is stored
in the temporary variable blocked until it is returned in line 7.
5.2.6 Scope
Temporary variables are always declared within a pair of braces. It may be the pair of
braces defining the body of a method or the pair of braces used to define the body of a
loop or a clause in an if statement. Each of these pairs of braces defines a block.
225
whileƒ(«test»)ƒ
{ƒ«listƒofƒstatements»
}
The general form of the if and if-else statements are similar. So far all of our examples
have used only method calls and assignment statements in «listƒofƒstatements».
That need not be the case. if and while statements are also statements and can be
included in «listƒofƒstatements».
For example, consider the situation shown in Figure 5-6. A robot is an unknown dis-
tance from a wall. Between it and the wall are a number of Thing objects placed ran-
domly on the intersections. The robot is to pick up one thing from each intersection (if
there is one) and stop at the last intersection before the wall.
(figure 5-6)
The robot needs to move zero or more times, indicating that a while loop is needed.
In addition, at each intersection, the robot must execute pickThing either once or not
CHAPTER 5 | MORE DECISION MAKING 226
at all, depending on whether or not a thing is present. An if statement solves this kind
of problem.
These two ideas can be combined in a single method, as shown in Listing 5-4. The if
statement is said to be nested within the while statement, just as toys such as blocks or
dolls are sometimes nested, one inside another.
1 /** Pick up one thing (if there is a thing) from each intersection between this robot and the
2 * nearest wall it is facing. */
3 publicƒvoidƒpickThingsToWall()
4 {ƒwhileƒ(this.frontIsClear())
5 ƒƒ{ƒthis.move();
6 ƒƒƒƒifƒ(this.canPickThing())
7 ƒƒƒƒ{ƒƒthis.pickThing();
8 ƒƒƒƒ}
9 ƒƒ}
10 }
In pickThingsToWall, the while loop executes zero or more times to move the robot
to the wall. The test ensures the robot will stop when it reaches the wall. Inside the loop,
two things happen. First, the robot moves to the next intersection. Once it is there, it
asks if it can pick up a Thing. If the answer is yes, the robot picks that thing up.
ifƒ(this.canPickThing())
{ƒ// There’s a thing here, so this robot will turn
ƒƒifƒ(this.countThingsInBackpack()ƒ>ƒ0)
ƒƒ{ƒthis.turnRight();
ƒƒ}ƒelse
ƒƒ{ƒthis.turnLeft();
ƒƒ}
}
KEY IDEA Nesting statements sometimes makes a method hard to understand, particularly if we
Use helper methods use several levels of nesting or many steps within the if or while statement. When a
to simplify nested method becomes too complicated, the appropriate approach is to use helper methods.
statements.
For example, the pickThingsToWall method could have been written using helper
methods, as shown in Listing 5-5.
1 /** Pick up one thing (if there is a thing) from each intersection between this robot
2 * and the nearest wall it is facing. */
3 publicƒvoidƒpickThingsToWall()
4 {ƒwhileƒ(this.frontIsClear())
5 ƒƒ{ƒthis.move();
6 ƒƒƒƒthis.pickThingIfPresent();
7 ƒƒ}
8 }
9
10 /** Pick up one thing (if there is a thing) from the robot’s intersection. */
11 privateƒvoidƒpickThingIfPresent()
12 {ƒifƒ(this.canPickThing())
13 ƒƒ{ƒthis.pickThing();
14 ƒƒ}
15 }
This solution has more lines in total, but each method can be understood more easily
than the larger version of pickThingsToWall in Listing 5-4.
Another useful form of nesting involves nesting if-else statements within if-else
statements. If the nesting is always done in the else-clause, the effect is to choose at
most one of a list of alternatives. For example, suppose that a robot should do exactly
one of the actions shown in Table 5-2.
CHAPTER 5 | MORE DECISION MAKING 228
It could be that more than one of these situations is true. For example, it could be that the
robot’s front and left are blocked. We still want the robot to perform only one action. We’ll
assume that the first matching situation listed in the table should be performed.
You should trace this code to convince yourself that only one of the actions listed in the
table is executed. That is, only one of lines 2, 5, 8, or 10 is executed, no matter what
situation the robot is in. Furthermore, when this code is read from top to bottom, the
first test that returns true determines which statement is executed.
ifƒ(this.frontIsBlocked())
{ƒthis.turnAround();
}ƒelseƒifƒ(this.canPickThing())
{ƒthis.turnRight(); Cascading-if
}ƒelseƒifƒ(this.leftIsBlocked())
{ƒthis.turnLeft();
}ƒelse
{ƒthis.move();
}
229
(figure 5-7)
Flowchart illustrating a
nested if statement true frontIs false
Blocked
turnLeft move
The switch statement is similar to the cascading-if statement in that both are
designed to choose one of several alternatives. The switch statement is more
restrictive in its use, however, because it uses a single value to choose the alternative
to execute. The cascading-if can use any expressions desired. This restriction is
also the switch statement’s strength: the reader knows that the decision is based on
a single value.
In Section 5.2.1, we used a series of if statements to direct a Robot based on the num-
ber of things on the intersection. Either a cascading-if or a switch statement would
be a better choice because it makes clear that the Robot should perform only one of
the actions.
The two code fragments shown in Figure 5-8 both implement a variant of the problem
just described and behave exactly the same way when executed.
CHAPTER 5 | MORE DECISION MAKING 230
} else case 2:
{ this.turnAround(); this.turnLeft();
break;
}
default:
this.turnAround();
}
The break statement causes execution to continue after the end of the switch state-
ment. If the break statement is not included, execution “falls through” to the next
case of the switch. For example, in the following code, the break is omitted from the
first case. The result is that a Robot on an intersection with zero Things will move and
turn right because it “falls through” to the second case. However, a Robot on an
avenue with one thing will only turn right.
switchƒ(this.countThingsHere())
{ƒcaseƒ0:
ƒƒƒƒthis.move();ƒ//ƒFall though
ƒƒcaseƒ1:
ƒƒƒƒthis.turnRight();
ƒƒƒƒbreak;
}
This behavior is sometimes useful if the robot should do exactly the same thing for two
or more cases, but this is rare. In reality, the break is often forgotten and is a source of
bugs. If you choose to use the switch statement, it is a good idea to use a compiler set-
ting to warn you if you omit a break statement. If you deliberately omit a break state-
ment, be sure to document why.
The default keyword may be used instead of case to indicate the group of actions that
occurs if none of the cases match. It is equivalent to the last else in the cascaded-if
statement.
The value used in a switch statement must be countable. Integers, as shown in Figure 5-8,
fit this description. In later chapters, we will learn about characters and enumerations that
also work in a switch statement.
231
Consider a situation in which karel is facing east. It is known that in the distance are
things and walls. We want karel to travel forward, stopping at the first thing or wall
it comes to. Figure 5-9 shows two such situations.
(figure 5-9)
The robot might need to move zero or more times to reach the first thing or wall, so a
while statement is appropriate. To construct it, we follow the four-step process dis-
cussed earlier. The first step is already apparent: the body of the loop should contain a
move statement.
The second step in the process requires some thought. We want the robot to stop when
it reaches a thing or a wall. We have a predicate, canPickThing, to determine if it is
beside a thing. Another predicate, frontIsClear, will determine when a wall is
reached. But we do not have a predicate that combines these two tests.
Fortunately, programming languages have operators that can combine Boolean expres-
sions into more complex expressions. You are already familiar with operators from the
mathematics you have studied: plus, minus, multiply, and divide are all operators that
combine two arithmetic expressions to create a more complex expression. The equiva-
lents for Boolean expressions are the and and or operators.
KEY IDEA We often use Boolean operators in our everyday language. You might say, “I will go swim-
Both sides of an ming if the weather is hot and sunny.” From this statement, I know that if the weather is
“and” must be true to cloudy, you will not go swimming. Similarly, if the weather is cool, you will not go swim-
do the action.
ming. In order to go swimming, both expressions joined by “and” must be true.
CHAPTER 5 | MORE DECISION MAKING 232
On the other hand, if you say “I will go swimming if it is hot or sunny,” we might ques- KEY IDEA
tion your sanity. With this statement, you might go swimming in a thunderstorm (if it Only one side of an
happens to be hot that day) or you might go swimming in a frozen pond (if it happens to “or” is required to be
true to do the action.
be a sunny winter day). The “or” operator requires a minimum of one of the two tests to
be true. Of course, if it happens to be both hot and sunny, you would still go swimming.
Java’s logical operators work in the same way except that instead of writing “and,” we KEY IDEA
write &&, and instead of writing “or,” we write ||. Like English, an expression includ- In Java, write “and”
ing && is true only if both expressions it joins are true. For an expression including || as && and write “or”
as ||.
to be true, one or both of the expressions it joins must be true.
In the earlier problem, we want karel to stop when it’s beside a thing or its front is
blocked. This can be written in Java as follows:
karel.canPickThing()ƒ||ƒkarel.frontIsBlocked()
Step 2 of the four-step process says that we should negate this expression to find out
when the loop should continue. We can negate the entire expression by wrapping it in
parentheses and using the ! operator, as follows:
1 whileƒ(!(karel.canPickThing()ƒ||ƒkarel.frontIsBlocked()))
2 {ƒkarel.move();
3 }
The informal descriptions of && and || given previously mention the “expressions it
joins.” Let’s be more precise about what constitutes a legal expression. There are four rules:
1. Literal values such as true, false, and 50 are legal expressions. The type of
the expression is the type of the literal. For example, boolean and int in these
examples.
2. A variable is a legal expression. The type of the expression is the type of the
variable.
3. A method call whose arguments are legal expressions with the appropriate
types is a legal expression. The type of the expression is the return type of the
method.
4. An operator whose operands are legal expressions with the appropriate types is
a legal expression. The type of the expression is given by the return type of the
operator. Operators include &&, ||, !, the comparison operators, and the arith-
metic operators. Their operands are the expressions they operate on.
The first two rules just set the groundwork. The power is all in the last two rules,
which let us combine expressions to any level of complexity. For example, within the
Robot class, this.canPickThing() and this.frontIsClear() are both expres-
sions (by rule 3). These two expressions can be joined with an operator such as && to
233
this.canPickThing()ƒ&&ƒthis.frontIsClear()
Two other expressions are this.getAvenue() and 0 (rules 3 and 1). They can be
joined by the operator > to form a new expression (by rule 4). This expression has type
boolean and can be combined with the previous expression by rule 4. For example,
using || gives the following expression:
this.canPickThing()ƒ&&ƒthis.frontIsClear()ƒ||ƒ
this.getAvenue()ƒ>ƒ0
This expression can also be combined with other expressions in ever-increasing complexity.
Suppose we have a complex expression. How can we evaluate it to find its value? We
can use a technique we’ll call evaluation diagrams to annotate the expression.
The second step is to repeatedly draw an oval around an operator with its operands or
a method with its arguments until the entire expression is enclosed in a single oval. For
each oval drawn:
➤ Verify that all operands and arguments enclosed in the new oval already have
ovals around them, from either the first step or a previous iteration of the sec-
ond step.
➤ Verify that the type of each operand or argument is appropriate for the opera-
tor or method call being enclosed in the new oval. For example, you may not
draw an oval around the && operator if one of its operands has type int. If
such a situation occurs, it means that the expression as a whole is not well-
formed and will be rejected by the Java compiler. Some operators, such as
negation (!), use only one operand. In that case, the oval will include only the
operator and one operand.
➤ Write the type returned by the operator or method above the oval and the
value returned below the oval.
Figure 5-10 shows the process of constructing an evaluation diagram. The top cell of
the diagram shows the robot’s situation. The bottom four cells of the diagram illustrate
the series of steps required to construct the diagram. From the last step we conclude
that in the given situation, the expression returns true.
CHAPTER 5 | MORE DECISION MAKING 234
0 1 2 (figure 5-10)
Evaluating a Boolean
expression using an
Robot’s situation
evaluation diagram
boolean boolean int int
this.canPickThing() && this.frontIsBlocked() || this.getAvenue() > 0
false true 1 0
Step one in constructing the evaluation diagram
boolean
boolean boolean int int
this.canPickThing() && this.frontIsBlocked() || this.getAvenue() > 0
false true 1 0
true
First iteration of step two in constructing the evaluation diagram
boolean boolean
boolean boolean int int
this.canPickThing() && this.frontIsBlocked() || this.getAvenue() > 0
false true 1 0
false true
Second iteration of step two in constructing the evaluation diagram
boolean
boolean boolean
boolean boolean int int
this.canPickThing() && this.frontIsBlocked() || this.getAvenue() > 0
false true 1 0
false true
true
Last iteration of step two in constructing the evaluation diagram
Operator Precedence
You may have noticed that some discretion was involved in choosing which operator
to include in an oval. For example, in the second iteration of step two in Figure 5-10,
we could have drawn the oval around the || operator instead of the && operator. The
resulting evaluation diagram is shown in Figure 5-11. Notice that the value of the
expression as a whole is false rather than true.
235
The operators are chosen in order of their precedence. Precedence denotes the priority
they are given when evaluating the expression. The operators we have encountered,
from the highest precedence to the lowest, are listed in Table 5-3. We see that && is
listed before ||. Therefore, the expression diagram in Figure 5-11 is incorrect because
it drew an oval around || when && should have been chosen.
+ƒƒ-ƒ 11
<ƒƒ>ƒƒ<=ƒƒ>=ƒ 9
==ƒƒ!=ƒ 8
&&ƒ 4
||ƒ 3
It may be that the normal precedence rules are not what you want. For example, you
really do want the answer shown in Figure 5-11. In that case, override the precedence
rules with parentheses—just like you would in an arithmetic expression. The following
example has an expression diagram as shown in Figure 5-11:
this.canPickThing()ƒ&&ƒ
ƒƒƒƒƒƒƒ(this.frontIsBlocked()ƒ||ƒthis.getAvenue()ƒ>ƒ0)
LOOKING AHEAD If an expression has two or more operators with equal precedence, circle them in order
These rules are not from left to right.
yet complete. We will
expand them in
Chapter 7.
CHAPTER 5 | MORE DECISION MAKING 236
Perhaps we want karel to turn around if the number of things in its backpack is either
1 or 2. A direct translation of this English statement into Java might be as follows:
ifƒ(karel.countThingsInBackpack()ƒ==ƒ1ƒ||ƒ2)
{ƒkarel.turnAround();
}
A correct expression for determining if karel has one or two things in its backpack is
as follows:
ifƒ(karel.countThingsInBackpack()ƒ==ƒ1ƒ||
ƒƒƒƒkarel.countThingsInBackpack()ƒ==ƒ2)
{ƒkarel.turnAround();
}
Sometimes Boolean expressions can become quite complicated as they are combined
and negated. Simplifying them can be a real service, both to yourself as the program-
mer and to others who need to understand your code.
Simplifying Negations
!(this.getAvenue()ƒ==ƒ0) this.getAvenue()ƒ!=ƒ0
!(this.getAvenue()ƒ!=ƒ0) this.getAvenue()ƒ==ƒ0
De Morgan’s Laws
When negations involve more complex expressions, it’s easy to get mixed up. Faced
with this problem, Augustus De Morgan (1806–1871) introduced what have become
known as De Morgan’s Laws, which formalize the process of finding the opposite form
of a complex test. De Morgan’s Laws state the following equivalencies ( means that
the expression on the left is equivalent to the expression on the right):
!(karel.canPickThing()ƒ||ƒ
ƒƒƒƒƒƒ(karel.leftIsBlocked()ƒ&&ƒkarel.rightIsBlocked()))
!karel.canPickThing()ƒ&&ƒ
ƒƒƒƒƒƒ(!karel.leftIsBlocked()ƒ||ƒ!karel.rightIsBlocked())
This can be simplified again by restating each negated predicate, using new predicates
if necessary:
!karel.canPickThing()ƒ&&ƒ
ƒƒƒƒƒƒ(karel.leftIsClear()ƒ||ƒkarel.rightIsClear())
CHAPTER 5 | MORE DECISION MAKING 238
Suppose you have a robot in the situation shown in Figure 5-13 that is about to exe-
cute the following code fragment:
ifƒ(this.frontIsClear()ƒ&&ƒthis.thingOnSixthAvenue())
{ƒthis.putAllThings();
}
0 2 3 4 5 6 (figure 5-13)
0
Time-consuming test
We can observe two things. First, the robot can find out quickly if its front is clear. On
the other hand, it will take a relatively long time to move all the way to Sixth Avenue
to find out if a Thing is there. Second, when the robot is in a situation like this, it
doesn’t need to waste its time checking Sixth Avenue. The definition of “and” says that
if the first part of the test is false (the robot’s front is not clear), then the entire test will
be false. It doesn’t matter whether the second part of the test is true or false.
With these two observations, we can conclude that the following is a more efficient
way to write the previous code fragment:
ifƒ(this.frontIsClear())
{ƒifƒ(this.thingOnSixthAvenue())
ƒƒ{ƒthis.putAllThings();
ƒƒ}
}
This fragment will only cause the robot to check Sixth Avenue if that test will really
make a difference to the robot’s behavior.
However, running these two code fragments in the situation shown in Figure 5-13 results KEY IDEA
in exactly the same behavior. In neither case does the robot check Sixth Avenue. This is Java only performs a
because Java uses short-circuit evaluation. When evaluating a Boolean expression test if it needs to.
test1ƒ&&ƒtest2, Java will only execute test2 if test1 is true. If test1 is false, Java
knows that executing test2 is a waste of time and doesn’t do it.
Sometimes we know before a loop begins exactly how many times we want it to exe-
cute. For example, consider a problem in which a robot named suzie must move
clockwise around a square defined by walls, as shown in Figure 5-14.
(figure 5-14) 0 1 2 3 4 5
0
Moving around a square
1
To solve the problem, suzie must traverse exactly four sides of the square—no more,
no less. For each side, suzie must move exactly five times. At each corner, suzie
must turn left exactly three times.
KEY IDEA A while loop works well when statements must be repeated an unknown number of
Use while when the times—while some condition is true. However, suzie’s situation is different. Here, we
number of iterations know exactly how many times the statements must be executed even before the loop
is unknown. Use for
begins. Java includes the for statement just for such situations.
when the number
is known.
The Form of the for Statement
The form of the for statement used to repeat statements a fixed number of times is as
follows:
forƒ(intƒ«counter»ƒ=ƒ0;ƒ«counter»ƒ<ƒ«limit»;ƒ
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ«counter»ƒ=ƒ«counter»ƒ+ƒ1)
{ƒ«statementsƒtoƒrepeat»
}
CHAPTER 5 | MORE DECISION MAKING 240
where
➤ «statementsƒtoƒrepeat» are the instructions to be executed each time
through the loop. They are called the body of the loop, the same term used for
the statements in the while loop.
➤ «counter» is an identifier or name, such as numTurns or sideCount.
➤ «limit» is the number of times «statementsƒtoƒrepeat» should be
executed.
publicƒvoidƒturnRight()
{ƒforƒ(intƒturnsƒ=ƒ0;ƒturnsƒ<ƒ3;ƒturnsƒ=ƒturnsƒ+ƒ1)
Counted Loop
ƒƒ{ƒthis.turnLeft();
ƒƒ}
}
In the for loop, turns is the «counter» that keeps track of how many times the
turnLeft method has been executed. The «limit», or total number of times we
want turnLeft to execute, is 3.
The for statement is nothing more than an abbreviation of a particular form of the KEY IDEA
while loop. The component parts of the for statement can be rearranged to create a A for statement is a
while loop that behaves in exactly the same way. shortcut for a
common form of the
{ while loop.
ƒƒintƒ«counter»ƒ=ƒ0;
ƒƒwhileƒ(«counter»ƒ<ƒ«limit»)
ƒƒ{ƒ«statementsƒtoƒrepeat»
ƒƒƒƒ«counter»ƒ=ƒ«counter»ƒ+ƒ1;
ƒƒ}
}ƒ//ƒNote: «counter» is not available beyond this closing brace
To gain further comfort with the for statement, let’s solve the problem illustrated in
Figure 5-14. We will extend Robot to create the class SquareMover. We can use step-
wise refinement and pseudocode to solve the problem. To move around the square, the
robot needs to move along four sides:
To move along one side, the robot needs to move five times:
To move along one side:
ƒfor (5 times)
241
To turn right:
ƒfor (3 times)
ƒ{ turn left
ƒ}
In each of these refinements, the robot must perform an action a number of times—and
that number is known before the loop begins executing. Such circumstances are ideal
for using a for statement. The class definition corresponding to this pseudocode is
shown in Listing 5-6.
29 ƒƒ{ƒforƒ(intƒturnsƒ=ƒ0;ƒturnsƒ<ƒ3;ƒturnsƒ=ƒturnsƒ+ƒ1)
30 ƒƒƒƒ{ƒthis.turnLeft();
31 ƒƒƒƒ}
32 ƒƒ}
33 }
Java provides a shortcut for «counter»ƒ=ƒ«counter»ƒ+ƒ1. This statement occurs so LOOKING AHEAD
frequently that Java allows the abbreviation «counter»++, which means “add 1 to the This, and other
value stored in «counter».” Another abbreviation is «counter»ƒ+=ƒ«expression». It shortcuts, are
discussed in
means to add the value on the right side to the variable on the left.
Section 7.2.5.
Finally, it should be noted that the for statement is more flexible than implied by these
examples. The «counter» need not start at 0; any Boolean expression can be used for
the test; and «counter»ƒ=ƒ«counter»ƒ+ƒ1 can be replaced by a more general state-
ment. In particular, the for statement’s template can be generalized as follows:
forƒ(«initialization»;ƒ«test»;ƒ«update»)
{ƒ«statementsƒtoƒrepeat»
}
The while loop always performs its test before the body of the loop is executed. If the KEY IDEA
test happens to be false right away, the loop’s body may not be executed at all. Another A do-while loop
loop, the do-while loop, performs its test after the loop body executes. This means always executes at
that it will always execute at least once. least once.
do
{ƒ«statementsƒtoƒrepeat»
}ƒwhileƒ(«test»);
243
It is unusual to have a loop that always executes at least once, and so the do-while
loop itself is unusual. A search of three projects2 totaling more than 20,000 lines of
code revealed not even one do-while loop.
The while-true loop is the most flexible loop available in Java. The other looping
forms, including the for loop, test either at the beginning of the loop’s body or at the
end. The while-true can test any place you want—and can even test several times
during the body’s execution.
Why is this history lesson relevant? The while-true loop is less structured than the
other loops because it allows multiple exits from the loop. Your instructor may feel
uncomfortable with that and not want you to use such loops. On the other hand,
many programmers believe that it strikes an excellent balance between the rigor of
one-entry/one-exit structured programming and the flexibility to solve problems eas-
ily. One such person is Eric Roberts, a noted computer science educator who wrote a
paper3 on the topic. Another is the designer of the Turing programming language, in
which while-true is used for all forms of looping except the for loop.
2 The becker library (10,500 lines), a testing tool named junit3.8.1 (5,000 lines), and an implemen-
of the Twenty-sixth SIGCSE Technical Symposium on Computer Science Education, ACM Press,
March 1995.
CHAPTER 5 | MORE DECISION MAKING 244
At first glance, a while-true loop looks like it will execute forever. That’s because the
test is the value true—which can never be false and cause the loop to end. That
observation tells us there must be another way out of the loop.
The loop uses an if statement combined with a break statement to end the loop. It’s
common for the break to be the only statement in the if, so it can all be put on a sin-
gle line. It uses the following form:
1 whileƒ(true)ƒƒƒƒ// use a break statement to exit!
2 {ƒ«optionalƒstatements1»
3 ƒƒifƒ(«test»)ƒƒƒƒ{ƒƒƒƒƒbreak;ƒƒ}
4 ƒƒ«optionalƒstatements2»
5 }
The test in line 1 is always true and so the program always enters the loop. When exe-
cution reaches the if statement in line 3, it performs its test. If the test is false, exe-
cution resumes with the optional statements in line 4. If the test is true, the break
instruction causes execution to resume after the end of the loop.
This flow of control is summarized in Figure 5-15. All the statements in the loop are exe-
cuted until the «test» is true. At that point, the break statement causes the loop to end.
(figure 5-15)
statements1 Flowchart for the
while-true loop
? false
true
statements2
The statements before the test (line 2 in the preceding code) might be omitted, in which
case the loop is like a standard while loop but with the test negated. On the other
hand, if the statements after the test (line 4 in the preceding code) are omitted, the loop
is like a do-while loop.
The loop can also have several tests. This may make the loop easier to understand than LOOKING AHEAD
an equivalent while loop with a compound Boolean expression for the test. See Programming
Exercise 5.7.
245
Consider again the fence-post problem shown in Figure 5-2. We wanted a robot to pick
up all of the things between its current location and a wall. We solved it with the fol-
lowing method, noting that we needed an extra call to pickThing after the loop.
publicƒvoidƒclearThingsToWall()ƒ
{ƒwhileƒ(this.frontIsClear())ƒ
ƒƒ{ƒthis.pickThing();ƒ
Loop-and-a-Half
ƒƒƒƒthis.move();ƒ
ƒƒ}
ƒƒthis.pickThing();
}
The extra call to pickThing is needed because we need the robot to pick up four
things but move only three times.
publicƒvoidƒclearThingsToWall()
{ƒwhileƒ(true)
ƒƒ{ƒthis.pickThing();
Loop-and-a-Half
ƒƒƒƒifƒ(this.frontIsBlocked()) { break; }
ƒƒƒƒthis.move();
ƒƒ}
}ƒ
When only two things remain to be picked up, this code executes as illustrated in
Figure 5-16.
(figure 5-16)
while (true)
Illustrating the execution { this.pickThing();
if (this.frontIsBlocked()) { break; }
of a while-true loop this.move();
}
while (true)
{ this.pickThing();
if (this.frontIsBlocked()) { break; }
this.move();
}
while (true)
{ this.pickThing();
if (this.frontIsBlocked()) { break; }
this.move();
}
while (true)
{ this.pickThing();
if (this.frontIsBlocked()) { break; }
this.move();
}
CHAPTER 5 | MORE DECISION MAKING 246
The while-true loop provides a general solution to the fence-post problem, also
known as the loop-and-a-half problem. Solving these kinds of problems with a tradi-
tional while statement requires repeating some of the code, because the loop must
execute an extra “half” iteration to perform the last action. In this case, the repeated
code is the call to pickThing. By putting the test inside the body of the loop, the
repeated code is no longer needed.
We have studied a number of different kinds of looping statements. Table 5-5 provides
guidelines on when to use each kind.
a parameter refers to the number of times use a count-down loop or a for statement. Guidelines for choosing a
the loop will execute and the value is not looping statement
needed for other purposes…
the number of times the loop will execute use a for statement.
is known before the loop is entered...
the loop executes an extra “half ” time for use a while-true loop.
a fence-post problem…
5.6 CODING
The following subsections explore these ideas more carefully.
WITH
5.6.1 Use Stepwise Refinement
STYLE
A long list of statements inside the body of a loop or if statement can cause the reader
to lose track of the loop or if statement as a whole. It should be easy for the reader to
remember when a loop terminates or what case is being handled by an if statement.
Long bodies in either structure make doing so difficult.
Using stepwise refinement naturally breaks long bodies into smaller steps. Some pro-
grammers put the entire body into a helper method so that the loop or if statement
contains only one line—the invocation of the helper method.
One of the most crucial aspects of good style is to keep tests easy to understand. First,
avoid negations if you can because people usually find them harder to understand than
positive statements. It is easier to understand whileƒ(this.frontIsBlocked())
than whileƒ(!this.frontIsClear()), for example. This style may mean that you
need to define your own predicate to put the test in a positive form.
A second way to keep tests easy to understand is to use predicates with descriptive
names. For example, ifƒ(this.isFacingSouth()) is easier to understand than
ifƒ(this.getDirection()ƒ==ƒDirection.SOUTH).
If you will be using the same test several times in the program, writing the predicate is
particularly worthwhile.
A third possibility, applicable to if-else statements, is to rewrite them with the goal
of making them simpler and easier to understand. Rewriting an if statement should
not change the execution of the program, only the way in which it is written. These
techniques are explored in the following subsections.
Test Reversal
When the robot’s front is not clear, it turns left. Otherwise it moves forward. This can be KEY IDEA
rewritten to use the opposite test if we interchange the then-clause and the else-clause: An if statement
executes the same
ifƒ(this.frontIsClear()) way if you negate the
{ƒthis.move(); test and swap the
}ƒelse then-clause with the
{ƒthis.turnLeft(); else-clause.
}
This code is easier to read and understand, primarily because we eliminated the nega-
tion in the test. Another way to make this code easier to read is to replace
!this.frontIsClear() with a new predicate, this.frontIsBlocked().
When written this way, it’s easy to see that we can drop the else-clause and use only the
Once or Not At All pattern.
ifƒ(!this.canPickThing())
{ƒthis.turnLeft();
}
Bottom Factoring
ƒƒthis.turnAround(); }ƒelse
}ƒelse {ƒthis.putThing();
{ƒthis.putThing(); }
ƒƒthis.turnAround(); this.turnAround();ƒƒ
}
249
5.6 CODING
Both code fragments result in the same final situation. In both fragments, the robot fin-
ishes by turning around. The code on the right, however, makes this more obvious by
moving this.turnAround() outside of the if-else statement. Only the actions
WITH
that actually depend on the test are left inside the if-else statement.
STYLE
Moving identical lines of code that appear at the end of both the then-clause and the
else-clause to just after the if-else statement is called bottom factoring.
Top Factoring
When identical code appears at the beginning of the then-clause and the else-clause, we
may be able to top factor. Top factoring means moving identical code from the begin-
ning of the then- and else-clauses to just before the if-else statement. For example:
ifƒ(this.canPickThing()) this.turnAround();
{ƒthis.turnAround(); ifƒ(this.canPickThing())
ƒƒthis.pickThing(); {ƒ
}ƒelse ƒƒthis.pickThing();
{ƒthis.turnAround(); }ƒelse
ƒƒthis.putThing(); {ƒ
} ƒƒthis.putThing();
}
Both versions of this code will always result in the same final situation. In both ver-
sions, the robot always turns around, regardless of the test’s result.
Top factoring is not as simple as bottom factoring, however. If the identical lines of
code affect the outcome of the test, they cannot simply be moved. Consider the follow-
ing example:
ifƒ(this.isFacingNorth()) this.turnAround();
{ƒthis.turnAround(); ifƒ(this.isFacingNorth())
ƒƒthis.pickThing(); {ƒ
}ƒelse ƒƒthis.pickThing();
{ƒthis.turnAround(); }ƒelse
ƒƒthis.putThing(); {ƒ
} ƒthis.putThing();
}
KEY IDEA Suppose the robot’s initial situation is facing north on an intersection with a thing.
Top factor only if the Executing the code on the left leaves the robot facing south and having picked up one
code moved outside thing. Executing the code on the right also leaves the robot facing south, but this time
the if statement has the robot has put a thing down rather than picking a thing up.
no effect on the test.
CHAPTER 5 | MORE DECISION MAKING 250
Another important stylistic rule is to line up braces vertically and indent the bodies of
loops. This rule is the same as appropriately indenting methods.
If you read code written by someone else, you may notice that sometimes braces are omit-
ted in an if or while statement. When the body consists of a single statement, the braces
surrounding it are optional. For example, both of the following statements are legal:
ifƒ(this.frontIsClear()) whileƒ(this.frontIsClear())
ƒƒthis.move(); ƒƒthis.move();
else
ƒƒthis.turnRight();
There are, however, dangers in leaving out the braces. The first comes from adding
code. Suppose that after executing the if statement we realize that if the front is not
clear, the robot should turn right and move. We might add an extra statement, as in the
following example:
ifƒ(this.frontIsClear())
ƒƒthis.turnLeft();
else
ƒƒthis.turnRight();
ƒƒthis.move();
In spite of the indentation, the move will occur whether the front is clear or not, which
is not what was desired. Why? Braces should group the new line with the instruction to
turn right. Without the braces, a compiler interprets the preceding code as follows:
ifƒ(this.frontIsClear())
{ƒthis.turnLeft();
}ƒelse
{ƒthis.turnRight();
}
this.move();
The compiler is interpreting the code correctly. The mistake is the programmer’s in
using white space to imply an incorrect program structure.
The second danger is called a dangling else. If braces are not included, where the else
goes can be confusing. For example, consider the following fragment:
ifƒ(this.frontIsClear())
ƒƒifƒ(this.canPickThing())
ƒƒƒƒthis.pickThing();
else
ƒƒthis.turnLeft();
}
251
ifƒ(this.frontIsClear())
{ƒifƒ(this.canPickThing())
TO
ƒƒƒƒthis.pickThing();
DRAW
ƒƒelse
ƒƒƒƒthis.turnLeft();
}
If we want to write code that does what the indentation implies, we are forced to add
braces so that the if without an else is clearly identified, as follows:
ifƒ(this.frontIsClear())
{ƒifƒ(this.canPickThing())
ƒƒƒƒthis.pickThing();
}ƒelse
ƒƒƒƒthis.turnLeft();
1 importƒjavax.swing.*;
2 importƒjava.awt.*;
ch05/lineArt/ 3
4 /** Create a component that paints our “art.”
5 * @author Byron Weber Becker */
6 publicƒclassƒArtComponentƒextendsƒJComponent
7 {
8 ƒƒpublicƒArtComponent()
9 ƒƒ{ƒsuper();
10 ƒƒƒƒthis.setPreferredSize(newƒDimension(300,300));
11 ƒƒ}
12
13 /** Paint the component with our “art.” */
14 ƒƒpublicƒvoidƒpaintComponent(Graphicsƒg)
15 ƒƒ{ƒsuper.paintComponent(g);
CHAPTER 5 | MORE DECISION MAKING 252
16
17 // Standard stuff to scale the image.
18 ƒƒƒƒGraphics2Dƒg2ƒ=ƒ(Graphics2D)ƒg;
19 ƒƒƒƒg2.scale(this.getWidth()/11,ƒthis.getHeight()/11);
20 ƒƒƒƒg2.setStroke(newƒBasicStroke(1.0F/this.getWidth()));
21
22 // draw our “art”
23 ƒƒƒƒg2.drawLine(1,ƒ1,ƒ10,ƒ10);
24 ƒƒ}
25 }
With a for loop, we can draw a shape over and over again. But if we replace line 23 LOOKING AHEAD
with the following loop, we draw the same line repeatedly in the same place, having no The number 10 has a
visible effect. special significance in
this code. In Chapter 6
forƒ(intƒlineƒ=ƒ1;ƒlineƒ<=ƒ10;ƒlineƒ=ƒlineƒ+ƒ1) we will see a better
{ƒg2.drawLine(1,ƒ1,ƒ10,ƒ10); way to handle it using
} named constants.
What we need is a way to change the position of the line in each iteration of the loop.
One way to change the position of the line with each iteration of the loop is to use
line, the loop counter, as a parameter to drawLine. The parameters to drawLine are
integers, and line holds integers. The value of this integer changes from 1 to 10 as the
loop executes. With each iteration of the loop, a different value is passed to drawLine,
changing the position of each of the 10 lines.
For example, we can replace line 23 in Listing 5-7 with the following loop:
forƒ(intƒlineƒ=ƒ1;ƒlineƒ<=ƒ10;ƒlineƒ=ƒlineƒ+ƒ1)
{ƒg2.drawLine(1,ƒ1,ƒ10,ƒline);
}
Counted Loop
This loop yields the image shown in Figure 5-17. Each of the 10 iterations of the loop
draws a line. The location of the left end-point is fixed, but the right end-point’s loca-
tion varies according to the current value stored in line, the loop’s counter variable.
253
forƒ(intƒlineƒ=ƒ1;ƒlineƒ<=ƒ10;ƒlineƒ=ƒlineƒ+ƒ1)
{ƒg2.drawLine(1,ƒline,ƒ10,ƒline);
}
TO
DRAW
(figure 5-17)
In Section 5.3 we saw that if statements and while statements can be nested—that is,
one can be placed inside the other. for statements control any number of if, while,
and for statements. As such, for statements may be nested, just like if and while. We
could, for example, replace the single drawLine command at line 23 in Listing 5-7 with
the following nested loop.
forƒ(intƒleftƒ=ƒ1;ƒleftƒ<=ƒ5;ƒleftƒ=ƒleftƒ+ƒ1)
{ƒƒforƒ(intƒrightƒ=ƒ1;ƒrightƒ<=ƒ10;ƒrightƒ=ƒrightƒ+ƒ1)
ƒƒƒ{ƒg.drawLine(1,ƒleft,ƒ10,ƒright);
Counted Loop
ƒƒƒ}
}
These five lines cause a total of 50 lines to be drawn. The outer loop executes five
times. In each of the five iterations of the outer loop, the inner loop executes com-
pletely, performing 10 iterations each time.
The image drawn after one iteration of the outer loop looks like Figure 5-18a. After
two iterations of the outer loop, it looks like Figure 5-18b, and so on. Each iteration of
the outer loop draws one more spray of lines (see Figures 5-18c and d). Each spray is
drawn by the inner loop. In each iteration through the outer loop, the variable left
has a value one larger than the previous iteration. When passed as an argument to
drawLine, the coordinates of the left end of the line change.
CHAPTER 5 | MORE DECISION MAKING 254
(figure 5-18)
Images produced by a
nested loop after 1, 2, 3,
and 4 iterations of the
outer loop
The initial value in a for loop need not be 0. For example, the following nested loop
starts the outer loop at 3 instead of 0. The result is shown in Figure 5-19.
forƒ(intƒleftƒ=ƒ3;ƒleftƒ<=ƒ7;ƒleftƒ=ƒleftƒ+ƒ1)
{ƒforƒ(intƒrightƒ=ƒ1;ƒrightƒ<=ƒ10;ƒrightƒ=ƒrightƒ+ƒ1)
ƒƒ{ƒg.drawLine(1,ƒleft,ƒ10,ƒright);
ƒƒ}
}
(figure 5-19)
forƒ(intƒleftƒ=ƒ1;ƒleftƒ<=ƒ10;ƒleftƒ=ƒleftƒ+ƒ1)
{ƒforƒ(intƒrightƒ=ƒ1;ƒrightƒ<=ƒleft;ƒrightƒ=ƒrightƒ+ƒ1)
ƒƒ{ƒg.drawLine(1,ƒleft,ƒ10,ƒright);
TO
ƒƒ}
DRAW
}
The first time the outer loop executes, the variable left has a value of 1. This value
limits the inner loop to executing 1 time. The second time through the outer loop,
left has a value of 2. The inner loop draws a spray consisting of two lines. The
third time through the outer loop, left has a value of 3 and so the inner loop draws
a spray of 3 lines. See Figure 5-20.
(figure 5-20)
We can also add if statements inside a loop. Consider this program fragment:
forƒ(intƒleftƒ=ƒ1;ƒleftƒ<=ƒ10;ƒleftƒ=ƒleftƒ+ƒ1)
{ƒifƒ(leftƒ<=ƒ5)
ƒƒ{ƒg2.setColor(Color.WHITE);
ƒƒ}ƒelse
ƒƒ{ƒg2.setColor(Color.BLACK);
ƒƒ}
ƒƒforƒ(intƒrightƒ=ƒ1;ƒrightƒ<=ƒ10;ƒrightƒ=ƒrightƒ+ƒ1)
ƒƒ{ƒg.drawLine(1,ƒleft,ƒ10,ƒright);
ƒƒ}
}
The if statements test the loop control variable against an integer, just as we tested the
result of an integer query such as getAvenue() against an integer. The drawing color
is set based on the test’s outcome. The result is shown in Figure 5-21a, in which the
first five sprays are white and the last five are black. The background is set to a darker
shade of gray to show the white lines more effectively.
CHAPTER 5 | MORE DECISION MAKING 256
(figure 5-21)
a) Color according to the value of left b) Color according to the value of left + right
One more possibility is to perform a slightly more complex test for the color. It is pos-
sible to compare two integer expressions in the if statement’s test. In the following
example, the if statement is moved into the inner loop. It makes the line white if the
sum of the values contained in left and right is greater than 10, and black other-
wise. The result is shown in Figure 5-21b.
forƒ(intƒleftƒ=ƒ1;ƒleftƒ<=ƒ10;ƒleftƒ=ƒleftƒ+ƒ1)
{ƒforƒ(intƒrightƒ=ƒ1;ƒrightƒ<=ƒ10;ƒrightƒ=ƒrightƒ+ƒ1)
ƒƒ{ƒifƒ(leftƒ+ƒrightƒ>ƒ10)
ƒƒƒƒ{ƒg2.setColor(Color.WHITE);
ƒƒƒƒ}ƒelse
ƒƒƒƒ{ƒg2.setColor(Color.BLACK);
ƒƒƒƒ}
ƒƒƒƒg.drawLine(1,ƒleft,ƒ10,ƒright);
ƒƒ}
}
We can also use Java’s remainder operator (%) in the test. The remainder operator gives
the remainder when one number is divided into another. For example, 4ƒ%ƒ2 is 0
because 2 goes into 4 an even number of times. On the other hand, 5ƒ%ƒ2 is 1 because
when 5 is divided by 2, 1 is left over. This mathematical relationship gives us an easy
test for whether a number is even or odd. For example, if leftƒ%ƒ2 is 0, then the
value contained in left is even. If leftƒ%ƒ2 is 1, then the value contained in left is
odd. In the following program fragment, this fact is used to color alternating sprays
differently. The result is shown in Figure 5-22.
257
TO
DRAW
forƒ(intƒleftƒ=ƒ1;ƒleftƒ<=ƒ10;ƒleftƒ=ƒleftƒ+ƒ1)
{ƒifƒ(leftƒ%ƒ2ƒ==ƒ0)
ƒƒ{ƒg2.setColor(Color.WHITE);
ƒƒ}ƒelse
ƒƒ{ƒg2.setColor(Color.BLACK);
ƒƒ}
ƒƒforƒ(intƒrightƒ=ƒ1;ƒrightƒ<=ƒ10;ƒrightƒ=ƒrightƒ+ƒ1)
ƒƒ{ƒg.drawLine(1,ƒleft,ƒ10,ƒright);
ƒƒ}
}
As you can see, selection and repetition statements such as if, while, and for can be
combined in many ways.
5.8 Patterns
Name: Loop-and-a-Half
Context: A loop is used for a variation of the fence-post problem; that is, some of the
repeated actions (the “fence-post actions”) must be performed one more time than the
other repeated actions (the “fence-section actions”).
Solution: There are two standard solutions. The first repeats part of the code either
before or after the loop, as appropriate. Templates for two variants follow:
«fencePostƒactions» whileƒ(«booleanExpression»)
whileƒ(«booleanExpression») {ƒ«fencePostƒactions»
{ƒ«fenceSectionƒactions» ƒƒ«fenceSectionƒactions»
ƒƒ«fencePostƒactions» }
} «fencePostƒactions»
CHAPTER 5 | MORE DECISION MAKING 258
The second solution avoids the repeated code with a while-true loop, as shown in
the following template:
whileƒ(true)
{ƒ«fencePostƒactions»
ƒƒifƒ(«booleanExpression»)ƒƒ{ƒƒbreak;ƒƒ}
ƒƒ«fenceSectionƒactions»
}
Consequences: The «fencePostƒactions» are executed one more time than the
«fenceSectionƒactions». The «fencePostƒactions» are always executed at
least once.
Related Pattern: This pattern is a variation of the Zero or More Times pattern. That
pattern is used when all of the repeated steps are executed an equal number of times.
Context: You need to store a value that is used in the task being performed rather than
as an attribute of the object. The value is only used in one method and perhaps in the
methods it invokes—for example, a variable to control a loop, to store a temporary
result for use in later calculations, or to accumulate a value to return to a client.
Solution: Use a temporary variable. For example, a Robot might be extended with the
following method, which uses a temporary variable, numWalls:
publicƒintƒnumBlockedDirections()
{ƒintƒnumWallsƒ=ƒ0;
ƒƒfor(intƒturnsƒ=ƒ0;ƒturnsƒ<ƒ4;ƒturnsƒ=ƒturnsƒ+ƒ1)
ƒƒ{ƒifƒ(!this.frontIsClear())
ƒƒƒƒ{ƒnumWallsƒ=ƒnumWallsƒ+ƒ1;
ƒƒƒƒ}
ƒƒƒƒthis.turnLeft();
ƒƒ}
ƒƒreturnƒnumWalls;
}
«type»ƒ«name»ƒ=ƒ«initialValue»;
where «type» is the type of value stored, such as int, double, or even the name of a
class; «name» is the name used for the variable; and «initialValue» is the first
value used for the variable. The initial value is optional; however, it must be assigned
before the variable is first used, and it is good practice to initialize the variable when it
is declared.
259
5.8 PATTERNS
Consequences: A variable is declared that may only be used within the smallest enclos-
ing block of code. Because it is only used locally, the reader’s burden of remembering
the name and purpose of the variable is significantly reduced, speeding the comprehen-
sion of the program and reducing errors.
Related Patterns: This pattern always occurs within an instance of a method pattern,
such as the Helper Method, Query, or Parameterized Method patterns.
Name: Counting
Context: You need to count a number of events, such as the number of times a Thing is
picked up, the number of moves a robot makes, or the number of times a test returns true.
Solution: Increment a temporary variable each time the event occurs. Initialize the vari-
able to zero before counting begins.
intƒ«counter»ƒ=ƒ0;
whileƒ(«booleanExpression»)
{ƒ«statements»
ƒƒ«counter»ƒ=ƒ«counter»ƒ+ƒ1;
}
Variations of this template may increment «counter» only if a certain test is met or
may use a different looping strategy.
Consequences: «counter» will record the number of events that have occurred since
it was initialized.
Related Patterns:
➤ This pattern uses a loop, typically the Zero or More Times pattern and the
Temporary Variable pattern.
➤ This pattern is often placed in an instance of the Query pattern.
Name: Query
Context: A calculation that yields a single value is required. This pattern is particularly
applicable if:
➤ the calculation involves a number of steps
➤ the calculation is complicated
➤ program readability is improved by giving the calculation a name
➤ the calcualtion is used more than once in the program
CHAPTER 5 | MORE DECISION MAKING 260
Solution: Write a method with a return value of the required type that uses a return
statement to identify the calculation’s answer. In general:
«accessModifier»ƒ«returnType»ƒ«queryName»(«optParameters»)
{ƒ«optionalStatements»
ƒƒ«returnType»ƒanswerƒ=ƒ«expression»;
ƒƒ«optionalStatements»
ƒƒreturnƒanswer;
}
privateƒintƒdistanceFromStreet(intƒtargetStr)
{ƒintƒanswer;
ƒƒifƒ(this.getStreet()ƒ>ƒtargetStr)
ƒƒ{ƒanswerƒ=ƒthis.getStreet()ƒ–ƒtargetStr;
ƒƒ}ƒelse
ƒƒ{ƒanswerƒ=ƒtargetStrƒ–ƒthis.getStreet();
ƒƒ}
ƒƒreturnƒanswer;
}
Consequences: Queries make code easier to understand because they name a calcula-
tion. They also make the calculation easier to reuse.
Related Patterns:
➤ The Query pattern is a specialization of the method creation patterns, such as the
Parameterless Command, Helper Method, and Parameterized Method patterns.
➤ The Simple Predicate and Predicate patterns are specializations of this pattern.
Name: Predicate
Context: You are using a Boolean expression that is not as easy to read or understand
as is desired, or a test is needed that can’t be written as a Boolean expression because it
requires extra processing.
Solution: Use the Query pattern where the «returnType» is boolean. Such a query
is called a predicate. The predicate may have parameters to make it more flexible.
5.8 PATTERNS
Related Patterns:
➤ The Predicate pattern is a specialization of the Query pattern.
➤ The Predicate pattern is often used to define predicates used in the Once or
Not At All, Zero or More Times, and Either This or That patterns, among
others.
➤ The Simple Predicate pattern is a simplified version of this pattern that does
not use a temporary variable or the optional statements.
Name: Cascading-if
Context: You have a situation in which exactly one of several groups of statements
should be executed based on a sequence of tests.
Solution: Order the tests from the most specific test to the most general test, pairing
each test with the appropriate group of actions. Format the tests and actions to empha-
size the pairings:
ifƒ(«test1»)
{ƒ«statementGroup1»
}ƒelseƒifƒ(«test2»)
{ƒ«statementGroup2»
...
}ƒelseƒifƒ(«testN»)
{ƒ«statementGroupN»
}ƒelse
{ƒ«defaultStatements»
}
Consequences: The tests are executed in order from 1 to N. The first one that returns
true will cause the associated statement group to be executed once. The final else
and «defaultStatements» are optional. They will be executed if none of the tests
return true.
Related Patterns:
➤ If there is only one test and one group of statements, this pattern becomes the
Once or Not At All pattern. Similarly, if there is only one test but two groups
of statements, this pattern becomes the Either This or That pattern.
➤ The switch statement, while not included as a pattern, solves similar kinds of
problems when the decision of which group of statements to execute is based
on a single value.
CHAPTER 5 | MORE DECISION MAKING 262
Context: You have a group of statements that must be executed a specific number of
times, a number that is known when the loop begins execution.
The general form for this pattern is shown in Section 5.5.1. There is an equivalent form
of the while statement, also shown in that section.
Consequences: The body of the for statement is executed zero or more times, depend-
ing on the specifics of the loop.
Related Pattern: The Counted Loop pattern is a specialization of the Zero or More
Times pattern.
Temporary or local variables are used to remember a value for later use in the same
method. They can simplify many methods and enable techniques such as the Counting
pattern. Temporary variables are also useful to remember a value to be returned from
a query. The value a temporary variable refers to can be changed with an assignment
statement in which the expression on the right side is evaluated and the resulting value
is assigned to the variable on the left side.
263
The statements discussed in this chapter and the previous chapter can significantly
increase the complexity of our programs, making appropriate style important. Writing
helper methods identified with stepwise refinement, using positively stated tests, and
visually structuring the code are all important techniques.
si
&& and ||
may be
Boolean
combined with
expressions
use short-circuit
evaluation
ma a
use
local
y
da variable
is also calle
temporary may be used elsewhere
block
variable within the smallest enclosing
use
refer assignment
s
a
to a can be changed
ing
value statement
with an
s
nt u
four-step
cou
ons t
are c g the loops
usin d are
st at the en
while with the te
loops
while-true break
can be stop with a
have loops statement
seve
ca ral
no infinite
mo ft common
re en include execution
suc be problems
cin expr
ctl e inclu
y w ssed de th
ith e fence-post
a
for loop problem
CHAPTER 5 | MORE DECISION MAKING 264
Written Exercises
5.1 Show the four steps used to derive a while loop for the following situations:
a. A robot must pick up all the things on the intersection it occupies.
b. A robot must pick up the same number of things as it has in its backpack.
c. A robot facing east must move until it is on the nearest street that is divisible
by eight. (Hint: Use the remainder operator (%) discussed in the code used
for Figure 5-22.)
d. A robot moves until it arrives at an intersection with a thing and a wall on
the right edge.
e. A robot moves between consecutive intersections picking up one thing from
each intersection, beginning with the one it is on. If there is still a thing on
the intersection after it has picked one up, the robot stops.
f. A variable, maxToPick, holds the maximum number of things a robot should
pick up. It picks up that many things from its current intersection unless there
aren’t enough things present. In that case, it picks up as many as it can.
5.2 The pickThingsToWall method, shown in Listing 5-4 and illustrated in
Figure 5-6, instructs a robot to move to a wall, picking one Thing from each
intersection that has one. Describe the changes required to make the robot pick
up an entire pile of things from those intersections that have them.
5.3 For each subproblem, write a predicate that returns the same value as the given
Boolean expression. That is, you could use your predicate instead of the
Boolean expression in a program with no difference in the overall behavior of
the program. Do not use &&, ||, or ! inside the predicate. (Hint: Use if and
if-else statements with a temporary variable and possibly helper methods.)
a. this.getAvenue()ƒ>ƒ5ƒ&&ƒthis.getAvenue()ƒ<ƒ10
b. this.countThingsInBackpack()ƒ>ƒ10ƒ&&ƒ!this.frontIsClear()
c. (this.getDirection()ƒ==ƒDirection.NORTHƒ||
ƒthis.getDirection()ƒ==ƒDirection.SOUTH)ƒ&&ƒ
ƒthis.frontIsClear()
5.4 For each subproblem, draw an oval diagram for the given expression, assuming
the robot is in the described situation.
a. this.getAvenue()ƒ>ƒ5ƒ&&ƒthis.getAvenue()ƒ<ƒ10
(the robot is on avenue 5)
b. this.countThingsInBackpack()ƒ>ƒ10ƒ&&ƒ!this.frontIsClear()
(the robot has 12 things in its backpack and is facing a wall)
265
Programming Exercises
5.5 Use the cascading-if statement to write a method named faceNorth that
always turns a robot to face north.
5.6 A HomingRobot’s “home” is at 4th Street and 3rd Avenue in a city that has no
obstructions, such as walls. HomingRobot contains a method named goHome,
which returns the robot to (4, 3) no matter where the robot is in the city.
goHome is written as follows:
publicƒvoidƒgoHome()
{ƒwhileƒ(!this.atHome())
ƒƒ{ƒthis.faceHome();
ƒƒƒƒthis.move();
ƒƒ}
}
a. Write the predicate atHome.
b. Use a cascading-if to write faceHome.
5.7 Consider again the situation shown in Figure 5-9 in which a robot should stop
at a thing or a wall, whichever comes first.
a. Solve the problem using a while-true loop with one break statement.
b. Solve the problem using a while-true loop with two break statements.
5.8 Consider again the problem of shifting things from one intersection to another,
as illustrated in Figure 5-4. Solve the problem using a while-true loop.
5.9 Use techniques presented in Section 5.6.2 to improve the following code frag-
ments. If they can’t be improved, explain why.
a. b.
ifƒ(this.isFacingNorth()) ifƒ(this.getStreet()ƒ!=ƒ5)
{ƒthis.turnAround(); {ƒthis.turnleft();
ƒƒthis.pickThing(); }ƒelse
}ƒelse {ƒthis.turnRight();
{ƒthis.turnAround();
ƒƒthis.putThing(); }
}ƒ
CHAPTER 5 | MORE DECISION MAKING 266
c. d.
ifƒ(this.canPickThing()) ifƒ(countƒ!=ƒ5ƒ&&ƒ
{ƒthis.move(); ƒƒƒƒ!this.frontIsClear())
ƒƒthis.turnLeft(); {ƒthis.turnRight();
}ƒelse ƒƒcountƒ=ƒcountƒ+ƒ1;
{ƒthis.move(); }ƒelse
ƒƒthis.turnRight(); {ƒthis.turnLeft();
}ƒ ƒƒcountƒ=ƒcountƒ+ƒ1;
}ƒƒ
e. f.
intƒnƒ=ƒthis.thingsHere(); ifƒ(this.frontIsClear())
ifƒ(nƒ==ƒ0) {ƒifƒ(this.canPickThing())
{ƒthis.turnLeft(); ƒƒ{ƒthis.pickThing();
ƒƒthis.move(); ƒƒƒƒthis.move();
}ƒelseƒifƒ(nƒ==ƒ1) ƒƒ}ƒelse
{ƒthis.turnRight(); ƒƒ{ƒthis.move();
ƒƒthis.move(); ƒƒ}
}ƒelseƒifƒ(nƒ==ƒ2) }ƒelse
{ƒthis.turnAround(); {ƒthis.turnLeft();
ƒƒthis.move(); ƒƒthis.move();
}ƒelse }ƒƒ
{ƒthis.move();
}ƒ
5.10 Assume that a Prospector robot is on an intersection with either one or two
things. Write a new method named followTrail that commands the robot to
face north if it is on an intersection with one thing and to face south if it is on
an intersection with two things. The robot must leave the same number of
things on the intersection as it found originally.
5.11 Write a predicate that returns true if and only if a robot is completely sur-
rounded by walls and unable to move in any direction. Of course, the predicate
should not have side effects.
5.12 Implement the pile-shifting robot described in Section 5.1.2.
Programming Projects
5.13 karel is in a completely enclosed rectangular room that has, unfortunately,
litter strewn all over it (see Figure 5-23). Create a new class of robot that can
pick up the litter. The size of the room is unknown and the amount of litter
on each intersection is also unknown. However, its top-left corner is always
on intersection (1, 1), and karel always starts there, facing east. karel
should return to its starting position when its task is complete. Make use of
stepwise refinement and helper methods. Create files representing different
rooms to test your program.
267
Littered room
5.14 Write a new Robot class, Houdini, that includes a method named escapeRoom.
It will cause the robot to search for an exit to a rectangular room—a break in the
wall. Such an exit always exists and is never in a corner. The robot may start any-
where in the room, but it will not be facing the exit. When the exit is found, the
robot will move through the exit and then stop. See Figure 5-24 for two of many
possible initial situations.
(figure 5-24)
(figure 5-25)
One possible
steeplechase situation
5.16 Extend the RobotSE class to create a MazeWalker. MazeWalker has a single
public method, followWallRight. Assume that when it executes, the robot
has a wall directly to its right. By calling this method repeatedly, the robot will
eventually find its way out of a maze.
CHAPTER 5 | MORE DECISION MAKING 268
Study the online documentation for the MazeCity class to learn how to con-
struct a city with a maze in it. Place your MazeWalker at (0, 0) facing south
and a Thing someplace within the city for it to find. Call followWallRight
repeatedly until the thing is found.
a. One strategy for followWallRight involves four different position
changes, as shown in Figure 5-26. The dark robot signifies the initial posi-
tion and the light robot signifies its position after followWallRight is
invoked.
b. Another strategy for followWallRight is for the robot to make exactly
one move each time the method is called.
c. Develop a solution that minimizes the number of “useless” turns the robot
makes to determine if its right or left side is blocked.
Comments: Option (a) is easy because there are hints in Figure 5-26, but it’s
hard to get the robot to stop at the right place. Option (b) is hard because it
has no hints, but it’s easy to get the robot to stop at the right place.
(figure 5-26)
Movements of a
MazeWalker robot
5.17 Implement a class named TrailBot that extends RobotSE and contains a sin-
gle public method, followTrail. A TrailBot follows a trail to a destination.
Trails begin at (0, 0) with the robot facing south. Trails consist of various signs
that indicate how to continue following the trail. The robot must leave the trail
signs as they were found. One way to test this is to have two robots follow the
same trail. The robots may or may not start with Things in their backpacks.
a. The trail signs consist of piles of one or more things. The number of Things
in the pile instruct the robot how far to move forward. After moving that
distance, the robot may find another trail sign (a pile of Things). If so, the
number present instructs the robot how far to go to find the next trail sign.
Finding a pile and moving a distance equal to its size continues until the
robot arrives at an empty intersection (the end of the trail). There may be
things between the piles that instruct the robot how far to go. If so, they
should be ignored.
b. The trail signs are as follows:
➤ A Wall and one Thing: end of the trail
➤ One Thing: move one intersection to the right
➤ A Wall: move one intersection to the left
➤ Empty intersection: move one intersection forward.
c. Design your own set of trail signs and create a robot to follow it.
269
(figure 5-27)
1 2 3
Robot begins with six things Robot has zero things left
1 2 3
Robot begins with seven things Robot has one thing left
5.19 An instance of ClearTunnelBot is facing a tunnel that has at least one Thing
on each intersection. When given the clearTunnel command, the robot
should remove all of the Things, placing them as shown in the final situation.
The robot may carry at most one Thing at a time and may not make any trips
back to the tunnel once all the Things have been removed. Figure 5-28 shows
two typical situations and their corresponding final situations. The robot will
always start with a wall behind it, marking where the things should be placed.
The distance to the tunnel and the length of the tunnel may vary.
(figure 5-28)
Tunnel-clearing situation
and its corresponding
final situation
a) b) (figure 5-29)
c) d)
271
g) h)
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 273
Chapter Objectives
After studying this chapter, you should be able to:
➤ Add new instance variables to a simple version of the Robot class
➤ Store the results of calculations in temporary variables and use those results later
in the method
➤ Write methods using parameter variables
➤ Use constants to write more understandable code
➤ Explain the differences between instance variables, temporary variables, parameter
variables, and constants
➤ Extend an existing class with new instance variables
Every computer program stores and updates information. When we tell a robot to
move, it updates its current street and avenue information. When a robot picks up a
thing from an intersection, the intersection updates its list of things, removing the thing
the robot picked up. The robot also updates its list of things to include the new thing it
picked up.
A variable is a place in the computer’s memory where information can be stored. When
stored in a variable, the information can be changed, copied, or used in an expression.
Programming languages offer several kinds of variables. The best one to use depends
on factors such as how long the information must be stored and the source of the first
value to store.
273
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 274
274
CHAPTER 6 | USING VARIABLES
In this chapter, we will write a simplified version of the Robot class, named
SimpleBot, to see its variables in action. It will also use a simplified version of City,
named SimpleCity. By the end of this chapter, you will be able to understand all of the
classes used except for three that are intimately involved with displaying the robots on
the screen. By the end of Chapter 13, you will be able to understand those three as well.
You are strongly encouraged to download these classes from the software downloads sec-
tion of the Robots Web site (www.learningwithrobots.com/software/downloads.html).
You will increase your understanding if you write and run the programs as we develop the ch06/simpleBots/
SimpleBot class.
We will spend most of our time developing the SimpleBot class, but you need a brief
introduction to the SimpleCity class for everything to make sense. The SimpleCity
class is a container for all the things that are “in” the city and need to be displayed by
the city. In our simple version, the city only contains intersections and robots.
The intersections and robots in the city are displayed by calling their paint method. In LOOKING AHEAD
the SimpleBot class, the paint method paints a robot; in the SimpleIntersection In Section 7.6, we will
class, the paint method paints a street and an avenue. We will guarantee to the see how the presence
SimpleCity object that the objects we ask it to display have a paint method by requir- of particular methods
can be assured with
ing these paintable objects to extend a class named Paintable. This class is very simple: Java interfaces.
It extends Object and has a single method that does nothing—the paint method.
Classes that should display themselves override this method. The Paintable class is
shown, in its entirety, in Listing 6-1.
Listing 6-1: The complete source code for the Paintable class
ch06/simpleBots/
1 importƒjava.awt.Graphics2D; Paintable.java
2
3 /** Subclasses of Paintable can be displayed in the city. Each subclass should
4 * override the paint method to paint an image of itself.
5 * @author Byron Weber Becker */
6 publicƒclassƒPaintableƒextendsƒObject
7 {
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 275
275
8 ƒƒpublicƒPaintable()
9 ƒƒ{ƒsuper();
10 ƒƒ}
IN THE ROBOT
11 ƒƒ
12 ƒƒ/** Each subclass should override paint to paint an image of itself. */
13 ƒƒpublicƒvoidƒpaint(Graphics2Dƒg)
14 ƒƒ{
CLASS
15 ƒƒ}
16 }
The city displays the intersections and the robots by calling paint about twenty times
each second, first for the intersections and then for the robots. If a robot has moved
since the last time the city was displayed, painting the intersections will erase the old
robot image, and painting the robot will position it in its new location.
The following sections concentrate on the SimpleBot class and, in particular, how it
uses variables to store and manipulate the information a robot needs. The robots in
this section are simple—they only move and turn left. Eventually you will be able to
increase their capabilities substantially.
KEY IDEA Our approach is to start simply, adding functionality in small increments. First, we’ll
Start simply. Add display a round “robot” on intersection (4, 2). Then we’ll make it move and turn left—
functionality in small except that we won’t be able to tell which way it faces (because it is displayed as a cir-
increments. cle) until it moves again. We will then improve its appearance so that it shows which
direction it’s facing, and enhance its functionality in other ways.
We know from our previous experience with robots that they have attributes that specify
the street and avenue they currently occupy. In Figure 1-8, reproduced in Figure 6-1, we
were introduced to a Robot class diagram showing these attributes. The instances of the
class, as shown on the right side of Figure 6-1, have specific values for these attributes.
Recall that each instance has its own copies of the attributes defined by the class. Each
individual robot has its own street and avenue, for example.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 276
276
CHAPTER 6 | USING VARIABLES
When a Robot object paints itself on the city, it evidently looks at the street and
avenue attributes to determine where to paint the image. If the attributes hold the val-
ues 4 and 2, respectively, then the robot image is painted on the intersection of 4th
Street and 2nd Avenue.
The idea of an attribute is implemented in Java with an instance variable. You can KEY IDEA
imagine an instance variable as a box that has a name. Inside the box can be one, and An instance variable
only one, value. When the name of the box is used in the code, a copy of the value cur- stores a value, such
as 10 or –15, for
rently inside the box is retrieved and used. An instance variable also allows us to
later use.
change the value inside the box.
➤ Each object has its own set of instance variables. Each robot, for example, has Each object has its
own set of instance
its own street and avenue variables to remember its location.
variables.
➤ The scope of an instance variable extends throughout the entire class. An
instance variable can be used within any method.
➤ The lifetime of an instance variable—the length of time its values are
retained—is the same as the lifetime of the object to which it belongs.
We create and name an instance variable—a named “box” that holds a value—with a KEY IDEA
variable declaration. The declaration is often combined with an assignment statement A variable declaration
to specify the variable’s initial value. Instance variables are declared inside the classes’ sets aside space in
memory to store a
first and last braces but outside of any methods. The beginnings of the SimpleBot
value and associates a
class, including two instance variables to hold the street and avenue, are shown in name with that space.
Listing 6-2.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 277
277
IN THE ROBOT
4 ƒƒprivateƒintƒavenueƒ=ƒ2; // Create space to store the robot's current avenue.
5 ƒƒ
6 ƒƒpublicƒSimpleBot()
7 ƒƒ{ƒsuper();
CLASS
8 ƒƒ}
9
10 ƒƒ// An incomplete class!
11 }
These instance variable declarations have four key parts that occur in the following order:
➤ Declarations start with an access modifier. For reasons we will explore in
Chapter 11, the keyword private should be used almost exclusively. Like
using private before a helper method, this keyword identifies this part of the
class as “for internal use only.”
➤ Declarations specify the type of values stored. The int says that these
“boxes” hold integers—values such as 1, 33, or -15. Later, we’ll study other
possibilities, such as double (values such as 3.14159) and String (values
such as “IƒloveƒJava”).
➤ Declarations name the variable. In these examples, the names are street and
avenue. Instance variables are generally named like methods, using one or
more descriptive words, with the first letter of the entire name being lowercase
and the first letter of subsequent words being uppercase. Examples include
avenue, direction, and nextLocation.
➤ Declarations may include an initial value, placed after an equal sign. In these
examples, street and avenue are given initial values of 4 and 2, respectively.
If the initial value is not explicitly assigned, Java will provide a default initial
value appropriate to the type. The default for integers is 0 and for boolean
variables is false. However, your code is more understandable if you explic-
itly initialize your variables.
278
CHAPTER 6 | USING VARIABLES
Two major tasks remain in writing the SimpleBot class. First, we need to display the
robot within the city. To do this, we will access the values stored in the instance vari-
ables. Second, we need to make the robot move. We will do this by updating the values
stored in the instance variables. Then, when the robot is painted again, it will appear at
a different place in the city.
0 1 2 3 (figure 6-2)
0 Simple robot and its
location
1
3
50
5 50
1 publicƒvoidƒpaint(Graphics2Dƒg)
2 {ƒg.setColor(Color.BLACK);
3 ƒƒg.fillOval(100,ƒ200,ƒ50,ƒ50);
4 }
This method takes a parameter, g, which is an object used to paint on the screen. Line 2 LOOKING AHEAD
says to use the color black in subsequent painting operations. Line 3 says to paint a We will discuss
solid oval. Recall that the first argument to fillOval is the x (horizontal) coordinate, parameters in depth
in Section 6.2.2.
the second argument is the y (vertical) coordinate, and the last two arguments are the
height and width, respectively.
279
IN THE ROBOT
1 publicƒvoidƒpaint(Graphics2Dƒg)
2 {ƒg.setColor(Color.BLACK);
Instance Variable
3 ƒƒg.fillOval(this.avenueƒ*ƒ50,ƒthis.streetƒ*ƒ50,ƒ50,ƒ50);
4 }
CLASS
We will use the keyword this to access the instance variables in our code. Using
this to access an instance variable is like using this to access a helper method: It
reinforces that the variable belongs to this object, the one that contains the currently
executing code.1
Line 3 of the preceding code can be better understood with the help of an evaluation
diagram, like the one we used in Section 5.4. Recall that we begin by drawing ovals
around literals (like 50) and variables (like this.avenue), and writing their type
above and their value below the ovals. We then repeatedly circle method calls and
operators, together with their arguments and operands, in order of their precedence.
This process is shown in Figure 6-3, where we assume that the robot is on (4, 2).
Notice that the arguments to fillOval are circled before fillOval is circled. This
means that the arguments are evaluated before the method is called. Also note that the
type of the oval around fillOval is void because fillOval has a return type of
void. It doesn’t return a value to place below the oval. Instead, the side effect of the
method is written.
1 The keyword this is actually optional much of the time. Students are strongly encouraged to use it,
however, to reinforce that they are accessing an instance variable that belongs to a particular object.
There is also the practical reason that many modern programming environments display a list of
instance variables and methods when “this.” is typed, reducing the burden on the programmer’s mem-
ory and eliminating many spelling mistakes.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 280
280
CHAPTER 6 | USING VARIABLES
void
int int
int int int int int int
g.fillOval( this.avenue * 50 , this.street * 50 , 50 , 50 ) ;
2 50 4 50 50 50
100 200
(the oval is drawn)
After the last iteration of step two in drawing an evaluation diagram
The code for the paint method needs one last detail: The classes Graphics2D and
Color must be imported before we can use them in lines 1 and 2, respectively. To do
so, add the following two lines at the beginning of the file:
importƒjava.awt.Graphics2D;ƒ
importƒjava.awt.Color;ƒ
Our robot needs a move method. When it is invoked, the robot should move to another LOOKING BACK
intersection. From previous experience, we know that invoking move changed a robot’s Take a look at the
attributes. Because attributes are implemented with instance variables, we now know state change diagram
in Figure 1-12 to
that move must change either street or avenue by 1, depending on the direction.
better understand the
effect of move on the
We already discussed incrementing and decrementing parameters and temporary vari-
avenue and street
ables in Chapters 4 and 5. Changing an instance variable by 1 is similar and is shown attributes.
in the following partially implemented move method:
// Move the robot one intersection east, assuming it is facing east and nothing blocks it.
publicƒvoidƒmove()
{ƒthis.avenueƒ=ƒthis.avenueƒ+ƒ1;ƒƒƒƒ// incomplete
} Instance Variable
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 281
281
Recall that an assignment statement works in two steps. First, it calculates the value of
the expression to the right of the equal sign. Second, it forces the variable on the left of
the equal sign to store whatever value was calculated. The variable continues to store
IN THE ROBOT
that value until it is changed with another assignment statement. The assignment state-
ments used with parameter and temporary variables in Chapters 4 and 5 behaved the
same way.
CLASS
How does this process move the robot? The entire city is repainted about 20 times per
second with a loop such as the following:
whileƒ(true)
{ƒpaint everything in layer 0 (the intersections)
ƒƒpaint everything in layer 1 (the things)
ƒƒpaint everything in layer 2 (the robots)
}
When move is called, the entire city is repainted within about 50 milliseconds.
Repainting the intersections has the effect of erasing the robot’s old image at its old
location. Shortly thereafter, the robot’s paint method is called. It paints the robot’s
image in its new location, as determined by the current values of street and avenue.
The effect is that the robot appears to move on the screen.
But there is a problem. Executing the move method takes far less than 50 milliseconds.
If several consecutive move instructions are executed, we won’t see most of them
because they occur in the time between repainting the screen. To solve this problem, we
need to ensure that move takes at least 50 milliseconds to execute. This is done by
instructing the move method to sleep, or do nothing, for a while. The becker library
contains a method to sleep for a specified number of milliseconds. To use it, import
becker.util.Utilities and add Utilities.sleep(400) to the move method.
The robot will then stop for 0.400 seconds each time it moves.
The code for the SimpleBot class, as developed so far, is shown in Listing 6-3. Robots
instantiated from this class will always start out on intersection (4, 2) and can only
travel east. We will remove these restrictions soon.
282
CHAPTER 6 | USING VARIABLES
5 /** A first try at the SimpleBot class. These robots are always constructed on street 4,
6 * avenue 2. There is no way to tell which way they are facing and they can only move east.
7 *
8 * @author Byron Weber Becker */
9 publicƒclassƒSimpleBotƒextendsƒPaintable
10 {
11 ƒƒprivateƒintƒstreetƒ=ƒ4;
12 ƒƒprivateƒintƒavenueƒ=ƒ2;
13
14 ƒƒ/** Construct a new Robot at (4, 2). */
15 ƒƒpublicƒSimpleBot()
16 ƒƒ{ƒsuper();
17 ƒƒ}
18
19 ƒƒ/** Paint the robot at its current location. */
20 ƒƒpublicƒvoidƒpaint(Graphics2Dƒg)
21 ƒƒ{ƒg.setColor(Color.BLACK);
22 ƒƒƒƒg.fillOval(this.avenueƒ*ƒ50,ƒthis.streetƒ*ƒ50,ƒ50,ƒ50);
23 ƒƒ}
24
25 ƒƒ/** Move the robot one intersection east. */
26 ƒƒpublicƒvoidƒmove()
27 ƒƒ{ƒthis.avenueƒ=ƒthis.avenueƒ+ƒ1;
28 ƒƒƒƒUtilities.sleep(400);
29 ƒƒ}
30
31 ƒƒ/** Turn the robot 90 degrees to the left. */
32 ƒƒpublicƒvoidƒturnLeft()
33 ƒƒ{
34 ƒƒ}
35 }
The main method to test this class is slightly different from the ones we’ve written in
previous chapters, in which we passed the city to the robot’s constructor. The con-
structor then added the robot to the city. The constructor in Listing 6-3 isn’t that
sophisticated (yet). Therefore, we must add the robot to the city in the main method,
specifying that it appears in layer 2 so that it is painted after the intersections (layer 0)
and things (layer 1). A second change is to explicitly wait for the user to press the Start
button before moving the robots. These two details are at lines 11-14 of Listing 6-4.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 283
283
IN THE ROBOT
SimpleBot class yourself.
CLASS
ch06/simpleBots/
Main.java
1 /** A main method to test the SimpleBot and related classes.
2 *
3 * @author Byron Weber Becker */
4 publicƒclassƒMain
5 {
6 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
7 ƒƒ{ƒSimpleCityƒnewYorkƒ=ƒnewƒSimpleCity();
8 ƒƒƒƒSimpleBotƒkarelƒ=ƒnewƒSimpleBot();
9 ƒƒƒƒSimpleBotƒsueƒ=ƒnewƒSimpleBot();
10 ƒ
11 ƒƒƒƒnewYork.add(karel,ƒ2);
12 ƒƒƒƒnewYork.add(sue,ƒ2);
13 ƒƒ
14 ƒƒƒƒnewYork.waitForStart();ƒƒƒ// Wait for the user to press the Start button.
15
16 ƒƒƒƒfor(intƒi=0;ƒi<4;ƒiƒ=ƒi+1)
17 ƒƒƒƒ{ƒkarel.move();
18 ƒƒƒƒƒƒkarel.move();
19 ƒƒƒƒƒƒkarel.turnLeft();
20 ƒƒƒƒ}
21
22 ƒƒƒƒsue.move();
23 ƒƒ}
24 }
LOOKING AHEAD So far we’ve seen how to declare, initialize, access, and modify instance variables to
In Section 7.4, we will implement the street and avenue attributes for a robot. Keep in mind that instance vari-
look at a detailed ables are also used to implement classes that have nothing to do with robots: bank
example that has accounts, employees, properties for a Monopoly game, and so on.
nothing to do
with robots.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 284
284
CHAPTER 6 | USING VARIABLES
Right now, however, let’s implement another attribute of robots: direction. When we’re
done, the robots will be able to turn left and move in the direction they are facing.
Representing Directions
Our basic plan is to use a new instance variable, direction, to store the direction the
robot is facing. direction will be an integer. When it has a value of 0, the robot is
facing east; 1 means the robot is facing south, 2 is west, and 3 is north. Turning left is
as easy as subtracting 1 from direction—unless the robot is facing east (0). Then we
need to wrap around and set direction to north (3). As with the move method, forc-
ing the robot to sleep after turning allows us to see what has happened.
Listing 6-5 shows the addition of the direction instance variable and the turnLeft
method in a skeleton of the SimpleBot class.
Listing 6-5: Changes to the SimpleBot class to add the turnLeft service
1 publicƒclassƒSimpleBotƒextendsƒPaintable
2 {ƒ...
3 ƒƒprivateƒintƒdirectionƒ=ƒ0; // Begin facing east.
4 ƒƒ...
5
6 ƒƒ/** Turn the robot left 1/4 turn. */
7 ƒƒpublicƒvoidƒturnLeft()
8 ƒƒ{ƒifƒ(this.directionƒ==ƒ0) // If facing east...
9 ƒƒƒƒ{ƒthis.directionƒ=ƒ3; // face north.
10 ƒƒƒƒ}ƒelse
11 ƒƒƒƒ{ƒthis.directionƒ=ƒthis.directionƒ-ƒ1;
12 ƒƒƒƒ}
13 ƒƒƒƒUtilities.sleep(400);
14 ƒƒ}
15 }
Remembering that 0 means east and 3 means north makes turnLeft difficult to
understand. Listing 6-5 compensates with comments, but we can do better. One
approach is to declare four new instance variables, as follows:
privateƒintƒeastƒ=ƒ0;
privateƒintƒsouthƒ=ƒ1;
privateƒintƒwestƒ=ƒ2;
privateƒintƒnorthƒ=ƒ3;
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 285
285
However, these “variables” seem different from instance variables such as avenue and
direction because they should not change while the program executes. We should
IN THE ROBOT
always use 0 to mean east, and it would be a programming error if east ever had a dif-
ferent value.
KEY IDEA Java uses the keyword final to indicate that the first value a variable receives should
CLASS
Use the final also be the final value it ever receives. If we try to change the variable’s value, Java will
keyword when issue a compile-time error. Such variables are often called constants. It is traditional to
a variable should
use all uppercase characters to name constants to emphasize that they are unchanging,
never be assigned a
new value. as follows:
In addition to making the code easier to read, constants are useful because they provide
one place to change when assumptions change. For example, we assumed that inter-
sections are 50 pixels square. If we ever need to display larger cities, we may want to
change it to 40 pixels. Finding and changing one constant is much easier than finding
and changing every place the value 50 is used in the program.
LOOKING AHEAD A second keyword, static, is often used with final instance variables. It allows pro-
Using static with grammers to access the variable using the class name rather than an object reference.
non-final instance For example, suppose EAST were declared as follows:
variables is discussed
in Section 7.5.1. publicƒstaticƒfinalƒintƒEASTƒ=ƒ0;
ifƒ(this.directionƒ==ƒSimpleBot.EAST)
This may not seem like much of an improvement, but if the variable is public, then it
Named Constant
can be used from any class without using an object. We have, in fact, done this already
in the main method of graphics programs when we use JFrame.EXIT_ON_CLOSE to
set the frame’s default close operation.
Sometimes constants are used in many different classes. In such cases, it can make
sense to have a class named something like Constants that contains nothing but pub-
lic constants.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 286
286
CHAPTER 6 | USING VARIABLES
Now that we can easily represent directions using final instance variables and can
change a robot’s direction with the turnLeft method, we must reimplement the move
method so that it actually moves in the correct direction.
Each time the robot moves, we will adjust both the street and the avenue by the values
shown in Table 6-1.
SOUTH 1 0
This is a perfect job for two helper methods, strOffset and aveOffset. They use a
cascading-if statement to set a temporary variable to the appropriate offset, based on
testing the value stored in the direction instance variable. They then return that
value using a return statement, just like the queries written in Section 5.2.4.
The new direction instance variable, the new turnLeft method, the modified move
method, and the two helper methods are all shown in Listing 6-6. The class assumes
that appropriate constants have been declared in a class named Constants. The
turnLeft method uses two additional constants to clarify why they constitute a spe-
cial case. They are declared in Constants as follows:
publicƒstaticƒfinalƒintƒFIRST_DIRƒ=ƒEAST;
publicƒstaticƒfinalƒintƒLAST_DIRƒ=ƒNORTH;
Methods that provide access to private instance variables are called accessor methods.
An accessor method is a query that answers the question “What value does attribute X
currently hold?” That is, it makes the value stored in an instance variable accessible to
code outside of the class.
publicƒ«typeReturned»ƒget«Name»()
{ƒreturnƒthis.«instanceVariable»;
}
Accessor Method
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 287
287
«Name» is usually the name of the instance variable. It should be a name that is mean-
ingful to users of the class.
IN THE ROBOT
Finally, «instanceVariable» is the name of the appropriate instance variable to
access.
CLASS
Three examples of accessor methods, one each for street, avenue, and direction,
are shown in Listing 6-6.
Listing 6-6: A SimpleBot class that includes the ability to turn left
1 importƒjava.awt.Graphics2D;
2 importƒjava.awt.Color;
3 importƒbecker.util.Utilities;
4
5
6 /** A second try at the SimpleBot class. These robots are always constructed at (4, 2) facing
7 * east. Robots can move forward and turn left, although the user cannot determine which
8 * way the robot is facing until it moves.
9 *
10 * @author Byron Weber Becker */
11 publicƒclassƒSimpleBotƒextendsƒPaintable
12 {
13 ƒƒprivateƒintƒstreetƒ=ƒ4;
14 ƒƒprivateƒintƒavenueƒ=ƒ2;
15 ƒƒprivateƒintƒdirectionƒ=ƒConstants.EAST;
16
17 ƒƒ/** Construct a new robot at (4, 2) facing east. */
18 ƒƒpublicƒSimpleBot()
19 ƒƒ{ƒsuper();
20 ƒƒ}
21
22 ƒƒ/** Paint the robot at its current location. */
23 ƒƒpublicƒvoidƒpaint(Graphics2Dƒg)
24 ƒƒ{ƒg.setColor(Color.BLACK);
25 ƒƒƒƒg.fillOval(this.avenueƒ*ƒConstants.INTERSECTION_SIZE,
26 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒthis.streetƒ*ƒConstants.INTERSECTION_SIZE,
27 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒConstants.INTERSECTION_SIZE,ƒ
28 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒConstants.INTERSECTION_SIZE);
29 ƒƒ}
30
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 288
288
CHAPTER 6 | USING VARIABLES
Listing 6-6: A SimpleBot class that includes the ability to turn left (continued)
289
74 ƒƒƒƒ}
75 ƒƒƒƒreturnƒoffset;
76 ƒƒ}
77
78 ƒƒ/** Calculate how far the robot should move along the street.
79 ƒƒ * @return {-1, 0, or 1} */
80 ƒƒprivateƒintƒstrOffset()
81 ƒƒ{ƒintƒoffsetƒ=ƒ0;
82 ƒƒƒƒifƒ(this.directionƒ==ƒConstants.NORTH)
83 ƒƒƒƒ{ƒoffsetƒ=ƒ-1;
84 ƒƒƒƒ}ƒelseƒifƒ(this.directionƒ==ƒConstants.SOUTH)
85 ƒƒƒƒ{ƒoffsetƒ=ƒ1;
86 ƒƒƒƒ}
87 ƒƒƒƒreturnƒoffset;
88 ƒƒ}
89 }
Like parameter and temporary variables, instance variables store a value. They are also
different in important ways. We will have more to say about these similarities and dif-
ferences in Section 6.5, but for now, remember the following:
➤ Instance variables are declared inside a class but outside of all methods.
Parameter and temporary variables are declared inside a method.
➤ Instance variables have a larger scope. They may be used within any of the
methods in the class. Parameter and temporary variables can be used only
within the method in which they are declared.
➤ Instance variables have a longer lifetime. They retain their value until changed
by an assignment statement or until the object is no longer in use. Parameter
and temporary variables disappear when their method finishes executing and
are reinitialized each time the method executes again.
290
CHAPTER 6 | USING VARIABLES
Right now our robots are displayed with a black oval that covers the entire intersec-
tion. We can’t tell which direction the robot is facing unless it moves. In this section,
we will upgrade our robot to correct these problems. Our new, improved robot will
appear as shown in Figure 6-4.
The large circle, representing the body of the robot, is centered on the middle of the
intersection and has a radius of 15 pixels. The smaller circle, representing the robot’s
sensor, is centered on the perimeter of the larger circle with a radius of 6 pixels.
Because the size of the circle no longer matches the size of the intersection, more
work will be required to paint the robot. Figure 6-5 shows relevant values that we
will need to calculate. They depend heavily on the center of the robot’s body and the
center of the sensor. We will find it useful to calculate and store these values in tem-
porary variables.
(figure 6-5)
15
6
2 * 15
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 291
291
Listing 6-7 provides a skeleton for the paint method. It declares four temporary vari-
ables to store the center coordinates of the body and the sensor in lines 5-8. Their ini-
tialization is shown in pseudocode.
The values in these four variables are used in lines 11 and 14 to paint the two circles
representing the robot’s body and sensor. Recall that fillOval’s first two arguments
represent the upper-left corner of the smallest rectangle that will include the oval, shown
with dotted lines in Figure 6-5. The expression bodyXƒ-ƒ15 in line 11 uses the center of
the circle to calculate the left edge of the body’s enclosing rectangle. bodyYƒ-ƒ15
calculates the top edge of the body’s enclosing rectangle.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 292
292
CHAPTER 6 | USING VARIABLES
The center of the robot’s body is the same as the center of the intersection. To calculate
it, we can calculate the intersection’s upper-left corner and then add one half of the
intersection’s width and height. Recall that the intersection’s size is stored in
Constants.INTERSECTION_SIZE. This name is unwieldy to use repeatedly in a
method, so we first assign it to a temporary variable with a shorter name.
intƒiSizeƒ=ƒConstants.INTERSECTION_SIZE;
intƒbodyXƒ=ƒthis.avenueƒ*ƒiSizeƒ+ƒiSizeƒ/ƒ2;
intƒbodyYƒ=ƒthis.streetƒ*ƒiSizeƒ+ƒiSizeƒ/ƒ2;
We can increase our confidence that these calculations are correct by producing an
evaluation diagram with some sample values for the robot’s location and intersection
size. For example, see Figure 6-6.
100 25
125
One question that arises is what happens when two numbers do not divide evenly. For
example, what would the preceding expression produce if the intersection size was 51
instead of 50? One might expect an answer of 125.5 because 51/2 is 25.5—but that
answer is wrong.
Java performs integer division when both operands are integers. Integer division is like KEY IDEA
the long division you learned in grade school, but with the remainder thrown away. Dividing two integers
That is, 51 divided by 2 is 25 with a remainder of 1. The remainder is thrown away, results in an integer.
The decimal portion,
and the answer is 25. Java has a second kind of division that preserves the decimal por-
if any, is lost.
tion. We will study it in Section 7.2.2.
A related operator is %, the remainder operator. It returns the remainder of the long divi- KEY IDEA
sion. For example, 51ƒ%ƒ2 returns 1 because 51 divided by 2 is 25 with a remainder n % d gives the
of 1. If the first operand happens to be negative, the answer will be negative as well. remainder of
dividing n by d.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 293
293
ifƒ(nƒ%ƒ2ƒ==ƒ0)
{ƒ// n is even...
➤ The remainder operator can be used to process every nth item. For example,
consider a robot traveling east until it finds a wall. The following code will
place a Thing on every 5th intersection.
whileƒ(karel.frontIsClear())
{ƒifƒ(this.getAvenue()ƒ%ƒ5ƒ==ƒ0)
ƒƒ{ƒthis.putThing();
ƒƒ}
}
➤ The remainder operator can be used together with the / operator to find the
individual digits of a number. For example, 123ƒ%ƒ10 gives the right-most
digit, 3. Dividing by 10 gives the number without the right-most digit. For
example, 123ƒ/ƒ10 gives 12. Taking the remainder of this number gives the
next digit, 2, and so on.
➤ The remainder operator can be used to perform “wrap around” or “clock”
arithmetic. We’ve already seen an example of this kind of arithmetic when we
implemented turnLeft. We subtracted one from direction, unless the direc-
tion was EAST (0); then we wrapped around back to NORTH (3). The more
common case is incrementing by one until an upper limit is reached, then start-
ing over at 0. This calculation can be implemented as follows:
varƒ=ƒ(varƒ+ƒ1)ƒ%ƒupperLimit;
We now turn to calculating the sensor’s center, as required by lines 7 and 8 in Listing 6-7.
Once again, we turn to Figure 6-5 for guidance. It appears that sensorY is the same as
bodyY and that sensorX is the same as bodyXƒ+ƒ15, the body’s radius.
Unfortunately, it isn’t that simple. These calculations only work if the robot is facing
east. Figure 6-7 shows the robot in all four positions with the associated calculations.
(figure 6-7)
294
CHAPTER 6 | USING VARIABLES
We could solve this problem with a cascading-if statement, but there is an easier way.
This situation is similar to moving the robot. There, we wanted to add -1, 0, or 1 to
the street or avenue, depending on the direction the robot is facing. Here we want to
add -15, 0, or 15. For the move method, we used two helper methods—strOffset
and aveOffset. For this problem, we just need to multiply their results by 15.
Lines 7 and 8 in Listing 6-7 can be replaced by the following two lines:
intƒsensorXƒ=ƒbodyXƒ+ƒthis.aveOffset()ƒ*ƒ15;
intƒsensorYƒ=ƒbodyYƒ+ƒthis.strOffset()ƒ*ƒ15;
The final keyword can be used with any kind of variable, not just instance variables.
It always means that the first value assigned to the variable should also be the final
value assigned.
finalƒintƒiSizeƒ=ƒConstants.INTERSECTION_SIZE;
It’s also worth noting that none of the temporary variables change after they are ini-
tialized. (They may, however, have a different value the next time the paint method is
called and the variables are initialized again.) It wouldn’t hurt to make the fact that
they don’t change while paint is executing explicit by using final for all of the tem-
porary variables.
Delaying Initialization
intƒsensorX;
ifƒ(this.directionƒ==ƒConstants.EAST)
{ƒsensorXƒ=ƒbodyXƒ+ƒ15;
}ƒelseƒifƒ(this.directionƒ==ƒConstants.NORTH)
{ƒsensorXƒ=ƒbodyX;
...
When initialization is delayed, the temporary variable holds an unknown value KEY IDEA
between the time it is declared and when it is initialized. It would be an error to try to An uninitialized
use it. Fortunately, the Java compiler actively tries to prevent this error. For example, temporary
variable holds an
unknown value.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 295
295
intƒbodyX;
intƒsensorXƒ=ƒbodyXƒ+ƒ15;
Occasionally, the compiler will issue this error even though the variable is initialized in
an if statement. In that case, simply initialize the variable when it is declared even
though you know it will have a new value assigned before it is used.
This paint method could have been written without temporary variables (see Listing 6-8).
However, temporary variables allow us to break the calculation into smaller pieces. The
pieces can be individually named and documented, making them easier to understand than
one large calculation.
KEY IDEA Temporary variables and instance variables are similar in that they both store a value
Use temporary that can be used later. Their major differences are in how long the value is stored and
variables when you in where the value can be used. Because instance variables have a longer lifetime and a
can; instance
larger scope, they can often be used in place of temporary variables. This can lead to
variables only if
you must. mistakes. The shorter lifetimes of temporary variables and their much smaller scope (a
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 296
296
CHAPTER 6 | USING VARIABLES
method rather than the entire class) result in a much smaller opportunity for misuse.
Instance variables are vitally important in object-oriented programming, but should
only be used when other kinds of variables cannot be used.
Consider the following modification of the move method in the SimpleBot class. It
causes the robot to move two intersections in the direction it is currently facing.
1 publicƒvoidƒmoveFar()
2 {ƒintƒhowFarƒ=ƒ2;
3 ƒƒthis.streetƒ=ƒthis.streetƒ+ƒthis.strOffset()ƒ*ƒhowFar;
4 ƒƒthis.avenueƒ=ƒthis.avenueƒ+ƒthis.aveOffset()ƒ*ƒhowFar;
5 ƒƒUtilities.sleep(400);
6 }
A method to move the robot three intersections can be developed by copying moveFar to
a new method, moveReallyFar, and changing the 2 in line 2 to 3. Another method,
moveReallyReallyFar, could be identical to moveFar except for setting howFar to 4.
The methods are all identical except for that one number. This seems silly, for a num-
ber of reasons:
➤ What if we discover a bug in the first one—for example, if line 3 used
aveOffset() instead of strOffset()? Chances are good that the same bug
has been cut and pasted into the other methods.
➤ What if we want to move 7 intersections? We must define a new method, with
a new name—and that still wouldn’t help us move 25 intersections in another
part of the program.
➤ What if we want to calculate the distance to move, storing it in a variable? We
need to resort to a messy cascading-if or switch statement to choose the
specific method to execute.
Instead of initializing howFar when we write the method, we want to initialize it when KEY IDEA
we call the method. Using parameter variables, we can accomplish this goal. The argument,
Parameters allow us to replace karel.moveFar() with karel.move(2) and to provided when the
method is called, is
replace karel.moveReallyReallyFar() with karel.move(4). The argument—
used to initialize the
the number in the parentheses—specifies how far we want the robot to move. If we parameter variable.
want the robot to move five intersections, we can write karel.move(5).
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 297
297
intƒhowFarƒ=ƒ2;
To transform it into a parameter, think of its two distinct parts: the declaration and the
initialization. The declaration, intƒhowFar, stays inside the method, where it becomes
the parameter variable. The value it is initialized with, 2, becomes the argument. It is
provided when the method is called. (The equal sign is discarded in the process.)
The left side of Figure 6-8 shows relevant portions of a program that uses moveFar.
On the top is a main method that calls moveFar, and on the bottom is the definition
of moveFar. The right side of the figure shows the program after transforming it to use
a parameter variable.
Inside the method, the parameter variable behaves like any other temporary variable. It
can be used in expressions, passed as an argument to another method, and assigned a
new value. Its scope is the entire method. Like a temporary variable, it has a short life-
time, disappearing when the method finishes executing. It is re-created and reinitialized
each time the method is executed. The difference is in how it is initialized.
As we’ve seen in previous chapters, a method may have more than one parameter. For
example, the following method is called with two arguments, karel.move(5,
Constants.EAST). It turns karel to face the specified direction and then move the
specified distance. Each pair of declarations is separated with a comma.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 298
298
CHAPTER 6 | USING VARIABLES
publicƒvoidƒmove(intƒhowFar,ƒintƒaDir)
{ƒthis.face(aDir);
ƒƒthis.move(howFar);
}
Overloading Methods
We now have three methods named move, the usual one without a parameter, one with
a single parameter, and one with two parameters. Fortunately, this does not usually
cause a problem as long as every method in the class has a different signature. A
method’s signature is its name together with an ordered list of its parameter types.
The signature of the usual move method is simply move(). It has no parameters and
hence its ordered list of parameter types is empty. The signature of the move method
shown in the right side of Figure 6-8 is move(int). Notice that the parameter name is
not included in the signature. The last version of move has the signature
move(int,ƒint).
Assuming karel is an instance of a SimpleBot class that has these three methods
defined, karel.move(), karel.move(3), and karel.move(3,ƒConstants.NORTH)
are all legal method calls. In each case, Java executes the method with the matching
signature.
Methods and constructors that have the same name but different signatures are said to
be overloaded. Note that we now have two terms incorporating the word “over”:
➤ Overload—A method overloads another method in either a superclass or the
same class when they have the same name but different signatures. Any of the
methods may be executed, depending on the arguments provided when it is
called.
➤ Override—A method in a subclass overrides a method in a superclass if they
have the same signatures. The overriding method is executed and the overrid-
den method is not (unless it is called by the overriding method).
Parameters are also useful for writing constructors. Our current implementation of
SimpleBot always begins on 4th Street and 2nd Avenue facing east. We can use para-
meters in the constructor to make it more flexible.
Listing 6-9 shows a constructor with four parameters to construct a robot in a speci-
fied city at a specified location. Three of the parameters are used to provide the initial
values to the instance variables street, avenue, and direction. Because the initial
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 299
299
1 publicƒclassƒSimpleBotƒextendsƒPaintable
2 {ƒprivateƒintƒstreet;
3 ƒƒprivateƒintƒavenue;
4 ƒƒprivateƒintƒdirection;
5
6 ƒƒ/** Construct a new robot in the given city at the given location.
7 ƒƒ * @param aCity The city in which this robot appears.
8 ƒƒ * @param aStreet This robot's initial street.
9 ƒƒ * @param anAvenue This robot's initial avenue.
10 ƒƒ * @param aDirection This robot's initial direction. */
11 ƒƒpublicƒSimpleBot(SimpleCityƒaCity,ƒ
12 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒintƒaStreet,ƒintƒanAvenue, intƒaDirection)
13 ƒƒ{ƒsuper();
14 ƒƒƒƒthis.streetƒ=ƒaStreet;
15 ƒƒƒƒthis.avenueƒ=ƒanAvenue;
16 ƒƒƒƒthis.directionƒ=ƒaDirection;
17 ƒƒƒƒaCity.add(this,ƒ2); // Add this robot to the given city in the top level.
18 ƒƒ}
19 ƒƒ// Remainder of the class omitted.
20 }
With this new constructor, line 8 is changed as follows to place the robot in the city
named newYork on 4th Street and 2nd Avenue, facing east:
8 SimpleBotƒkarelƒ=ƒnewƒSimpleBot(newYork,ƒ4,ƒ2,ƒConstants.EAST);
Line 11 is omitted from the main method because that task is now performed in the
SimpleBot constructor. When the constructor is called as shown in the preceding
code, the value stored in newYork is assigned to the parameter variable aCity. The
reference to the newly created object is assigned to the implicit parameter variable
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 300
300
CHAPTER 6 | USING VARIABLES
this. Both variables are used in line 17 of Listing 6-9 to add this robot to the city KEY IDEA
known within the constructor as aCity. The effect is the same as our previous A reference to this
approach, newYork.add(karel,ƒ2). object can be passed
as a parameter
using this.
Name Conflicts
It is often the case that the natural name for a parameter is the same as the name of an
instance variable. For example, some people find the parameter names in lines 11 and
12 of Listing 6-9 awkward and would rather use names like street and avenue. In
fact, the names of the parameters can be the same as the names of the instance vari-
ables. Using this removes the ambiguity that would otherwise be present. For exam-
ple, lines 11–14 could be reimplemented as follows:
11 publicƒSimpleBot(Cityƒcity,ƒ
12 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒintƒstreet,ƒintƒavenue,ƒDirectionƒdirection)
13 {ƒsuper();
14 ƒƒthis.streetƒ=ƒstreet;
...
A temporary variable may also have the same name as an instance variable, but tem-
porary and parameter variables within the same method must have unique names.
There is, however, a danger in using the same names. As noted briefly earlier, this is
actually optional in most circumstances, and many programmers, unfortunately, habit-
ually omit it. Omitting this when the parameter name and instance variable name are
different poses no danger. But suppose this was omitted from line 14 of the preceding
code, as follows:
14 streetƒ=ƒstreet;
The compiler would interpret this as assigning the value in the parameter to itself—a
useless but perfectly valid action. The instance variable would remain uninitialized.
Like other kinds of variables, parameter variables can use the keyword final. As else-
where, it means that the variable’s value may not be changed. As with other kinds of
parameters, use final to emphasize and enforce that intention.
301
WITH
in this chapter) to create a new class, LimitedBot. Our goal is to create a kind of
VARIABLES
robot that can carry only a limited number of things; if it attempts to carry more, it will
break. Each of these limited robots will need to know two pieces of information: How
many things it can hold before breaking, and how many things it is currently holding.
We’ll call one maxHold (the maximum the robot can hold at one time) and call the
other numHeld (the number held right now).
These two pieces of information will be stored as instance variables. Why use instance
variables and not some other kind of variable? A temporary variable won’t work
because the robot needs to remember this information even when a method is not
being executed. A parameter variable isn’t what we need because we don’t want to rely
on the client to tell the robot how much it can carry every time a method is called.
302
CHAPTER 6 | USING VARIABLES
Listing 6-10 shows the beginning of our new kind of robot, LimitedBot. It includes
the two new instance variables and the constructor, but nothing else. LimitedBot
objects are identical to normal Robot objects except for the (currently unused)
instance variables.
Listing 6-10: A LimitedBot is like a normal Robot, but has two additional (yet to be used)
instance variables
1 importƒbecker.robots.*;
2
3 /** A LimitedBot can carry or hold only a limited number of things. The
4 * actual limit set when the robot is constructed.
5 *
6 * @author Byron Weber Becker */
7 publicƒclassƒLimitedBotƒextendsƒRobot
8 {
9 ƒƒprivateƒintƒmaxHold; // Maximum # of things this robot can hold.
10 ƒƒprivateƒintƒnumHeldƒ=ƒ0; // Number of things currently held by this robot.
11
12 ƒƒ/** Construct a new LimitedBot.
13 ƒƒ* @param aCity This robot's city.
14 ƒƒ* @param aStr This robot's initial street.
15 ƒƒ* @param anAve This robot's initial avenue.
16 ƒƒ* @param aDir This robot's initial direction.
17 ƒƒ* @param maxCanHold The maximum number of things this robot can carry/hold. */
18 ƒƒpublicƒLimitedBot(CityƒaCity,ƒintƒaStr,ƒintƒanAve,ƒ
19 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒDirectionƒaDir,ƒintƒmaxCanHold)
20 ƒƒ{ƒsuper(aCity,ƒaStr,ƒanAve,ƒaDir);
21 ƒƒƒƒthis.maxHoldƒ=ƒmaxCanHold;
22 ƒƒ}
23 }
The number of things held by the robot will always be zero when the robot is con-
structed, and so the numHeld instance variable is initialized to 0 when it is declared in
line 10. The initial value of maxHold, however, isn’t known when the class is written.
It is initialized in the constructor with the value passed to the maxCanHold parameter,
allowing its initial value to be determined when the LimitedBot is constructed.
303
WITH
via the call to super.
VARIABLES
6.3.2 Maintaining and Using Instance Variables
Having the maxHold and numHeld instance variables is not enough. We need to main-
tain and use the information they store.
First, we need to monitor how many things are currently held by the robot, and call
breakRobot if this number exceeds the number stored in maxHold. The number of
things held by the robot changes whenever it picks a thing up or puts a thing down.
Thus, we will need to override the definitions of pickThing and putThing.
Let’s focus on pickThing first. In pseudocode, we want it to perform the following tasks:
ifƒ(already holding the maximum number of things)
{ƒbreak the robot
}ƒelse
{ƒpick up a thing
ƒƒincrement the count of the number of things being held
}
The pseudocode for putting a thing down is similar except that there is no need to
check if the maximum has been exceeded:
These two methods are shown in lines 24–33 and 35–39 of Listing 6-11.
Listing 6-11: Source code for a kind of robot that can pick up only a limited number
ch06/limitedBot/ of things
1 importƒbecker.robots.*;
2
3 /** A LimitedBot can carry or hold only a limited number of things. The
4 * actual limit set when the robot is constructed.
5 *
6 * @author Byron Weber Becker */
7 publicƒclassƒLimitedBotƒextendsƒRobot
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 304
304
CHAPTER 6 | USING VARIABLES
Listing 6-11: Source code for a kind of robot that can pick up only a limited number
of things (continued)
8 {
9 ƒƒprivateƒintƒmaxHold; // Maximum # of things this robot can hold.
10 ƒƒprivateƒintƒnumHeldƒ=ƒ0; // Number of things currently held by this robot.
11 ƒƒ
12 ƒƒ/** Construct a new LimitedBot.
13 ƒƒ* @param aCity This robot's city
14 ƒƒ* @param aStr This robot's initial street.
15 ƒƒ* @param anAve This robot's initial avenue.
16 ƒƒ* @param aDir This robot's initial direction.
17 ƒƒ* @param maxCanHold The maximum number of things this robot can carry/hold. */
18 ƒƒpublicƒLimitedBot(CityƒaCity,ƒintƒaStr,ƒintƒanAve,ƒ
19 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒDirectionƒaDir,ƒintƒmaxCanHold)
20 ƒƒ{ƒsuper(aCity,ƒaStr,ƒanAve,ƒaDir);
21 ƒƒƒƒthis.maxHoldƒ=ƒmaxCanHold;
22 ƒƒ}
23
24 ƒƒ/** Pick up a thing. If the robot is already holding the maximum number
25 ƒƒ* of things, it breaks. */
26 ƒƒpublicƒvoidƒpickThing()
27 ƒƒ{ƒifƒ(this.numHeldƒ==ƒthis.maxHold)
28 ƒƒƒƒ{ƒthis.breakRobot("Tried to pick up too many things.");
29 ƒƒƒƒ}ƒelse
30 ƒƒƒƒ{ƒsuper.pickThing();
31 ƒƒƒƒƒƒthis.numHeldƒ=ƒthis.numHeldƒ+ƒ1;
32 ƒƒƒƒ}
33 ƒƒ}
34 ƒƒ
35 ƒƒ/** Put down one thing. */
36 ƒƒpublicƒvoidƒputThing()
37 ƒƒ{ƒsuper.putThing();
38 ƒƒƒƒthis.numHeldƒ=ƒthis.numHeldƒ-ƒ1;
39 ƒƒ}
40 }
In pickThing, we call super.pickThing() at line 30. This statement calls the unmodi- LOOKING BACK
fied version of pickThing provided by the LimitedBot’s superclass. The code surround- Overriding methods
ing this call details the additional steps that should be taken when a LimitedBot’s version was discussed in
Section 2.6.1.
of pickThing is called. super.putThing() is called at line 37 for similar reasons.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 305
305
In Listing 6-11, maxHold is given a value when the object is initialized, but thereafter
the value is unchanged. This suggests that maxHold is really a kind of constant even
though we don’t know its value until the object is instantiated.
WITH
The constructor may assign a value to a final instance variable as long as the variable
VARIABLES
hasn’t been used already. This suggests that line 9 of Listing 6-11 should be rewritten
as follows:
9 privateƒfinalƒintƒMAX_HOLD; // Maximum # of things this robot can hold.
Appropriate changes in the variable name should also be made in lines 21 and 27. A
final variable that is not initialized until later is called a blank final. The compiler must
be able to verify that a blank final is not used before it is assigned a value.
So, which is preferable: to extend a class with new functionality or modify the class itself?
As usual, the answer depends on the context. If the source code is not available (as is
the case with Robot), you can’t modify the class directly. The question becomes more
interesting when the source code is available. The decision is usually made based on
two criteria:
➤ How extensively has the class already been used, including subclasses?
Modifying a class that is extensively used carries a significant risk of breaking
code that already works. It also carries the burden of significant testing. In
these cases, extending the class is usually the better idea.
➤ Are the proposed changes useful in many circumstances? If they are, modify-
ing the class may be a good idea. However, if the changes are of limited use,
the class becomes cluttered with features that are not typically used. Extending
the class is often the wiser course in this situation as well.
306
CHAPTER 6 | USING VARIABLES
Class is not used Modify the class. Modify the class. Extend the class.
extensively.
A third option is to create a new class that makes substantial use of an existing class to
do its job. That’s the topic of Chapter 8.
are declared... inside a class but inside a method. inside a method’s Comparing the different
outside of the methods. parameter list. kinds of variables
use the final the value stored the value stored the value stored
keyword when... should not be should not be should not be
changed. changed. changed.
are named (by like methods: the first like methods: the like methods: the
convention)... “word” is lowercase; first “word” is first “word” is
subsequent “words” lowercase; lowercase;
have an initial capital. subsequent “words” subsequent “words”
If the final keyword have an initial have an initial
is used, names should capital. capital.
be all uppercase.
can be used... in any method in in the smallest block in the method where
the class. enclosing the they are declared.
declaration.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 307
307
OF
until... the object is no smallest enclosing method has finished
VARIABLES
longer used. block has finished executing.
executing.
are referenced... with the keyword with only the with only the
this, a dot, and the variable’s name. variable’s name.
variable’s name; may
be accessed with the
class name when
modifiers permit and
they have the static
keyword.
Table 6-4 can help you decide when each kind of variable is an appropriate choice
based on your program’s needs. The solutions are ordered from the most preferred to
the least preferred; therefore, read the table from the top and use the first solution that
meets your needs.
find yourself writing almost identical code look for a way to put the code in a method,
several times accounting for the differences with parameters.
308
CHAPTER 6 | USING VARIABLES
One of the hardest choices for many beginning programmers is deciding whether to use
an instance variable or a temporary variable. This choice is difficult because nearly
anything that can be done with a temporary variable can also be done with an instance
variable. This situation often leads beginning programmers to overuse instance vari-
ables and underuse temporary variables.
Suppose that you need a query, numIntersectionsWithThings, that counts the num-
ber of intersections containing Things between the robot’s current location and a wall
that is somewhere in front of it. Invoking numIntersectionsWithThings on the robot
shown in Figure 6-10 would move the robot to just before the wall and return the value 3.
(figure 6-10)
309
OF
VARIABLES
ch06/counter/
1 importƒbecker.robots.*;
2
3 publicƒclassƒCounterBot2ƒextendsƒRobotSE
4 {
5 ƒƒpublicƒCounterBot2(Cityƒc,ƒintƒstr,ƒintƒave,ƒDirectionƒd)
6 ƒƒ{ƒsuper(c,ƒstr,ƒave,ƒd);
7 ƒƒ}
8 ƒƒ
9 ƒƒpublicƒintƒnumIntersectionsWithThings()
10 ƒƒ{ƒintƒintersectionsƒ=ƒ0;
11 ƒƒƒƒwhile(true)
12 ƒƒƒƒ{ƒifƒ(this.canPickThing())
13 ƒƒƒƒƒƒ{ƒintersectionsƒ=ƒintersectionsƒ+ƒ1;
14 ƒƒƒƒƒƒ}
15 ƒƒƒƒƒƒifƒ(!this.frontIsClear())ƒƒƒ{ƒƒbreak;ƒ}
16 ƒƒƒƒƒƒthis.move();
17 ƒƒƒƒ}
18 ƒƒƒƒreturnƒintersections;
19 ƒƒ}
20 }
Does it matter whether you choose an instance variable or a temporary variable? Yes,
for the following reasons:
➤ Reading a program is easiest if variables are declared close to their use.
Temporary variables keep declarations as close to their use as possible. That
way the reader doesn’t have to remember as many details for as long a time.
➤ The class as a whole is easier to understand if it isn’t cluttered by extraneous
instance variables. Readers assume that each instance variable has a meaning
to the class as a whole and to several methods. If that’s not true, it can take
longer to understand the class.
➤ The longer lifetimes and larger scope of instance variables give programmers more
opportunity to misuse them. Don’t provide such opportunities unless you must.
➤ Extra instance variables increase the amount of memory required to run the
program. For large programs, this can become an issue because it may limit
the amount of data it can handle.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 310
310
CHAPTER 6 | USING VARIABLES
Temporary variables should be used when the value is not an attribute of the object KEY IDEA
and is primarily local to a method, or when storing a temporary value. Prime candi- Use parameter and
dates include loop counters, a temporary variable to store an intermediate calculation, temporary variables
when you can;
an accumulator such as intersections in Listing 6-13, or the temporary storage of
instance variables
the answer to a query before it’s used in further calculations. only when you must.
For each instance variable, you should think carefully about whether it must be an
instance variable. Is the data relevant to more than one public method? Does the data
represent an attribute of the class? If so, make it an instance variable. If not, consider
other options.
For example, the pickThing method in LimitedBot (see Listing 6-11) can be modified
to print out useful debugging information by adding lines 2 and 3 in the following code:
1 publicƒvoidƒpickThing()
2 {ƒSystem.out.print("PickThing: numHeld="); // debug
3 ƒƒSystem.out.println(this.numHeld);ƒ // debug
4 ƒƒifƒ(this.numHeldƒ==ƒthis.maxHold)
5 ƒƒ{ƒthis.breakRobot("Tried to pick up too many things.");
6 ƒƒ}ƒelse
7 ƒƒ{ƒsuper.pickThing();
8 ƒƒƒƒthis.numHeldƒ=ƒthis.numHeldƒ+ƒ1;
9 ƒƒ}
10 }
The result of picking up three things using the modified class is shown in Figure 6-11.
The black window in front of the usual robot window is the console.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 311
311
The print and println methods are overloaded to take all of Java’s types as argu-
ments. In line 2, the print method is used to print the given string literal. In the next
line, the println method is used to print the value stored in an integer variable.
When the plus operator (+) is used with a string, the result is a single string composed
of the first operand textually followed by the second operand. The resulting string is
then printed.
The difference between print and println is in where text will go the next time one of
these methods is called. Using print causes subsequent text to be printed on the same
line; using println causes subsequent text to be printed on the next line. The “ln” in
println stands for “line.”
A debugger is a tool that can be used to view values while the program is running. It
does not require you to add temporary code to your program. An example of one
debugger is shown in Figure 6-12. It is part of the Eclipse project, a freely available
integrated development environment. Three areas of the debugger are shown under the
robot’s window:
➤ The source code that is currently being executed is shown in the bottom left of
the figure. It helps remind the programmer which variables are currently rele-
vant. It is possible to set breakpoints before running the program. A break-
point is associated with a program statement and causes the debugger to stop
executing the program each time the statement is reached, giving the user an
opportunity to examine the values held by variables.
➤ The variables that are currently in scope are shown in the upper-right corner
of Figure 6-12. In this example, all the variables happen to be instance vari-
ables, but parameter and temporary variables can also appear in this area. The
current value held by each variable is also shown. If the variable happens to
refer to an object, a plus sign appears to the left, allowing its instance variables
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 312
312
CHAPTER 6 | USING VARIABLES
(figure 6-12)
Debuggers are powerful tools that are worth learning. However, they are also complex
and may distract beginning programmers from more important learning tasks.
313
Three Thermometer
components, each with a
different temperature
setting
The program in Listing 6-14 was used to create this image and may be used as a test
harness during the development process. It creates three instances of the Thermometer
class, displays them, and sets each to show a different temperature.
314
CHAPTER 6 | USING VARIABLES
In Section 2.7.3, we learned that the paintComponent method can be called by the
Java system at any time. The user can resize a frame or expose a previously hidden
frame. In either case, paintComponent will be called to repaint the contents of the
frame. Therefore, the paintComponent method must be able to determine what the
component should look like. For a Thermometer, this includes determining how high
the alcohol (the modern replacement for mercury) should be drawn. It does so by con-
sulting an instance variable. A client can set the instance variable to a given tempera-
ture with a small method called setTemperature.
Listing 6-15 shows the beginnings of the Thermometer class, complete with the
instance variable used to store the current temperature. Most of the code in
paintComponent must still be developed.
315
Recall that the preferred size, set in line 17, is used by the frame to determine how large
the thermometer should be. Forgetting to set the preferred size will make the compo-
nent so small that it is almost invisible.
LOOKING AHEAD This version of the class fixes the minimum and maximum temperature the thermome-
Programming ter can display with two named constants.
Project 6.15 asks you
to improve upon the Working out the actual code for paintComponent is somewhat tedious. It helps to
hard-coded minimum declare temporary variables initialized with significant values. The diagram in Figure 6-14
and maximum. illustrates the meaning of those used in Listing 6-16.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 316
316
CHAPTER 6 | USING VARIABLES
(figure 6-14)
Thermometer
fluidTop
calculations
bulbTop
stemHeight
h
stemLeft
fluidHeight
stemWidth
bulbLeft
bulbDia
The height and width of the component are found first in lines 5 and 6 and stored in
variables to make using them more convenient. All the calculations should ultimately
depend on the height and width so that the thermometer is drawn appropriately as the
component is resized.
The variables with names ending in Left and Top hold values specifying the location
of a shape. Variables with names ending in Height, Width, and Dia (short for “diam-
eter”) hold values specifying the size of a shape.
317
14 ƒƒfinalƒintƒstemHeightƒ=ƒhƒ-ƒbulbDia;
15
16 ƒƒfinalƒintƒfluidHeightƒ=ƒstemHeightƒ*ƒ
17 ƒƒƒƒƒƒƒƒ(this.tempƒ-ƒMIN_TEMP)ƒ/ƒ(MAX_TEMPƒ-ƒMIN_TEMP);
18 ƒƒfinalƒintƒfluidTopƒ=ƒstemHeightƒ-ƒfluidHeight;
19 ƒ
20 ƒƒ// paint the fluid
21 ƒƒg.setColor(Color.RED);
22 ƒƒg.fillOval(bulbLeft,ƒbulbTop,ƒbulbDia,ƒbulbDia);
23 ƒƒg.fillRect(stemLeft,ƒfluidTop,ƒstemWidth,ƒfluidHeight);
24
25 ƒƒ// paint the stem above the fluid
26 ƒƒg.setColor(Color.BLACK);
27 ƒƒg.fillRect(stemLeft,ƒ0,ƒstemWidth,ƒfluidTop);
28 }
If you run the test harness with the current version of Thermometer, you will notice
that the thermometers are painted as though the temperature is 0 rather than the tem-
peratures set in the test harness. However, if you resize the frame, forcing the ther-
mometers to be repainted, then they will be drawn with the correct temperatures. In
other words, the thermometers display a temperature change only when they are
repainted.
KEY IDEA Somehow we need to be able to trigger the repainting of the component whenever the
Call repaint when temperature changes. We do so with an inherited method, repaint. Calling repaint
instance variables after we have reset the instance variable informs the Java system that it should call
affecting the
paintComponent as soon as possible. The revised version of setTemperature is:
image change.
publicƒvoidƒsetTemperature(intƒnewTemp)
{ƒthis.tempƒ=ƒnewTemp;
ƒƒthis.repaint();
}
318
CHAPTER 6 | USING VARIABLES
for(intƒtempƒ=ƒt0.MIN_TEMP;ƒtempƒ<=ƒt0.MAX_TEMP;ƒtempƒ=ƒtemp
+ƒ1)
{ƒƒt0.setTemperature(temp);
ƒƒƒUtilities.sleep(50);
}
The call to Utilities.sleep causes the current thread to pause for 50 milliseconds,
or 0.050 seconds, to give the Java system a chance to repaint the screen—and so you
have time to see the change in the thermometer.
The sleep method should not be called inside the paintComponent method.
paintComponent is called by the Java system; it has many important things to do and
should not be forced to wait for anything.
6.8 Patterns
Every time you are writing an expression, you need values. These values could come
from any of the constructs discussed in this chapter. In almost every situation, one of
the constructs is a better choice than the others. Carefully consider which of the fol-
lowing patterns best describes your situation and is best suited to solve your problem.
Context: You have a literal value used one or more times in your program. The value is
known when you write the program and does not change while the program is running.
«type» is the type of the value stored in the constant. So far, we have discussed only
integers, but any type (including a class name) is possible. «name» is the name of the
variable, and «value» is the first (and last) value assigned to it.
Graphics programs often use many constants in the course of drawing a picture. (See
paintComponent in Section 2.7.3 for an example.) Having a named constant for each
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 319
319
6.8 PATTERNS
can become tedious, and it is common practice to use literals instead. An excellent mid-
dle ground is to look for relationships between the numbers. It is often possible to
define a few well-chosen constants that can be used in expressions to calculate the
remaining values.
Related Patterns:
➤ This pattern is a specialization of the Instance Variable pattern.
➤ When constants are used to distinguish a set of values, such as the four direc-
tions or MALE and FEMALE, the Enumeration pattern (see Section 7.7.3) is
often a better choice.
Context: An object needs to maintain a value. The value must be remembered for
longer than one method call (when a temporary variable would be appropriate). The
value is usually needed in more than one method.
Solution: Use an instance variable. Instance variables are declared within the class but
outside of all the methods. Following are examples of instance variables:
privateƒintƒnumMovesƒ=ƒ0;
privateƒintƒcurrentAve;
«accessModifier»ƒ«type»ƒ«name»ƒ=ƒ«initialValue»;
«accessModifier»ƒ«type»ƒ«name»;
where «accessModifier» is usually private and «type» is the type of the variable.
Examples include int, double, boolean, and names of classes such as Robot.
«name» is the name used to refer to the value stored. The variable’s initial value should
either be established in the declaration, as shown in the first form, or assigned in the
constructor. Assign the initial value in the declaration if all instances of the class start
with the same value. Assign it in the constructor if each instance will have its initial
value supplied by parameters to a constructor.
An instance variable may be accessed within methods or constructors with the implicit
parameter, this, followed by a dot and the name of the variable. It may also be
accessed by giving the name of the variable if the name is not the same as a parameter
or temporary variable.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 320
320
CHAPTER 6 | USING VARIABLES
An instance variable that is not explicitly initialized will be given a default value appro-
priate for its type, such as 0 for integer types and false for boolean.
Consequences: An instance variable stores a value for the lifetime of the object. It can
be explicitly changed by an assignment statement.
Related Patterns:
➤ The Instance Variable pattern is inappropriate for storing values used within a
single method for intermediate calculations, counting events, or loop indices.
Use the Temporary Variable pattern instead.
➤ The Instance Variable pattern is inappropriate for communicating a value
from client code to a method. Use the Parameterized Method pattern instead.
➤ The Instance Variable pattern always occurs within an instance of the Class
pattern.
Context: You have a class with instance variables that are private to prevent misuse by
clients. However, clients have a legitimate need to know the values of the instance vari-
ables even though they should not be allowed to directly change them.
publicƒclassƒSimpleBot
{ƒprivateƒintƒstreet;
ƒƒ...
ƒƒpublicƒintƒgetStreet()
ƒƒ{ƒreturnƒthis.street;
ƒƒ}
}
Related Patterns: The Accessor Method pattern is a specialization of the Query pattern.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 321
321
Instance variables belong to an object. Each instance of a class has its own set of
instance variables that implement that object’s attributes. The lifetime is the same as
the lifetime of the object. The scope is the entire class.
Temporary variables belong to the method or the block within a method in which they
are declared, which also limits their scope. Of the three kinds of variables, temporary
variables have the most limited scope. They are used for tasks such as storing interme-
diate calculations and counting events, such as loop iterations, within the method.
Parameter variables are temporary variables that are initialized when the method is
called. Their scope is the entire method where they are declared, and their lifetime is
for as long as the method executes. Both temporary and parameter variables disappear
when the method in which they are declared finishes execution. If the method is exe-
cuted again, space for the variable is reallocated and the variable is reinitialized.
Classes may be extended with additional instance variables, much as they can be
extended with additional methods.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 322
322
CHAPTER 6 | USING VARIABLES
constants
modified with the keyword final are
instance
variables are used to implement
attributes
may be m
ade av
c an ailabl
be e to
acc clien
ess ts v
ed ia
ha from
an assignment ve h all
a l ave of accessor
statement arg a the
e lon methods
may be
g cla
sse
s’
wit new
a
h
val gned
ue
i
ass
int
is a methods
are
g
sm
rin
may
a
cla
v e
ha rt de
sho ir
be
have
a he
t ot o
t
van
t
ts
rele
en
may be
temporary at ion
cli
n
io
mat
r
lize y
nfo
tia all
i
d
ini speci
ate
nic
mu
are
com
parameter
variables
Written Exercises
6.1 What kind of variable does not occur in a class diagram?
6.2 The Account class models a bank account. Identify which type of variable
(temporary, parameter, or instance) should be used in each of the following val-
ues. Justify your answers using Table 6-4.
a. The bank account’s balance.
b. The amount to deposit in the account.
c. The account’s current interest rate.
d. The amount of interest earned in the last month.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 323
323
(figure 6-15)
324
CHAPTER 6 | USING VARIABLES
6.6 In Section 6.2.1 we saw the following code to place a Thing on every 5th
intersection:
whileƒ(...)
{ƒifƒ(this.getAvenue()ƒ%ƒ5ƒ==ƒ0)
ƒƒ{ƒthis.putThing();
ƒƒ...
What difference would it make if the if statement’s Boolean expression was
changed to this.getAvenue()ƒ%ƒ5ƒ==ƒ2?
Programming Exercises
6.7 Finish the following code to sum, and print the individual digits stored in
digits. For example, the sum, of the digits of 312 is 6 because 3 + 1 + 2 = 6.
(Hint: Review the integer division and remainder operations and apply the
four-step process to construct a loop.)
publicƒstaticƒvoidƒmain(String[]ƒargs)
{ƒintƒdigitsƒ=ƒ312;
ƒƒintƒsumƒ...
ƒƒƒ
ƒƒSystem.out.println(sum);
}
6.8 Write a class named FixedDistanceBot that can only travel a specified num-
ber of intersections. The exact limit should be specified when the robot is con-
structed. If the limit is reached, the robot should break. Write a main method
to test your class.
6.9 Extend the harvester robot from Section 3.2.7 to pick up all the things on each
intersection (there may be 0, 1, or many), and count the total number of things
it collects. Make the total available to the robot’s client with a query. The robot
is not guaranteed to start with an empty backpack.
6.10 Write a class named DistanceBot that extends Robot. It will have a query
named totalDistance that returns the total distance traveled by the robot so
far. A second query, tripDistance, returns the distance traveled since the
“trip” was started by a call to resetTrip.
6.11 Create a component similar to the stick figure shown in Figure 2-15. Add
methods named setShirtColor and setPantsColor that each take a single
parameter of type Color. Invoking these methods should change the color of
the corresponding article of clothing. (Hint: You will need variables of type
Color; import java.awt.Color.)
6.12 Create a subclass of JFrame named JClosableFrame. Its constructor takes a
JPanel as a parameter and does everything necessary to display it. Rewrite the
main method in Listing 6-14 to test your class. (Hint: Your class will have a
constructor but no methods of its own.)
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 325
325
Programming Projects
Robot problems in this section use the simplified Robot classes. Get them from your
instructor or download them from the Examples section of www.learningwithrobots.
com/software/downloads.html.
6.14 Download the SimpleBot classes from the Robots Web site. Make the follow-
ing enhancements to the SimpleBot class. In all cases, write a main method to
test your work.
a. Complete the SimpleBot class as described in this chapter, including the
move, turnLeft, and paint methods as well as the SimpleBot constructor.
b. Add a turnRight method.
c. Add a method named goToOrigin. The effect of calling
karel.goToOrigin() is to have the robot named karel appear at the
origin, facing east, the next time its paint method is called.
d. Add a method named teleport. The effect of calling karel.teleport
(5,ƒ3) is to have karel appear on the intersection of 5th Street and
3rd Avenue the next time paint is called. The direction it faces should not
change. Of course, your method should work with values other than 5 and 3.
e. Implement a suite of three methods in the SimpleBot class that modify the
robot’s speed. ben.goFaster() causes the robot named ben to move 10%
faster. ben.goSlower() causes ben to move 10% slower. Finally,
ben.setMoveTime(400) causes ben to wait 400 milliseconds each time it
moves. Also accommodate values other than 400.
f. Modify the SimpleBot class so that its color can be specified. This change
will require a new instance variable, a change to the paint method, and a
new method named setColor that takes a parameter variable of type Color.
g. Modify the SimpleBot class so that the size of each robot can be specified.
puffer.setSize(30) causes the robot named puffer to have a body
with a radius of 30 pixels. Other features, such as the sensor, should change
size accordingly. Note that the size of the intersection should not change and
that your method should work with many different values, not just 30.
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 326
326
CHAPTER 6 | USING VARIABLES
h. Rewrite the paint method in SimpleBot so that robots have two “eyes”
set on short antennae, as shown in Figure 6-16. Choose different colors for
the eyes and the body.
(figure 6-16)
327
(figure 6-17)
Stick figures
holding hands
6 Chapter C5743 40143.ps 11/30/06 1:21 PM Page 328
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 329
We now have the intellectual tools to start writing object-oriented programs that have
nothing to do with robots. In this chapter, we’ll learn about additional kinds of infor-
mation we can store (such as dollar values, individual characters, or strings of charac-
ters). We’ll use that knowledge to build a class that could be used as part of a gas pump
at your local gas station.
One problem, however, is that such programs are not nearly as easy to debug as robot
programs because they are not as visual. We’ll start by learning some techniques for
testing and debugging our programs and finish by learning techniques for coupling a
class with a graphical user interface.
329
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 330
330
VARIABLES AND METHODS
before they buy software. We’ll begin by learning how to test the SimpleBot class programmer’s life
much easier.
CHAPTER 7 | MORE
used in Chapter 6. Later in this chapter, we’ll apply these same techniques to a non-
robot class.
It is tempting to test the SimpleBot class by writing and running a short program that
creates a robot and moves it several times, and then looking at the screen to verify that
the robot did, indeed, move correctly. The problem with this approach is that a person
must remember what should happen and verify that it actually did happen. Relying on
people for such tedious details is a risky proposition.
Remembering and verifying tedious details is something that computers do well, how- KEY IDEA
ever. Our goal is to completely automate as much of the testing as possible by writing Testing involves many
a program called a test harness. A test harness is used to test a class, and usually con- tedious details—
something computers
tains many individual tests.
are good at. Use them
as much as possible
Writing a test involves five steps.
in the testing process.
1. Decide which method you want to test.
2. Set up a known situation.
3. Determine the expected result of executing the method.
4. Execute the method.
5. Verify the results.
For example, we may want to test the move method in the SimpleBot class (Step 1).
To set up a known situation (Step 2), we create a robot named karel at (4, 2) facing
east in an empty city. This is shown in lines 7 and 8 of Listing 7-1. The choice of (4, 2)
facing east is not critical. We could just as easily use a different intersection. However,
we need to know which intersection is chosen so we can determine the expected result
(Step 3). In this case, moving from (4, 2) should result in the robot being on intersec-
tion (4, 3), still facing east.
Line 11 in Listing 7-1 executes the code we want to test (Step 4).
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 331
331
TO
➤ One line is printed for each invocation of ckEquals in lines 15–18. It prints
TEST CLASSES
“Passed” if the last two arguments have equal values. If they do not, it
prints “***Failed”. In either case, ckEquals also prints the values of both
arguments.
➤ The ckEquals method also prints the string given as the first argument. This
serves simply to identify the test.
1 importƒbecker.util.Test;
2
Test Harness 3 publicƒclassƒTestHarness
4 {
5 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
6 ƒƒ{ƒ// Set up a known situation (an empty city; a robot on (4, 2) facing east).
7 ƒƒƒƒSimpleCityƒcƒ=ƒnewƒSimpleCity();
8 ƒƒƒƒSimpleBotƒkarelƒ=ƒnewƒSimpleBot(c,ƒ4,ƒ2,ƒConstants.EAST);
9
10 ƒƒƒƒ// Execute the move method.
11 ƒƒƒƒkarel.move();
12
13 ƒƒƒƒ// Verify the results -- robot on intersection (4, 3).
14 ƒƒƒƒTestƒtesterƒ=ƒnewƒTest(); // This line isn't needed. See Section 7.5.2.
15 ƒƒƒƒtester.ckEquals("new ave",ƒ3,ƒkarel.getAvenue());
16 ƒƒƒƒtester.ckEquals("same str",ƒ4,ƒkarel.getStreet());
17 ƒƒƒƒtester.ckEquals("same dir",ƒConstants.EAST,
18 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒkarel.getDirection());
19 ƒƒ}
20 }
KEY IDEA We should not be under the illusion that Listing 7-1 is sufficient to test the move
Testing a method method. At a minimum, it should test moving in each of the four directions. If pro-
usually requires grams using SimpleBots can include walls or similar obstructions, more tests are
repeating Steps 2–5
required to verify that move behaves correctly when a robot is blocked. This observa-
several times.
tion implies that Steps 2–5 for testing a method should be repeated as many times as
necessary.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 332
332
VARIABLES AND METHODS
(figure 7-1)
What does ckEquals do? It compares the expected value (the second argument) with KEY IDEA
the actual value (the third argument) and prints an appropriate message. It is imple- ckEquals compares
mented approximately as shown in Listing 7-2. Overloaded versions for non-integer the expected value
with the actual value
types have a few minor variations.
and prints an
appropriate message.
Listing 7-2: A possible implementation of the ckEquals method for integers
1 publicƒvoidƒckEquals(Stringƒmsg,ƒintƒexpected,ƒintƒactual)
2 {ƒStringƒresult;
3 ƒƒifƒ(expectedƒ==ƒactual)
4 ƒƒ{ƒresultƒ=ƒ" Passed: "ƒ+ƒmsg;
5 ƒƒ}ƒelse
6 ƒƒ{ƒresultƒ=ƒ"*** Failed: "ƒ+ƒmsg;
7 ƒƒ}
8 ƒƒresultƒ+=ƒ": expected '"ƒ+ƒexpectedƒ+ƒ"'; actual '"ƒ+ƒactualƒ+ƒ"'.";
9 ƒƒSystem.out.println(result);
10 }
Testing a query is actually easier than testing a command. To test a command, we need
some way to verify what the command did. In the previous example, we used accessor
methods to get the current values of the critical instance variables. To test a query, we
only need to compare the query’s actual result with the expected result.
To further illustrate testing, let’s define a new SimpleBot query that answers the ques-
tion “How far is this robot from the origin?” Remember that the origin is the intersec-
tion (0, 0). Let’s assume that the distance we want is “as the robot moves” (the legs of a
triangle) rather than “as the crow flies” (the hypotenuse of a triangle). If the robot is on
Street 4, Avenue 2, the answer is 4 + 2 = 6.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 333
333
TO
To begin writing a test harness, we can perform the five steps mentioned previously.
TEST CLASSES
The code to test (Step 1) is distanceToOrigin. Our first known situation (Step 2)
will be to create a robot at the origin facing east (testing easy cases first is a good strat-
egy). In this situation, the distance to the origin should be 0 (Step 3). Executing the
code (Step 4) and verifying the result (Step 5) is shown in the following code in lines 7
and 10, respectively:
1 publicƒstaticƒvoidƒmain(String[]ƒargs)
2 {ƒ// Create a robot in an empty city at the origin facing east.
Test Harness
3 ƒƒSimpleCityƒcƒ=ƒnewƒSimpleCity();
4 ƒƒSimpleBotƒkƒ=ƒnewƒSimpleBot(c,ƒ0,ƒ0,ƒConstants.EAST);
5
6 ƒƒ// Execute the code to test.
7 ƒƒintƒdƒ=ƒk.distanceToOrigin();
8
9 ƒƒ// Verify the result.
10 ƒƒTestƒtesterƒ=ƒnewƒTest(); // This line isn't needed. See Section 7.5.2.
11 ƒƒtester.ckEquals("at origin",ƒ0,ƒd);
12 }
This is a very incomplete test, however. The distanceToOrigin query could be writ-
ten as follows and still pass this test:
publicƒintƒdistanceToOrigin()
{ƒreturnƒ0;
}
We can add more tests to this test harness that build from the original known situation.
For example, it’s not hard to see that after the previous test the robot should still be at
the origin. So let’s add another test immediately after it that moves the robot from the
origin and then checks the distance again.
1 publicƒstaticƒvoidƒmain(String[]ƒargs)
2 {ƒ// Create a robot in an empty city at the origin facing east.
3 ƒƒSimpleCityƒcƒ=ƒnewƒSimpleCity();
4 ƒƒSimpleBotƒkƒ=ƒnewƒSimpleBot(c,ƒ0,ƒ0,ƒ0);
5
6 ƒƒ// Execute the code to test.
7 ƒƒintƒdƒ=ƒk.distanceToOrigin();
8
9 ƒƒ// Verify the result.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 334
334
VARIABLES AND METHODS
16 ƒƒdƒ=ƒk.distanceToOrigin();
CHAPTER 7 | MORE
17 ƒƒtester.ckEquals("east 2",ƒ2,ƒd);
18 }
So far we have only tested the robot on streets and avenues that are numbered zero or
larger. What if the robot turned left (facing north) and moved to Street -1, as shown in
Figure 7-2? Let’s test it to make sure distanceToOrigin works correctly.
-1 0 1 2 (figure 7-2)
-1
Origin Robot at (-1, 2), three
0 moves from the origin
We could add the new test to our test harness by continuing to move the robot to (-1, 2).
The following code uses a simpler approach. It constructs a robot on intersection (-1, 2)
and then tests the result of the distanceToOrigin method. In this case, moving the
robot isn’t necessary. This code should be added after line 17 of the test harness.
SimpleBotƒk2ƒ=ƒnewƒSimpleBot(c,ƒ-1,ƒ2,ƒ0);
dƒ=ƒk2.distanceToOrigin();
tester.ckEquals("neg str",ƒ3,ƒd);
Running the test harness says that the test fails. The expected value is 3, but the actual
value is 1.
Reviewing the distanceToOrigin method shows why: we add the current street to
the current avenue. When both are positive values, that works fine. But in this situa-
tion, it gives -1 + 2, or 1—a wrong answer.
The problem is that we want to add the distance between the origin and the robot’s
street. Distances are always positive. When the street (or avenue) is negative, we need
to convert it to a positive number. We can do this with the helper method abs, short
for “absolute value.”
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 335
335
TO
4
LOOKING AHEAD
TEST CLASSES
5 privateƒintƒabs(intƒx)
In Section 7.5.2 we 6 {ƒintƒanswerƒ=ƒx;
will learn about using
7 ƒƒifƒ(xƒ<ƒ0)
a library of math
functions. It already
8 ƒƒ{ƒanswerƒ=ƒ-x;
has abs. 9 ƒƒ}ƒ
10 ƒƒreturnƒanswer;
11 }
With this change, all of the tests shown earlier will pass.
One fact that is implicit in the previous discussion is that Java allows multiple main
methods. You can have only one main method in any given class, but as many classes
as you want may each have their own main method. This is a good thing. If only one
main method were allowed, we would need to choose between writing a test harness
and writing a main method to run the program to perform its task.
KEY IDEA One common way to exploit the ability for each class to have a main method is to
Each class can have write one class that has nothing but main—the way we have been doing. This class is
its own main used to run the program to perform the desired task. However, every other class also
method.
has a main method to act as a test harness for that class. For example, the test harness
shown in Listing 7-1 is in its own class. Instead, this could be written as part of the
SimpleBot class. An outline of this approach is shown in Listing 7-3. Lines 1–14
show representative parts of the SimpleBot class. The test harness is in lines 16–28.
Listing 7-3: An outline of how to include a test harness in the SimpleBot class
Test Harness
1 importƒjava.awt.*;
2 importƒbecker.util.Test;
3 ...
4
5 publicƒclassƒSimpleBotƒextendsƒPaintable
6 {
7 ƒƒprivateƒintƒstreet;
8 ƒƒprivateƒintƒavenue;
9 ƒƒprivateƒintƒdirection;
10 ƒƒ...
11 ƒƒpublicƒSimpleBot(...)ƒ{ƒ...ƒ}
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 336
336
VARIABLES AND METHODS
Listing 7-3: An outline of how to include a test harness in the SimpleBot class (continued)
12 ƒƒpublicƒvoidƒmove()ƒ{ƒ...ƒ}
13 ƒƒ...
14
ON
16 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
17 ƒƒ{ƒ// Set up a known situation -- a robot on intersection (4, 2)
18 ƒƒƒƒSimpleCityƒcƒ=ƒnewƒSimpleCity();
19 ƒƒƒƒSimpleBotƒkarelƒ=ƒnewƒSimpleBot(c,ƒ4,ƒ2,ƒEAST);
20
21 ƒƒƒƒ// Execute the code we want to test.
22 ƒƒƒƒkarel.move();
23
24 ƒƒƒƒ// Verify the results -- robot on intersection (4, 3).
25 ƒƒƒƒTestƒtesterƒ=ƒnewƒTest(); // This line isn't needed. See Section 7.5.2.
26 ƒƒƒƒtester.ckEquals("new ave",ƒ3,ƒkarel.getAvenue());
27 ƒƒƒƒ...
28 ƒƒ}
29 }
One issue that may be initially confusing is that even though main is within the KEY IDEA
SimpleBot class, we don’t use the keyword this. Inside the test harness, we construct The main method
a specific SimpleBot object, karel. Throughout the main method, we invoke can’t use this.
karel’s methods to test what has happened to that specific object.
One advantage of placing a main method inside the class it tests is that we have access
to the classes’ private instance variables. For example, line 26 of Listing 7-3 can be
replaced with the following:
tester.ckEquals(“newƒave”,ƒ3,ƒkarel.avenue);
Many programmers take testing even further with a tool named JUnit. It provides a
graphical user interface, shown in Figure 7-3, and does a better job of isolating indi-
vidual tests from each other. More information, and the tool itself, is available at
www.junit.org.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 337
337
Java includes eight primitive types. Six of these store numbers, one stores the Boolean
values true and false, and the last one stores characters.
KEY IDEA Why would Java have six different types to store numbers? Because they differ in the size
An int can only and precision of the values they store. An int, for example, can only store values
store values in a between -2,147,483,648 and 2,147,483,647. This range is large enough to store
certain range. the net worth of most individuals, but not that of Bill Gates. It’s more than enough to
store the population of any city on earth, but not the population of the earth as a whole.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 338
338
VARIABLES AND METHODS
To address these issues, Java offers several kinds of integers, each with a different KEY IDEA
range, or number of different values it can store. The ranges of the four integer types Different types can
are shown in Table 7-1. Variables with a greater range require more memory to store. store different ranges
of values.
For programs with many small numbers to store, it makes sense to use a type with a
smaller range. Because beginning programmers rarely encounter such programs, we
won’t need to use byte and short in this book and will use long only rarely.
ON
CHAPTER 7 | MORE
Two other primitive types, float and double, store numbers with decimal places,
such as 125.25, 3.14259, or -134.0. They are called floating-point types because of the
way they are stored in the computer hardware.
Floating-point types can be so large or small that they are sometimes written in KEY IDEA
scientific notation. The number 6.022E23 has two parts, the mantissa (6.022) and Scientific notation can
the exponent (23). To convert 6.022E23 to a normal number, write down the man- be used to express
tissa and then add enough zeros to slide the decimal point 23 places to the right. If very large or very
small numbers.
the exponent is negative, you must add enough zeros to slide the decimal point that
many places to the left. For example, 6.022E23 is the same number as
602,200,000,000,000,000,000,000, while 5.89E-4 is the same as 0.000589. Their
ranges and precisions are listed in Table 7-2.
339
Floating-point numbers don’t behave exactly like real numbers. Consider, for a
moment, 1/3 written in decimal: 0.33333.... No matter how many threes you add,
0.33333 won’t be exactly equal to 1/3. The situation is similar with 1/10 in binary, the
number system computers use. It’s impossible to represent 1/10 exactly; the best we can
do is to approximate it. The closeness of the approximation is given by the precision.
floats have about 7 digits of precision, while doubles have about 16 digits. This
means, for example, that a float can’t distinguish between 1.00000001 and
1.00000002. As far as a float is concerned, both numbers are indistinguishable
from 1.0. Another effect is that assigning 0.1 to a float and then adding that num-
ber to itself 10 times does not yield 1.0 but 1.0000001.
KEY IDEA The fact that floating-point numbers are only approximations can cause programmers
Comparing floating- headaches if their programs require a high degree of precision. For beginning pro-
point numbers for grammers, however, this is rarely a concern. One exception, however, is when com-
equality is usually a paring a float or a double for equality, the approximate nature of these types may
bad idea.
cause an error. For example, the following code fragment appears to print a table of
numbers between 0.0 and 10.0, increasing by 0.1, along with the squares of those
numbers.
doubleƒdƒ=ƒ0.0;
whileƒ(dƒ!=ƒ10.0)
{ƒSystem.out.println(dƒ+ƒ“ƒ“ƒ+ƒd*d);
ƒƒdƒ=ƒdƒ+ƒ0.1;
}
0.0ƒ0.0
0.1ƒ0.010000000000000002
0.2ƒ0.04000000000000001
0.30000000000000004ƒ0.09000000000000002
0.4ƒ0.16000000000000003
Already we can see the problem: d, the first number on each line, is not increasing by
exactly 0.1 each time as expected. In the fourth line the number printed is only
approximately 0.3.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 340
340
VARIABLES AND METHODS
By the time d gets close to 10.0, the errors have built up. The result is that d skips
from 9.99999999999998 to 10.09999999999998 and is never exactly equal to
10.0, as our stopping condition requires. Consequently, the loop keeps printing for a
very long time.
The correct way to code this loop is to use an inequality, as in the following code:
ON
CHAPTER 7 | MORE
whileƒ(dƒ<=ƒ10.0)
{ƒ...
}
doubleƒdƒ=ƒ159;
Java will implicitly convert the integer 159 to a double value (159.0) and then assign
it to d.
The reverse is not true. If assigning a value to another type risks losing information, a KEY IDEA
cast is required. A cast is our assurance to the compiler that we either know from the Casting converts
nature of the problem that information will not be lost, or know that information will values from one type
be lost and accept or even prefer that result. to another.
Sometimes it loses
For example, consider the following statements: information.
doubleƒdƒ=ƒ3.999;
intƒiƒ=ƒd;
Java will display an error message regarding the second assignment because an integer LOOKING AHEAD
can’t store the decimal part of 3.999, only the 3. If we want to perform this assign- Section 7.5.2
ment anyway and lose the .999, leaving only 3 in the variable i, we need to write it as discusses a method to
follows: round a number rather
than truncate it.
doubleƒdƒ=ƒ3.999;
intƒiƒ=ƒ(int)d;
The new part, (int), is the cast. The form of a cast is the destination type placed in
parentheses. It can also apply to an entire expression, as in the following statement:
intƒiƒ=ƒ(int)(dƒ*ƒdƒ/ƒ2.5);
Casting has a high precedence, so you will usually need to use parentheses around
expressions.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 341
341
Automatic conversion to a string does not work as well for double values, where we
often want to control how many significant digits are printed. For example, the fol-
lowing code might be used in calculating the price of a used car:
doubleƒcarPriceƒ=ƒ12225.00;
doubleƒtaxRateƒ=ƒ0.15;
System.out.println(“Car:ƒ“ƒ+ƒcarPrice);
System.out.println(“Tax:ƒ“ƒ+ƒcarPriceƒ*ƒtaxRate);
System.out.println(“Total:ƒ“ƒ+ƒcarPriceƒ*ƒ(1.0ƒ+ƒtaxRate));
Car:ƒ12225.0
Tax:ƒ1833.75
Total:ƒ14058.749999999998
These results are far from ideal. We want to see a currency symbol such as $ or £
printed. All of the amounts should have exactly two decimal places, rounding as nec-
essary. The thousands should also be grouped with commas or spaces, depending on
local conventions. It’s difficult to implement all these details correctly.
Fortunately, Java provides a set of classes for formatting numbers, including curren-
cies. These classes all include a method named format that takes a number as an argu-
ment and returns a string formatted appropriately. Listing 7-4 shows how to use a
currency formatting object named money. These statements produce formatted output
such as the following:
Car:ƒ$12,225.00
Tax:ƒ$1,833.75
Total:ƒ$14,058.75
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 342
342
VARIABLES AND METHODS
4 System.out.println("Car: "ƒ+ƒmoney.format(carPrice));
CHAPTER 7 | MORE
5 System.out.println("Tax: "ƒ+ƒ
6 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒmoney.format(carPriceƒ*ƒtaxRate));
7 System.out.println("Total: "ƒ+ƒ
8 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒmoney.format(carPriceƒ*ƒ(1.0ƒ+ƒtaxRate)));
A formatting object is not normally obtained by using a constructor. Instead, a LOOKING AHEAD
factory method in the NumberFormat class is called. A factory method returns an Implementing factory
object reference, as a constructor does. Unlike a constructor, a factory method has methods will be
discussed in
the option of returning a subclass of NumberFormat that is specialized for a specific
Chapter 12.
task. In this case, the factory method tries to determine the country where the com-
puter is located and returns an object customized for the local currency.
NumberFormatƒmoneyƒ=ƒNumberFormat.getCurrencyInstance();
A formatter for general numbers can be obtained with the getNumberInstance fac-
tory method. It can be customized to format numbers with a certain number of decimal
places and to print grouping characters. Consider the following example:
NumberFormatƒfƒ=ƒNumberFormat.getNumberInstance();
f.setMaximumFractionDigits(4);
f.setGroupingUsed(true);
System.out.println(f.format(3141.59265359));
These statements will print the value 3,141.5927—the value rounded to four decimal
places with an appropriate character (in this case, a comma) used to group the digits.
Columnar Output
Programs often produce lots of numbers that are most naturally formatted in columns.
Even with the program to calculate the tax for a car purchase, aligning the labels and
numbers vertically makes the information easier to read.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 343
343
KEY IDEA The printf method is unusual in that it takes a variable number of arguments. It
printf’s format always takes at least one, called the format string, that includes embedded codes
string says how to describing how the other arguments should be printed.
format the other
arguments. Here’s an example where printf has three arguments.
System.out.printf("%-10s%10s",ƒ"Car:",ƒmoney.format(carPrice));
The first argument is the format string. It includes two format specifiers, each one
ch07/formatNumbers/
beginning with a percent (%) sign and ending with a character indicating what kind of
data to print. The first format specifier is for the second argument; the second specifier
is for the third argument. Additional specifiers and arguments could easily be added.
In each case, the s indicates that the argument to print should be a string. The 10
instructs printf to print the string in a field that is 10 characters wide. The minus sign
(-) in one says to print that string left justified (starting on the left side of the column).
The specifier without the minus sign will print the string right justified (on the right
side of the column).
This line, as specified, does not print a newline character at the end; thus, any subse-
quent output would be on the same line. We could call println() to end the line, or
we could add another format specifier. The specifier %n is often added to the format
string to begin a new line. It does not correspond to one of the arguments.
Table 7-3 gives several examples of the most common format specifiers and the results
they produce. A d is used to print a decimal number, such as an int. An f is used to
print a floating-point number, such as a double. In addition to the total field width, it
specifies how many decimal places to print. More examples and a complete description
are available in the online documentation for the java.util.Formatter class.
“%10d”,ƒ314 •••••••314
“%10.4f”,ƒ3.1415926 3.1416••••
“%-10.4f”,ƒ3.1415926 ••••3.1416
The printf method has many other options that are documented in the Formatter
class. Discussing them further, however, is beyond the scope of this book.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 344
344
VARIABLES AND METHODS
Java includes a number of shortcuts for some of the most common operations per- KEY IDEA
formed with numeric types. For example, one of the most common is to add 1 to a i++ is a shortcut for
variable. Rather than writing aveƒ=ƒaveƒ+ƒ1, Java permits the shortcut of writing i = i + 1.
ON
It is also common to add the result of an expression to a variable. For example, the fol-
lowing is SimpleBot’s move method as written in Listing 6-6:
publicƒvoidƒmove()
{ƒthis.streetƒ=ƒthis.streetƒ+ƒthis.strOffset();
ƒƒthis.avenueƒ=ƒthis.avenueƒ+ƒthis.aveOffset();
ƒƒUtilities.sleep(400);
}
Instead of repeating the variable on the right side of the equal sign, we can use the +=
operator, which means to add the right side to the value of the variable on the left, and
then store the result in the variable on the left. More precisely, «var» += «expres-
sion» means «var» = «var»ƒ+ƒ(«expression»). The parentheses are important in
determining what happens if «expression» contains more than a single value. The
following example is equivalent to the previous code:
publicƒvoidƒmove()
{ƒthis.streetƒ+=ƒthis.strOffset();
ƒƒthis.avenueƒ+=ƒthis.aveOffset();
ƒƒUtilities.sleep(400);
}
There are also -=, *=, and /= operators. They are used much less frequently but
behave the same as += except for the change in numeric operation.
The boolean type is used for true and false values. We have already seen Boolean
expressions used to control if and while statements, and as a temporary variable and
the return type in predicates (see, for example, Listing 5-3). We have also explored
using boolean values in expressions (see Section 5.4).
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 345
345
ƒƒpublicƒvoidƒbreakRobot()
ƒƒ{ƒthis.isBrokenƒ=ƒtrue;
ƒƒ}
ƒƒpublicƒvoidƒmove()
ƒƒ{ƒifƒ(!this.isBroken)
ƒƒƒƒ{ƒthis.avenueƒ=ƒ...
ƒƒƒƒƒƒthis.streetƒ=ƒ...
ƒƒƒƒ}
ƒƒ}
ƒƒ...
}
One use for characters is to control a robot from the keyboard. Sim, a superclass of
Robot, has a protected method named keyTyped that is called each time a key is
typed, yet it does nothing. The method has a char parameter containing the character
that was typed. By overriding the method, we can tell a robot to move when ‘m’ is
typed, turn right when ‘r’ is typed, and so on. The KeyBot class in Listing 7-5 defines
such a robot. The same technique can be used in subclasses of Intersection and
Thing because they all descend from Sim—the class implementing keyTyped. (When
running a program using this feature, you must click on the image of the city before it
will accept keystrokes. The image will have a black outline when it is ready to accept
keystrokes.)
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 346
346
VARIABLES AND METHODS
4 {
CHAPTER 7 | MORE
5 ƒƒpublicƒKeyBot(Cityƒc,ƒintƒstr,ƒintƒave,ƒDirectionƒdir)
6 ƒƒ{ƒsuper(c,ƒstr,ƒave,ƒdir);
7 ƒƒ}
8
9 ƒƒprotectedƒvoidƒkeyTyped(charƒkey)
10 ƒƒ{ƒifƒ(keyƒ==ƒ'm'ƒ||ƒkeyƒ==ƒ'M')
11 ƒƒƒƒ{ƒthis.move();
12 ƒƒƒƒ}ƒelseƒifƒ(keyƒ==ƒ'r'ƒ||ƒkeyƒ==ƒ'R')
13 ƒƒƒƒ{ƒthis.turnRight();
14 ƒƒƒƒ}ƒelseƒifƒ(keyƒ==ƒ'l'ƒ||ƒkeyƒ==ƒ'L')
15 ƒƒƒƒ{ƒthis.turnLeft();ƒƒƒƒ// Watch out. The above test uses
16 ƒƒƒƒ// a lowercase 'L', not a "one".
17 ƒƒ}
18 }
The parameter, key, is compared to the letters ‘m’, ‘r’, and ‘l’ in lines 10, 12, and 14. KEY IDEA
In each case, if the comparison is true (that is, the parameter contains an ‘m’, ‘r’, or Override keyTyped
‘l’), an action is taken. If a different key is pressed, the robot does nothing. A slightly to make a robot that
can be controlled
enhanced version of this method is implemented in the RobotRC class. You can extend
from the keyboard.
RobotRC anytime you want to use the keyboard as a remote control (RC) for a robot.
The 'm', 'r', and 'l' are character literals. To write a specific character value, place KEY IDEA
the character between two single quotes. What if you want to compare a value to a sin- Some characters have
gle quote? Placing it between two other single quotes (''') confuses the compiler, special meaning to
Java. They have to be
causing an error message. The solution is to use an escape sequence. An escape
written with an
sequence is an alternative way to write characters that are used in the code for other escape sequence.
purposes. The escape sequence for a single quote is \' (a backslash followed by a sin-
gle quote). All escape sequences begin with a backslash. The escape sequence is placed
in single quotes, just like any other character literal. Table 7-4 shows some common
escape sequences, many of which have their origins in controlling printers.
The last escape sequence, \udddd, is used for representing characters from a wide
range of languages, and includes everything from accented characters to Bengali char-
acters to Chinese ideograms. You can find more information online at
www.unicode.org. Unfortunately, actually using these characters requires correspond-
ing fonts on your computer.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 347
347
\\ Backslash
\t Tab—inserts space so that the next character is placed at the next tab stop.
Each tab stop is a predefined distance from the previous tab stop.
\f Form feed—moves the cursor to the top of the next page in a printer
\udddd A Unicode character, each d being a hexadecimal digit (0–9, a–f, A–F)
Strings of characters such as “Hello, karel!” are used frequently in Java programs. Strings
are stored, appropriately, in variables of type String. A string can hold thousands of char-
acters or no characters at all (the empty string). These characters can be the familiar ones
found on the keyboard or those specified with escape characters, as shown in Table 7-4.
String is not a primitive type. In fact, it is a class just as Robot is a class. On the other
hand, strings are used so often that Java’s designers included special support for them
that other classes do not have—so much special support that it sometimes feels like
strings are primitive types.
KEY IDEA The special support the String class enjoys from the Java compiler falls into three
Java provides special categories:
support for the ➤ Java will automatically construct a String object for each sequence of char-
String class.
acters between double quotes; that is, Java has literal values for strings just
like it has literal values for integers (5, -259), doubles (3.14159), and
Booleans (true).
➤ Java will “add” two strings together with the plus operator to create a new
string consisting of one string followed by the other. This is called concatenation.
➤ Java will automatically convert primitive values and objects to strings before
concatenating them with a string.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 348
348
VARIABLES AND METHODS
Listing 7-6 shows several examples of this special support. The program uses
System.out.println to print the strings, as we did in Section 6.6.1. The difference
here is the manipulations of the strings before they are printed.
Listing 7-6: A simple program demonstrating built-in Java support for the String class
ON
ch07/stringDemo/
1
CHAPTER 7 | MORE
importƒbecker.robots.*;
2 A String object is created
3 publicƒclassƒMain automatically from the string
4 { literal "Hello"
5 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
6 ƒƒ{ƒStringƒgreetingƒ=ƒ"Hello";
7 ƒƒƒƒStringƒnameƒ=ƒ"karel"; Four strings are concatenated using
the "+" operator to produce a single
8
string, "Hello, karel!"
9
10 ƒƒƒƒSystem.out.println(greetingƒ+ƒ", "ƒ+ƒnameƒ+ƒ"!");
11
The primitive value resulting from this expression is automatically
12 converted to a string and concatenated using the plus operator
13
14 ƒƒƒƒSystem.out.println("Did you know that 2*PI = "ƒ+ƒ2*Math.PIƒ+ƒ"?");
15
16 ƒƒƒƒCityƒcƒ=ƒnewƒCity();
17 ƒƒƒƒRobotƒkarelƒ=ƒnewƒRobot(c,ƒ1,ƒ2,ƒDirection.SOUTH);
18 ƒƒƒƒSystem.out.println("c="ƒ+ƒc);
19 ƒƒ}
The object referenced by c is
20 } automatically converted to a
string by calling its
Program output: toString method
Hello,ƒkarel!
Didƒyouƒknowƒthatƒ2*PIƒ=ƒ6.283185307179586?
c=becker.robots.City[SimBag[robots=[becker.robots.Robot
[street=1,ƒavenue=2,ƒdirection=SOUTH,ƒisBroken=false,ƒnumThings
InBackpack=0]],ƒthings=[]]]
In lines 6 and 7, two String objects are created using the special support the Java lan-
guage provides for strings. These lines would look more familiar if they used a normal
constructor, which works as expected:
Stringƒgreetingƒ=ƒnewƒString("Hello");
Stringƒnameƒ=ƒnewƒString("karel");
349
Finally, when Java converts an object to a string, as it does in line 18, it calls the
method named toString, which every class inherits from Object.
Overriding toString
KEY IDEA Java depends on the fact that every object has a toString method that can be called
Every class should to provide a representation of the object as a string. The default implementation,
override toString inherited from the Object class, only prints the name of the class and a number
to provide meaningful identifying the particular object. To be useful, the method should be overridden in
information.
classes you write. The information it presents is often oriented to debugging, but it
doesn’t have to be.
The standard format for such information is the name of the object’s class followed by an
open bracket, “[”. Information relevant to the object follows, and then ends with a clos-
ing bracket, “]”. This format allows objects to be nested. For example, when the City
object is printed, we see that it prints the Robot and Thing objects it references. Each of
these, in turn, print relevant information about themselves, such as their location.
Listing 7-7 shows a toString method that could be added to the SimpleBot class
shown in Listing 6-6.
350
VARIABLES AND METHODS
Querying a String
The String class provides many methods to query a String object. These include
finding out how long a string is, whether two strings start the same way, the first loca-
tion of a particular character, and so on. The most important of these queries are
shown in Table 7-5.
ON
CHAPTER 7 | MORE
charƒcharAt(intƒindex) Returns the character at the location specified by Methods that query a
the index. The index is the position of the char- string
acter—an integer between 0 (the first character)
and one less than the length of the string (the
last character).
351
It may seem strange for strings to begin indexing at zero, but this is common in com-
puter science. We have already seen it in the robot cities, where streets and avenues
begin with zero. We’ll see it again in upcoming chapters, where collections of values
are indexed beginning with zero.
KEY IDEA When a and b are primitive types, we can compare them with operators such as
>, >=, <, and <= aƒ==ƒb, aƒ<ƒb, and aƒ>=ƒb. For reference types such as String, only the == and !=
don’t work for operators work—and they do something different than you might expect.
strings.
Instead of ==, compare two strings for equality with the equals method. It returns
KEY IDEA true if every position in both strings has exactly the same character.
Use the equals
ifƒ(oneString.equals(anotherString))
method to compare
{ƒSystem.out.println(“Theƒstringsƒareƒequal.”);
strings for equality.
}
!oneString.equals(anotherString)
KEY IDEA The string equivalent to less than and greater than is the compareTo method. It can be
Use compareTo to used as shown in the following code fragment:
compare strings for
order. Stringƒaƒ=ƒ...
Stringƒbƒ=ƒ...
ifƒ(a.compareTo(b)ƒ<ƒ0)
{ƒ// a comes before b in the dictionary
}ƒelseƒifƒ(a.compareTo(b)ƒ>ƒ0)
{ƒ// a comes after b in the dictionary
}ƒelseƒ// if (a.compareTo(b) == 0)
{ƒ// a and b are equal
}
352
VARIABLES AND METHODS
If the strings have non-alphabetic characters, you may consult Appendix D to deter-
mine their ordering. For example, a fragment of the ordering is as follows:
ON
CHAPTER 7 | MORE
!ƒ“ƒ#ƒ…ƒ0ƒ1ƒ2ƒ…ƒ9ƒ:ƒ;ƒ<ƒ…ƒAƒBƒCƒ…ƒZƒ[ƒ\ƒ…ƒaƒbƒcƒ…ƒzƒ{ƒ|ƒ}
This implies that “hope!” comes before “hopeful” because ! appears before f in the
previous ordering. Similarly, “Hope” comes before “hope”.
Transforming Strings
Other methods in the String class do not answer questions about a given string, but
rather return a copy of the string that has been transformed in some way. For example,
the following code fragment prints “WarningƒWARNING”; message2 is a copy of
message1 that has been transformed by replacing all of the lowercase characters with
uppercase characters.
Stringƒmessage1ƒ=ƒ“Warning”;
Stringƒmessage2ƒ=ƒmessage1.toUpperCase();
System.out.println(message1ƒ+ƒ“ƒ“ƒ+ƒmessage2);
The designers of the String class had two options for the toUppercase method. They
could have provided a command that changes all of the characters in the given string to
their uppercase equivalents. The alternative is a method that makes a copy of the string,
changing each lowercase letter in the original string to an uppercase letter in the copy.
The designers of the String class consistently chose the second option. This makes the KEY IDEA
String class immutable. After a string is created, it cannot be changed. The methods An immutable class is
given in Table 7-6, however, make it easy to create copies of a string with specific trans- one that does not
provide methods to
formations. The StringBuffer class is similar to String, but includes methods that
change its instance
allow you to modify the string instead of creating a new one. variables.
353
As an illustration of what you can do with strings, let’s write a program that counts the
number of vowels (a, e, i, o, u) in a string. As a test, we’ll use the famous quotation
from Hamlet, “To be, or not to be: that is the question.” The expected answer is 13.
To begin solving this problem, let’s think about how to solve it without a computer.
One straightforward method is to look at each letter, proceeding from left to right. If
the letter is a vowel, we can put a tick mark on a piece of paper. When we get to the
end, the number of ticks corresponds to the number of vowels. Our program can adopt
a similar strategy by using a variable to record the number of vowels.
This illustration will require us to examine individual letters in a string and compare
letters to other letters. We don’t have experience solving these kinds of problems, so
let’s proceed by solving a series of simpler problems. First, let’s print the individual let-
ters in the quotation. This shows that we can process the letters one at a time. After
mastering that, let’s count the number of times a single vowel, such as ‘o’, occurs.
Finally, after solving these subproblems, we’ll count all the vowels.
To print all the letters in the quotation, we must access each individual letter.
According to Table 7-5, the charAt method will return an individual character from a
string. However, it needs an index, a number between 0 and one less than the length of
the string. Evidently, the length method will also be useful. To obtain the numbers
between 0 and the length, we could use a for loop. We’ll start a variable named index
at 0 and increment it by 1 each time the loop is executed until index is just less than
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 354
354
VARIABLES AND METHODS
the length. These ideas are included in the following Java program that prints each
character in the quotation, one character per line.
1 publicƒstaticƒvoidƒmain(String[]ƒargs)
2 {ƒStringƒquotationƒ=ƒ"To be, or not to be: that is the question.";
3
ON
5 ƒƒforƒ(intƒindexƒ=ƒ0;ƒindexƒ<ƒquotation.length();ƒindex++)
6 ƒƒ{ƒ// Examine one letter in the quotation.
7 ƒƒƒƒcharƒchƒ=ƒquotation.charAt(index);
8 ƒƒƒƒSystem.out.println(ch);
9 ƒƒ}
10 }
Notice that the for loop starts the index at 0, the first position in the string. The loop KEY IDEA
continues executing as long as the index is less than the length of the string. As soon as A string of length 5
it equals the length of the string, it’s time to stop. For example, Figure 7-5 illustrates a has indices numbered
0 to 4.
string of length 5, but its largest index is only 4. Therefore, the appropriate test to
include in the for loop is indexƒ<ƒquotation.length().
(figure 7-5)
Index: 0 1 2 3 4
Characters: T o b e A string with its index
positions marked
To modify this program to count the number of times ‘o’ appears, we can replace the
println with an if statement and add a counter. The call to println in line 14 con-
catenates the value of our counter variable with two strings to make a complete sen-
tence reporting the results. The modifications are shown in bold in the following code:
1 publicƒstaticƒvoidƒmain(String[]ƒargs)
2 {ƒStringƒquotationƒ=ƒ"To be, or not to be: that is the question.";
3
4 ƒƒintƒcounterƒ=ƒ0; // Count number of os.
5 ƒƒ// Loop over each letter in the quotation.
6 ƒƒforƒ(intƒindexƒ=ƒ0;ƒindexƒ<ƒquotation.length();ƒindex++)
7 ƒƒ{ƒ// Examine one letter in the quotation.
8 ƒƒƒƒcharƒchƒ=ƒquotation.charAt(index);
9 ƒƒƒƒifƒ(chƒ==ƒ'o')
10 ƒƒƒƒ{ƒcounterƒ+=ƒ1;
11 ƒƒƒƒ}
12 ƒƒ}
13
14 ƒƒSystem.out.println("There are "ƒ+ƒcounterƒ+ƒ" occurrences of 'o'.");
15 }
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 355
355
We can reduce the number of tests if we first transform the quote using toLowerCase,
as shown in line 3 of Listing 7-8. This assures us that all vowels will be lowercase.
KEY IDEA The indexOf method shown in Table 7-5 offers an interesting possibility. It will search
indexOf searches a a string and return the index of the first occurrence of a given character. If the charac-
string for a particular ter isn’t there, indexOf returns -1. Suppose we take a letter from our quotation and
character. search for it in a string that has only vowels. If the letter from the quotation is a vowel,
it will be found and indexOf will return a 0 or larger. If it’s not there, indexOf will
return -1. This idea is implemented in Listing 7-8. The changes from the previous ver-
sion are again shown in bold.
KEY IDEA Programmers often need a variable that holds a limited set of values. For example, we
Java version 1.5 or may you need to store a person’s gender—either male or female. For this we need only
higher is required to two values.
use this feature.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 356
356
VARIABLES AND METHODS
We could define some constants, using m for male and f for female, as follows:
publicƒclassƒPersonƒextendsƒObject
{ƒpublicƒstaticƒfinalƒcharƒMALEƒ=ƒ'm';
ƒƒpublicƒstaticƒfinalƒcharƒFEMALEƒ=ƒ'f';
ƒƒprivateƒStringƒname;
ON
ƒƒprivateƒcharƒgender;
CHAPTER 7 | MORE
ƒƒpublicƒPerson(StringƒaName,ƒcharƒaGender)
ƒƒ{ƒsuper();
ƒƒƒƒthis.nameƒ=ƒaName;
ƒƒƒƒthis.genderƒ=ƒaGender;
ƒƒ}
ƒƒ...
}
But still, someone could create a Person object like this, either by mistake or maliciously:
Personƒjuanƒ=ƒnewƒPerson("Juan",ƒ'z');
Is Juan male or female? Neither. This mistake might create a severe problem later in the
program. It might crash, or it could just cause embarrassment if Juan happened to be
male and was assigned to a sports team with the following if statement:
ifƒ(juan.getGender()ƒ==ƒMALE)
{ƒadd to the boy’s team
}ƒelse
{ƒadd to the girl’s team
}
A better solution is to define an enumeration, also called an enumerated type. An enu- KEY IDEA
meration lists all of the possible values for that type. Those values can be used as liter- An enumeration has
als in the program, and the compiler will allow only those literals to be used. This an explicitly listed set
makes it impossible to assign Juan the gender of ‘z’. Direction, used extensively in of values.
An enumeration for gender can be defined as shown in Listing 7-9. Like a class, the
code is placed in a file matching the type name, Gender.java.
357
KEY IDEA The Person class shown earlier can be rewritten using this enumeration, as shown in
The compiler Listing 7-10. Notice that Gender is used as a type, just like int or Robot, when the
guarantees that only instance variable gender is declared in line 9. Similarly, it’s used to declare a parame-
valid values are ter variable in line 12 and a return type in line 19. In each of these cases, the Java com-
assigned to
enumerations.
piler will guarantee that the value is Gender.MALE, Gender.FEMALE, or null—and
nothing else. null is a special value that means “no value”; we will learn more about
null in Section 8.1.2.
The main method in lines 24–29 uses the value Gender.MALE twice, once to construct
a new Person object and once to test that the getGender method returns the
expected value.
358
VARIABLES AND METHODS
27 ƒƒƒƒtester.ckEquals("gender",ƒGender.MALE,ƒjuan.getGender());
28 ƒƒ}
29 }
ON
CHAPTER 7 | MORE
For our first example, we’ll start small and write a class that could be used as part of a LOOKING AHEAD
gas pump. Every gas pump must have a meter to measure the gas that is delivered to In this chapter’s GUI
the customer. Of course, measuring the gas is not enough. We must also be able to get section, we’ll learn
the measurement so that we can display it on the gas pump. Our meter can also pro- how to add a
prepared user
vide the following information: interface to this class.
➤ The price of one unit of gas (the price per liter or price per gallon)
➤ The octane level of the gas (a performance measure, typically between 87 and 93)
➤ A marketing name for that kind of gas (for example, “silver” or “ultra”)
➤ The total cost of the gas delivered to the customer
In addition, when one customer is finished and another one arrives, we must be able to
reset the measurements.
We’ll call this class a Meter. To develop it, we’ll build on the testing strategies outlined
in Section 7.1.1 and include a main method for testing. An initial skeleton is shown in
Listing 7-11. It extends Object because a meter doesn’t seem to be based on any of the
classes we’ve seen so far. It includes a constructor with no parameters (so far), and a
main method for testing purposes. The main method creates a new Meter object, but
there is nothing to test (yet).
1 publicƒclassƒMeterƒextendsƒObject
2 {
3 ƒƒpublicƒMeter()
4 ƒƒ{ƒsuper();
5 ƒƒ}
6
7 ƒƒ// Test the class.
8 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 359
359
9 ƒƒ{ƒMeterƒmƒ=ƒnewƒMeter();
10 ƒƒ}
11 }
KEY IDEA We’ll proceed by repeating the following steps until we think we’re finished with the class:
Write tests as you ➤ Choose one part of the description that we don’t have working and decide
write the class. what method is required to implement it.
➤ Understand what the method is to do and give it a name.
➤ Write one or more tests to determine if the method is working correctly.
➤ Write code so that the method passes the test(s).
Novice Well, nothing is working, so we’re really just looking for something to imple-
ment. I think the first bullet in the description, to provide the price of one unit
of gas, would be a good place to start.
Novice Return the price of one unit of gas. I guess that would be, for example,
$1.109/liter or $2.85/gallon. I think a good name for the method would be
getUnitCost.
Expert So, you’re assuming that gas costs $1.109 per liter?
Novice Yes.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 360
360
VARIABLES AND METHODS
Expert How would you implement getUnitCost so that it passes this test?
Novice It’s easy. Just return the value. I’ll even throw in the documentation:
/** Get the cost per unit of fuel.
* @return cost per unit of fuel */
publicƒdoubleƒgetUnitCost()
ON
{ƒreturnƒ1.109;
CHAPTER 7 | MORE
A number with a decimal point like 1.109 can be stored in a variable of type
double, so that will be the return type of the method.
Expert Aren’t you assuming that gas is always $1.109 per liter? What if the price goes
up or down? Or what if the gas pump can deliver three different grades of
gasoline? Surely they wouldn’t all have the same price.
Novice I see your point. Somehow each Meter object should have its own price for
the gas it measures, just like each Robot object must have its own street and
avenue. To test that, we want to have two Meter objects, each with a differ-
ent price:1
tester.ckEquals("Cost 1",ƒ1.109,ƒm1.getUnitCost());
tester.ckEquals("Cost 2",ƒ1.159,ƒm2.getUnitCost());
It sounds like we need to have an instance variable to store the unit price.
Expert Suppose you had an instance variable. How would you initialize it?
Novice Well, it couldn’t be where the instance variable is declared because then we’re
right back where we started—each Meter object would always have the same
price for its gas. I guess we’ll have to initialize it in the constructor. I think that
means the constructor requires a parameter so that the price can be specified
when the Meter object is created.
Putting these observations together results in the class shown in Listing 7-12. It adds an
instance variable, unitCost, at line 5 to remember the unit cost of the gas for each
Meter object. The instance variable is initialized at line 11 using the parameter vari-
able declared in line 9. In line 22, the value 1.109 is passed to the Meter constructor.
This value is copied into the parameter variable unitCost declared in line 9. The
value in unitCost is then copied into the instance variable in line 11. The value is
stored in unitCost for as long as the object exists (or it is changed with an assignment
statement).
Finally, the contents of unitCost are returned at line 17 each time getUnitCost is called.
1 ckEquals verifies that two double values differ by less than 0.000001.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 361
361
1 importƒbecker.util.Test;
2
3 publicƒclassƒMeterƒextendsƒObject
4 {
5 ƒƒprivateƒdoubleƒunitCost;
6
7 ƒƒ/** Construct a new Meter object.
8 ƒƒ* @param unitCost The cost for one unit (liter or gallon) of gas */
9 ƒƒpublicƒMeter(doubleƒunitCost)
10 ƒƒ{ƒsuper();
11 ƒƒƒƒthis.unitCostƒ=ƒunitCost;
12 ƒƒ}
13
14 ƒƒ/** Get the cost per unit of fuel.
15 ƒƒ* @return cost per unit of fuel */
16 ƒƒpublicƒdoubleƒgetUnitCost()
17 ƒƒ{ƒreturnƒthis.unitCost;
18 ƒƒ}
19
20 ƒƒ// Test the class.
21 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
22 ƒƒ{ƒMeterƒm1ƒ=ƒnewƒMeter(1.109);
23 ƒƒƒƒTestƒtesterƒ=ƒnewƒTest(); // This line isn't needed. See Section 7.5.2.
24 ƒƒƒƒtester.ckEquals("unit cost",ƒ1.109,ƒm1.getUnitCost());
25 ƒƒƒƒMeterƒm2ƒ=ƒnewƒMeter(1.149);
26 ƒƒƒƒtester.ckEquals("unit cost",ƒ1.149,ƒm2.getUnitCost());
27 ƒƒ}
28 }
Two other parts of the requirements—getting the octane level and getting the market-
ing name—follow a similar strategy. The difference is that they will use an integer and
a String, respectively. See Listing 7-13 for their implementations.
Expert So, how are you going to implement the actual measurement of the gas?
Wasn’t the point of the Meter class to measure how much gas is delivered to
a customer?
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 362
362
VARIABLES AND METHODS
Novice Yes. Somehow, it seems we need to find out when the pump is actually pump-
ing gas—and how much. You know, when the handle is only squeezed a little
way, only a little gas flows from the pump into the car. But when you squeeze
the handle all the way, a lot of gas flows.
Expert It sounds like the pump—the code that is going to be using your Meter
ON
CHAPTER 7 | MORE
class—needs to call a method every time a little bit of gas is pumped. Does it
get called repeatedly?
Novice Yes, and it needs to tell how much gas was pumped in that time. The job of
the Meter object is to keep track of the units of gas that are pumped.
Novice Sure. Think of a real pump. It has a motor to pump the gas. Every time the
motor goes around, some gas is pumped. How much depends on the speed of
the motor.
In our system, it’s as if the motor called a method in the Meter class every
time it turns. Furthermore, it will tell that method how much gas it pumped.
If the motor is turning slowly, it pumps only a small amount of gas; but if the
motor is turning fast, it pumps more. We’ll add up all the units of gas that are
pumped to calculate the total amount delivered to the customer.
Expert What do you want to call this method that is called by the motor?
Novice How about calling it pump? It will need a parameter, so the full signature will be
publicƒvoidƒpump(doubleƒhowMuch)
Expert How would you test this method? How will you know if it’s working
correctly?
Novice It’s like the move method in the Robot class. To test it, we had to have some
queries: getAvenue and getStreet. For the Meter class, we’ll need a
query—something like getVolumeSold.
363
Meterƒmƒ=ƒnewƒMeter(1.109);
tester.ckEquals("vol. sold",ƒ0.0,ƒm.getVolumeSold());
m.pump(0.02);
m.pump(0.03);
m.pump(0.01);
tester.ckEquals("vol. sold",ƒ0.06,ƒm.getVolumeSold());
Novice Well, somehow we need to add up all the units of gas that get passed as an
argument to the pump command. I’m thinking of using a temporary variable
inside the pump command.
Expert Are you sure about that? Doesn’t a temporary variable disappear each time the
method is finished, only to be re-created the next time the method is called?
Besides, how would getVolumeSold get access to a temporary variable?
Novice You’re right. We should use an instance variable instead. It maintains a value
even when a method is not being executed—and every method, including
getVolumeSold—can access an instance variable.
Expert Sounds good. What about resetting when a new customer comes? That was
another one of the requirements. I think we’re also supposed to return the cost
of the gas sold.
Novice We’ll create a reset method that will assign 0.0 to the volumeSold instance
variable. A method named calcTotalCost can simply return the volume
sold times the cost per unit. Both of those values will be stored in instance
variables.
364
VARIABLES AND METHODS
Novice Much like the others. We’ll set up a Meter object with a known unit price for
the gas. We’ll “pump” some gas and then call getVolumeSold and
calcTotalCost. Then we can reset the pump and verify that the volume sold
is back to 0.
365
37 ƒƒ}
38 ƒƒ
39 ƒƒ/** Get the label for this meter's fuel. For example, "Gold" or "Ultra".
40 ƒƒ* @return this meter's fuel label */
41 ƒƒpublicƒStringƒgetLabel()
42 ƒƒ{ƒreturnƒthis.label;
43 ƒƒ}
44
45 ƒƒ/** Pump some fuel into a tank. This method is called
46 ƒƒ* repeatedly while the "handle" on the pump is pressed.
47 ƒƒ* @param howMuch How much fuel was pumped since the last time
48 ƒƒ* this method was called. */
49 ƒƒpublicƒvoidƒpump(doubleƒhowMuch)
50 ƒƒ{ƒthis.volumeSoldƒ=ƒthis.volumeSoldƒ+ƒhowMuch;
51 ƒƒ}
52
53 ƒƒ/** Get the volume of fuel sold to this customer.
54 ƒƒ* @return volume of fuel sold */
55 ƒƒpublicƒdoubleƒgetVolumeSold()
56 ƒƒ{ƒreturnƒthis.volumeSold;
57 ƒƒ}
58
59 ƒƒ/** Calculate the total cost of fuel sold to this customer.
60 ƒƒ* @return price/unit * number of units sold */
61 ƒƒpublicƒdoubleƒcalcTotalCost()
62 ƒƒ{ƒdoubleƒtCostƒ=ƒthis.unitCostƒ*ƒthis.volumeSold;
63 ƒƒƒƒreturnƒtCost;
64 ƒƒ}
65
66 ƒƒ/** Reset the meter for a new customer. */
67 ƒƒpublicƒvoidƒreset()
68 ƒƒ{ƒthis.volumeSoldƒ=ƒ0.0;
69 ƒƒ}
70
71 ƒƒ// Test the class.
72 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
Test Harness 73 ƒƒ{ƒTestƒtesterƒ=ƒnewƒTest();
74 ƒƒƒƒMeter3ƒm1ƒ=ƒnewƒMeter3(1.109,ƒ87,ƒ"Regular");
75 ƒƒƒƒtester.ckEquals("unit cost",ƒ1.109,ƒm1.getUnitCost());
76 ƒƒƒƒtester.ckEquals("octane",ƒ87,ƒm1.getOctane());
77 ƒƒƒƒtester.ckEquals("label",ƒ"Regular",ƒm1.getLabel());
78
79 ƒƒƒƒMeter3ƒm2ƒ=ƒnewƒMeter3(1.149,ƒ89,ƒ"Ultra");
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 366
366
VARIABLES AND METHODS
Listing 7-13: The completed code for the Meter3 class (continued)
80 ƒƒƒƒtester.ckEquals("unit cost",ƒ1.149,ƒm2.getUnitCost());
81 ƒƒƒƒtester.ckEquals("octane",ƒ89,ƒm2.getOctane());
82 ƒƒƒƒtester.ckEquals("label",ƒ"Ultra",ƒm2.getLabel());
ON
83
CHAPTER 7 | MORE
84 ƒƒƒƒtester.ckEquals("volSold",ƒ0.0,ƒm2.getVolumeSold());
85 ƒƒƒƒm2.pump(0.02);
86 ƒƒƒƒm2.pump(0.03);
87 ƒƒƒƒm2.pump(0.01);
88 ƒƒƒƒtester.ckEquals("volSold",ƒ0.06,ƒm2.getVolumeSold());
89 ƒƒƒƒtester.ckEquals("totCost",ƒ0.06*1.149,ƒm2.calcTotalCost());
90 ƒƒƒƒm2.reset();
91 ƒƒƒƒtester.ckEquals("after reset",ƒ0.0,ƒm2.getVolumeSold());
92 ƒƒƒƒtester.ckEquals("after reset",ƒ0.0,ƒm2.calcTotalCost());
93 ƒƒ}
94 }
Instance variables are always associated with a specific object. Each Robot object
knows which avenue and street it is on. Each Meter object knows the price of the gas
it is measuring.
A class variable (also called a static variable) relates to the class as a whole rather than to
an individual object. A class variable is declared using the static keyword and is used
to store information common to all the instances of the class.
Consider an analogy: suppose that people are objects and that all people live in the
same town. Some information is specific to each individual—their name, age, birth date,
and so on. This information is stored in instance variables. But other information is
known by everyone—the current year, the name of the town, the name of the mayor,
whether the sun is up, and so on. In this situation, it doesn’t make sense for each
person object to have its own instance variable to store the year. Using a class variable,
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 367
367
A class variable is declared like an instance variable but includes the static keyword:
publicƒclassƒPersonƒextendsƒObject
{ƒ...
ƒƒprivateƒintƒbirthYear;
ƒƒprivateƒstaticƒintƒyear; // a class variable
ƒƒ...
}
Inside the class, a class variable can be accessed using the name of the class, the name
only, or this. For example, here are three different implementations of the method
getAge:
publicƒintƒgetAge()
{ƒreturnƒPerson.yearƒ–ƒthis.birthYear;
}
publicƒintƒgetAge()
KEY IDEA {ƒreturnƒyearƒ–ƒthis.birthYear;
}
Access a class
variable with the
name of the class publicƒintƒgetAge()
containing it. {ƒreturnƒthis.yearƒ–ƒthis.birthYear;
}
Of these three, the first is preferred because it is clear that year is a class variable. The
second example is probably the most common because it saves a few keystrokes (this
could also be omitted for birthYear). Accessing the year with this.year strongly
implies that year is an instance variable and is discouraged.
A method may also change a class variable. For example, the following method could
be used on January 1:
publicƒvoidƒincrementYear()
{ƒPerson.yearƒ=ƒPerson.yearƒ+ƒ1;
}
The effect of this is to change the year for every Person object—and it’s accomplished
with only one method call.
Class variables are created and initialized before a class is first used. They are set up
even before the first object is created for that class.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 368
368
VARIABLES AND METHODS
One use of class variables is to assign individual objects identification numbers. For
example, suppose that we want each person to have a unique identification number.
Obviously, if each person has a unique number, we need to store it in an instance vari-
able. We can use a class variable to make it unique, as follows:
ON
CHAPTER 7 | MORE
➤ Declare a class variable to store the ID number to assign to the next Person
object created.
➤ In the Person constructor:
➤ Assign the ID number using the class variable.
➤ Increment the class variable in preparation for assigning the next number.
publicƒclassƒPersonƒextendsƒObject
{ƒ...
ƒƒprivatefinalƒintƒid;
Assign a Unique ID
ƒƒprivateƒstaticƒintƒnextIDƒ=ƒ1000000; // first id is 1000000
ƒƒ...
ƒƒ
ƒƒpublicƒPerson(Stringƒname)
ƒƒ{ƒsuper();
ƒƒƒƒthis.idƒ=ƒPerson.nextID;
ƒƒƒƒPerson.nextID++;
ƒƒƒƒ...
ƒƒ}
}
With this scheme, every time a Person object is created, it is assigned an ID number.
Because nextID is a class variable and is incremented as soon as it has been assigned,
the next Person object constructed will receive the next higher number.
Class variables are quite rare in object-oriented code. If you find yourself declaring a KEY IDEA
class variable, you should be able to clearly explain why every instance of the class Class variables are
should access the same value or why there won’t be any instances of the class at all. relatively rare in
object-oriented code.
The static keyword can also be applied to methods; doing so, however, involves a KEY IDEA
trade-off. On the one hand, such a method cannot access any instance variables and Class methods cannot
are limited to calling methods that are also declared static. On the other hand, class use instance variables
methods can be called using only the name of the class. Because no object is needed, or nonstatic methods.
369
publicƒstaticƒvoidƒincrementYear()
{ƒPerson.yearƒ=ƒPerson.yearƒ+ƒ1;
}
KEY IDEA With this change, the year can be incremented as follows:
Class methods can be
Person.incrementYear();
called without using
an object. This works even if no Person objects have been created yet. Using a specific object
such as john.incrementYear() also works but using the class name is preferred
because it tells the reader that incrementYear applies to the entire class.
One of Java’s provided classes, java.lang.Math, contains only class methods. For
example, consider a method to calculate the maximum of two numbers:
publicƒstaticƒintƒmax(intƒa,ƒintƒb)
{ƒintƒanswerƒ=ƒa;
ƒƒifƒ(bƒ>ƒa)
ƒƒ{ƒanswerƒ=ƒb;
ƒƒ}ƒ
ƒƒreturnƒanswer;
}
This method does not use any instance variables. In fact, all of the methods in the Math
class are like this. Because the Math class does not have any instance variables, all of
the methods are static. Thus, all of the methods are called using the class name, Math,
as a prefix, as shown in the following example:
intƒmƒ=ƒMath.max(0,ƒthis.getStreet());
Most of the functions in the Math class are listed in Table 7-7. Some of them are over-
loaded with different numeric types for their parameters.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 370
370
VARIABLES AND METHODS
intƒƒƒƒabs(intƒx) absolute value of x; also overloaded for long Many of the mathematical
doubleƒabs(doubleƒx) and float functions included in
java.lang.Math
doubleƒacos(doubleƒx) arccosine of x, 0.0 ≤ x ≤ π
doubleƒasin(doubleƒx)
CHAPTER 7 | MORE
In Section 7.1.2, we wrote our own version of the absolute value function to use in the
distanceToOrigin query. We now know that we could have used the Math class, as
follows:
publicƒintƒdistanceToOrigin()
{ƒreturnƒMath.abs(this.street)ƒ+ƒMath.abs(this.avenue);
}
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 371
371
Our version of distanceToOrigin was the “as the robot moves” interpretation. If
we wanted the “as the crow flies” interpretation, we could use the Pythagorean theo-
rem (a2 + b2 = c2) and the square root function, as follows:
publicƒdoubleƒdistanceToOrigin()
{ƒdoubleƒa2ƒ=ƒthis.streetƒ*ƒthis.street; // one way to square a #
ƒƒdoubleƒb2ƒ=ƒMath.pow(this.avenue,ƒ2.0); // another way to square a #
ƒƒreturnƒMath.sqrt(a2ƒ+ƒb2);
}
In Section 7.2.3 we discussed casting. For example, when the variable d holds 3.999,
the statement intƒiƒ=ƒ(int)d assigns the value 3 to the variable i. In many cases,
however, we want the nearest integer, not just the integer portion. For example, we
want to round 3.999 to 4.
The Math class has a round method that will do just that. However, when the method
is passed a double as an argument it returns a long integer. This implies that we often
cast the result when working with integers. For example,
intƒiƒ=ƒ(int)Math.round(d);
KEY IDEA One of the most fun methods in the Math class is random. Each time it is called, it
random returns a returns a number greater than or equal to 0 and less than 1. When called repeatedly,
pseudorandom the sequence of numbers appears to be random.2 The first 10 numbers returned in one
number, x, such that
experiment are shown in Figure 7-6. The first number in the sequence depends on the
0 ≤ x < 1.
date and time the program begins running.
2 These numbers appear to be random but are not. If the numbers were really random, the next num-
ber could not be predicted. Because the next number in these sequences can be predicted, they are
called “pseudorandom.”
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 372
372
VARIABLES AND METHODS
A computer implementation of a game with dice will often use random to simulate the
dice. In this case, we need to map a double between 0 and 1 to an integer between 1
and 6. The following method will do so:
publicƒintƒrollDie()
{ƒreturnƒ(int)(Math.random()ƒ*ƒ6ƒ+ƒ1);
ON
}
CHAPTER 7 | MORE
We can understand how this works with a slight variation of an evaluation diagram, as
shown in Figure 7-7. The fact that the random method returns a value greater than or
equal to 0 and less than 1 is reflected below its oval with the notation [0, 0.9999...].
After the multiplication by 6, the expression has a value in the range [0, 5.9999...], a
number greater than or equal to 0 and less than 6. After the other operations are car-
ried out, we see that the result is an integer between one and six—exactly what is
needed to simulate rolling a die.
The Character class is automatically imported into every Java class and includes a
number of methods for classifying characters. A selection of these methods is shown in
Table 7-8. They are all declared static and can be called using the Character class
name, as shown in the following example:
ifƒ(Character.isDigit(ch))...
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 373
373
The methods in the Test class are other examples of class methods. The code for
ckEquals, as shown in Listing 7-2, does not use any instance variables or nonstatic
methods and can therefore be declared with the static keyword. Indeed, all the meth-
ods in the class are like this.
Because the methods are static, a typical test harness will not instantiate a Test object.
For example, the test harness in Listing 7-1 could be rewritten by removing line 14 and
replacing tester with test in lines 15–17, as follows:
1 importƒbecker.util.Test;
2
3 publicƒclassƒTestHarnessƒextendsƒObject
4 {
5 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
6 ƒƒ{ƒ// Set up a known situation (an empty city; a robot on (4, 2) facing east).
7 ƒƒƒƒSimpleCityƒcƒ=ƒnewƒSimpleCity();
8 ƒƒƒƒSimpleBotƒkarelƒ=ƒnewƒSimpleBot(c,ƒ4,ƒ2,ƒConstants.EAST);
9
10 ƒƒƒƒ// Execute the move method.
11 ƒƒƒƒkarel.move();
12
13 ƒƒƒƒ// Verify the results -- robot on intersection (4, 3).
14 ƒƒƒƒTestƒtesterƒ=ƒnewƒTest();
15 ƒƒƒƒtesterTest.ckEquals("new ave",ƒ3,ƒkarel.getAvenue());
16 ƒƒƒƒtesterTest.ckEquals("same str",ƒ4,ƒkarel.getStreet());
17 ƒƒƒƒtesterTest.ckEquals("same dir",ƒConstants.EAST,
18 karel.getDirection());
19 }
20 }
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 374
374
VARIABLES AND METHODS
The City class in the becker library automatically displays the city which is usually
not desirable in a test harness. This behavior can be controlled with another class
method, showFrame. The following code fragment shows how to use this method to
avoid having the city show.
publicƒstaticƒvoidƒmain(String[]ƒargs)
ON
{ƒCity.showFrame(false);
CHAPTER 7 | MORE
ƒƒCityƒcƒ=ƒnewƒCity();
ƒƒ...
}
We also write a class method for every program—main. Java requires main to be sta-
tic so that the Java system can call it using only the name of the class containing it. The
name of the class is passed to the Java system when the program is run.
(figure 7-8)
The problem set refers to several such GUIs in the becker.xtras package. A problem will
often begin by directing you to explore the documentation for a particular package. You
may want to do that now for the gasPump package. Go to www.learningwithrobots.com.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 375
375
This gas pump user interface is set up for a program that uses three instances of the
Meter class—one for each of three different octane levels. Of course, each octane level
has its own price.
Listing 7-14: A sample main method to run our class (Meter) with the provided graphical user
ch07/gasPump/ interface
1 importƒbecker.xtras.gasPump.*;
2
3 /** Run a gas pump with a graphical user interface.
4 *
5 * @author Byron Weber Becker */
6 publicƒclassƒMainƒextendsƒObject
7 {
8 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)ƒ
9 ƒƒ{ƒ// Create three meters for the pump.
10 ƒƒƒƒMeterƒsilverƒ=ƒnewƒMeter(1.109,ƒ87,ƒ"Silver");
11 ƒƒƒƒMeterƒgoldƒ=ƒnewƒMeter(1.149,ƒ89,ƒ"Gold");
12 ƒƒƒƒMeterƒplatinumƒ=ƒnewƒMeter(1.199,ƒ93,ƒ"Platinum");
13
14 ƒƒƒƒ// Create the graphical user interface.
15 ƒƒƒƒGasPumpGUIƒguiƒ=ƒnewƒGasPumpGUI(
16 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒsilver,ƒgold,ƒplatinum,ƒ"Liter");
17 ƒƒ}
18 }
The graphical user interface class GasPumpGUI will not work with just any class. It
must somehow be assured that the Meter objects passed in lines 15 and 16 have meth-
ods to get the price of the gasoline, the octane level, how much gas has been pumped to
the current customer, and how much that gasoline is worth so that it can display this
information to the user. Furthermore, just having methods that perform these functions
is not enough. The methods must be named exactly as GasPumpGUI expects, return the
expected types of values, and take the expected arguments; otherwise, it won’t be able
to call them.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 376
376
VARIABLES AND METHODS
This is a common problem: Two classes need to work together, but they are written by
different people at different times and places. This problem was also faced by the pro-
grammers who wrote the classes used in graphical user interfaces, such as JComponent,
JButton, and JFrame. To fully exploit their functionality, classes written several years
ago must be assured that objects we give them possess methods with specified signatures.
ON
Fortunately, Java provides a solution. The person who writes the first class also pro-
CHAPTER 7 | MORE
vides a list of the methods it requires to be in the second class. The list written by the
author of GasPumpGUI includes the following methods:
publicƒdoubleƒgetUnitCost();
publicƒdoubleƒgetVolumeSold();
publicƒintƒgetOctane();
publicƒStringƒgetLabel();
publicƒvoidƒreset();
publicƒvoidƒpump(doubleƒhowMuch);
publicƒdoubleƒcalcTotalCost();
This list, together with documentation, is put into a Java interface. Unfortunately, the KEY IDEA
word interface has two meanings in this section. One meaning is “graphical user inter- A Java interface is
face,” like the one shown in Figure 7-8. The other meaning—the one intended here—is used to guarantee the
a Java file used to guarantee that a class contains a specified set of methods. presence of specified
methods.
Listing 7-15 shows a complete interface except for the documentation, and is similar to
a class. It has a name (IMeter) and must be in a file with the same name as the inter-
face (IMeter.java). The list of methods is enclosed in curly braces. Interfaces may
also have constants, defined as they would be defined in a class. An interface should be
documented like a class.
3 It is possible, however, for an interface to extend another interface. In fact, it can extend several
interfaces, but that’s beyond the scope of this textbook.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 377
377
1 publicƒinterfaceƒIMeter
2 {
3 ƒƒpublicƒdoubleƒgetUnitCost();
4 ƒƒpublicƒintƒgetOctane();
5 ƒƒpublicƒStringƒgetLabel();
6 ƒƒpublicƒdoubleƒgetVolumeSold();
7 ƒƒpublicƒvoidƒreset();
8 ƒƒpublicƒvoidƒpump(doubleƒhowMuch);
9 ƒƒpublicƒdoubleƒcalcTotalCost();
10 }
publicƒGasPumpGUI(IMeterƒlowOctane,ƒIMeterƒmedOctane,ƒ
KEY IDEA ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒIMeterƒhighOctane,ƒStringƒvolumeUnit)
An interface name can
As this example shows, an interface can be used as the type in a variable declaration,
be used to declare the
type of a variable. including parameter variables, temporary variables, and instance variables.
The way we use IMeter is in the line that begins the definition of the Meter class:
There is no required relationship between the names IMeter and Meter, although
they are often similar. Both names are chosen by programmers, but should follow con-
ventions. In the becker library, the convention is for interface names to begin with I.
What follows the I should give an indication of the interface’s purpose. The person
implementing the Meter class can choose any name he wants, but should of course fol-
low the usual conventions for naming a class.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 378
378
VARIABLES AND METHODS
A class can implement as many interfaces as required, although implementing only one is
the usual case. To implement more than one, list all the interfaces after the implements
keyword, separating each one from the next with a comma.
What happens if the Meter class omits one of the methods specified by IMeter, say
calcTotalCost? The Java compiler will print an error message and refuse to compile
ON
the class. The error message might refer to a missing method or might say that the class
CHAPTER 7 | MORE
In a sense, we developed the Meter class in a backwards fashion. We first wrote the
class and then found out that it just happened to match the IMeter interface. A more
usual situation is one where we know, at the beginning, that we will be implementing a
particular interface. Suppose, for example, that our instructions were to develop a class
to use with the graphical user interface in the becker.xtras.gasPump package. As
soon as we investigated the package, we would know that we need to write a class
implementing the IMeter interface.
Begin by creating a class with your chosen name and a main method to be used for LOOKING BACK
testing. Add the implements keyword followed by the name of the interface. Add the A stub is a method
methods specified by the interface and a constructor that is implied by the main with just enough code
method shown in the documentation. For each method with a non-void return type, to compile. Stubs
were first discussed in
add a return statement—it doesn’t matter what value is returned—so that the method Section 3.2.2.
will compile. Such a method is called a stub. Following these clues results in the skele-
ton shown in Listing 7-16. Some development environments will do this much for you
almost automatically.
Finally, write tests and develop the methods, as we did earlier in this chapter.
Listing 7-16: Beginning the Meter class with methods required to implement IMeter
ch07/gasPump/
1 importƒbecker.xtras.gasPump.IMeter;
2 importƒbecker.util.Test;
3
4 publicƒclassƒMeterƒextendsƒObjectƒimplementsƒIMeter
5 {
6 ƒƒpublicƒMeter(doubleƒunitCost,ƒintƒoctaneRating,ƒ
7 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒStringƒtheLabel)ƒ
8 ƒƒ{ƒsuper();
9 ƒƒ}
10
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 379
379
11 ƒƒpublicƒdoubleƒgetUnitCost()
12 ƒƒ{ƒreturnƒ0.0;
13 ƒƒ}
14
15 ƒƒpublicƒintƒgetOctane()
16 ƒƒ{ƒreturnƒ0;
17 ƒƒ}
18
19 ƒƒpublicƒStringƒgetLabel()
20 ƒƒ{ƒreturnƒ"dummy";
21 ƒƒ}
22
23 ƒƒpublicƒvoidƒpump(doubleƒhowMuch)
24 ƒƒ{
25 ƒƒ}
26
ƒƒƒƒ...
38
39 ƒƒ/** To use for testing. */
40 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
41 ƒƒ{ƒMeterƒmƒ=ƒnewƒMeter(1.109,ƒ87,ƒ"Regular");
42 ƒƒ}
43 }
KEY IDEA The Meter class is the “model” part of this pattern. It keeps track of the information
The model must that the user interface—the “view” and “controller” parts—displays. The model must
inform the user inform the user interface each time information on the screen needs updating. In practice,
interface when this means calling a method named updateAllViews at the end of each method in
changes have
Meter that changes an instance variable. This can always be done in the same way, as
been made.
shown in Listing 7-17. The changes from the previous version of Meter (Listing 7-13)
are shown in bold.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 380
380
VARIABLES AND METHODS
Listing 7-17: Code required in the Meter class to inform the view of changes
ch07/gasPump/
1 importƒbecker.gasPump.IMeter;
2 importƒbecker.util.*;
3
ON
4 publicƒclassƒMeterƒextendsƒObjectƒimplementsƒIMeter
CHAPTER 7 | MORE
views, declared in line 6, is an object that maintains a list of graphical user interface
parts (the views) that need to be updated when this model changes. The class,
ViewList, is imported from the package becker.util in line 2.
The graphical user interface adds views to this list by calling the method addView,
which is declared in lines 21–23. It receives a parameter that implements the IView
interface. By using an interface, we don’t need to know exactly what kind of object is
passed as an argument—only that it includes the methods named in IView. The
addView method doesn’t actually do anything with the view except tell the list of
views to add it.
With this infrastructure in place, the last step is to call the updateAllViews method
in the views object at the appropriate times. It should be called at the end of each
method in the model that changes an instance variable. What happens if you forget to
call updateAllViews? The user interface will not change when you expect it to.
Finally, addView is in the IMeter interface even though it was omitted from Listing 7-15.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 381
381
7.7 PATTERNS
7.7 Patterns
Context: You want to increase the reliability of your code and make the development
process easier with testing.
Solution: Write a main method in each class. The following template applies:
importƒbecker.util.Test;
publicƒclassƒ«className»ƒ...
{ƒ// Instance variables and methods
ƒƒ...
ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
ƒƒ{ƒ// Create a known situation.
ƒƒƒƒ«className»ƒ«instance»ƒ=ƒnewƒ«className»(...);
Verifying the results will often require multiple lines of code. A typical test harness will
include many tests, all of which set up a known situation, execute some code, and then
verify the results.
Consequences: Writing tests before writing code helps you focus on the code you need
to write. Being able to test as you write usually speeds up the development process and
results in higher quality code.
Related Pattern: The Test Harness pattern is a specialization of the Java Program pattern.
Name: toString
Context: You would like to be able to easily print information about an object, usually
for debugging purposes.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 382
382
VARIABLES AND METHODS
Solution: Override the toString method in the Object class. The usual format lists
the class name with values of relevant instance variables between brackets, as shown in
the following template:
publicƒStringƒtoString()
{ƒƒreturnƒ“«className»[“ƒ+
ON
ƒƒƒƒƒƒƒƒƒƒ“«instanceVarName1»=”ƒ+ƒthis.«instanceVarName1»ƒ+
CHAPTER 7 | MORE
ƒƒƒƒƒƒƒƒƒƒ“,ƒ«instanceVarName2»=”ƒ+ƒthis.«instanceVarName2»ƒ
+
ƒƒƒƒƒƒƒƒƒƒ...
ƒƒƒƒƒƒƒƒƒƒ“,ƒ«instanceVarNameN»=”ƒ+ƒthis.«instanceVarNameN»ƒ
+
ƒƒƒƒƒƒƒƒƒƒ“]”;
}
Name: Enumeration
Context: You would like to have a variable with a specific set of values such as MALE
and FEMALE or the four compass directions.
Solution: Define an enumeration type listing the desired values. A template follows:
publicƒenumƒ«typeName»
{ƒ«valueName1»,ƒ«valueName2»,ƒ...,ƒ«valueNameN»
}
For example, a set of values identifying styles of jeans for a clothing store inventory
system could be defined as follows:
publicƒenumƒJeanStyle
{ƒƒCLASSIC,ƒRELAXED,ƒBOOT_CUT,ƒLOW_RISE,ƒSTRAIGHT
}
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 383
383
publicƒclassƒDenimJeans
{ƒprivateƒJeanStyleƒstyle;
ƒƒpublicƒDenimJeans(JeanStyleƒaStyle)
ƒƒ{ƒthis.styleƒ=ƒaStyle;
ƒƒ}
ƒƒpublicƒJeanStyleƒgetStyle()
ƒƒ{ƒreturnƒthis.style;
ƒƒ}
}
Consequences: Variables using an enumeration type are prevented from having any
value other than those defined by the enumeration and the special value null, helping
to avoid programming errors. Well-chosen names help make programs more under-
standable. Enumerations cannot be used with versions of Java prior to 1.5.
Related Pattern: Prior to Java 1.5, programmers often used the Named Constant pat-
tern to define a set of values. The Enumeration pattern is a better choice.
Context: Each instance of a specified class needs a unique identifier. The class should
not depend on something external to itself to establish and maintain the uniqueness of
the identifiers.
Solution: Store the unique identifier in an instance variable. Use a class variable to
maintain the next unique identifier to assign. For example:
publicƒclassƒ«className»
{ƒprivatefinalƒintƒ«uniqueID»;
ƒƒprivateƒstaticƒintƒ«nextUniqueID»ƒ=ƒ«firstID»;
ƒƒpublicƒ«className»()
ƒƒ{ƒsuper();
ƒƒƒƒthis.«uniqueID»ƒ=ƒ«className».«nextUniqueID»;
ƒƒƒƒ«className».«nextUniqueID»++;
ƒƒ}
}
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 384
384
VARIABLES AND METHODS
Consequences: Unique identifiers are assigned to each instance of the class for each exe-
cution of the program. If objects are stored in a file and then read again (see Section 9.4),
care must be taken to save and restore «nextUniqueID» appropriately.
Related Patterns: This pattern makes use of the Instance Variable pattern.
ON
CHAPTER 7 | MORE
Methods and variables provide the essential tools needed to write classes that address
many different kinds of problems; the gas pump meter class is just one example.
Interfaces include a list of methods that implementing classes are required to define.
Interfaces allow classes to work together in situations where it isn’t possible or desir-
able to specify a class name. One example of this is when two programmers work on a
project, one writing the graphical user interface and the other writing the model.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 385
385
e
lud
point types
verifies
inc
e
lud
inc
c
orrect b
verifies correct values in
types
numeric
inc
ehavior
lud types
e
n
tur
re
of
ve Strings
ha
methods char
ve
define
ha
alidv
enumerations boolean
variables
explicitly interfaces
ho
list valid
ld
ca
nb
e
list required
objects
can
be
values
hav
e th
share
eir
own
instance
variables
methods
class
variables
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 386
386
VARIABLES AND METHODS
Written Exercises
7.1 For each of the following situations, what would be the best choice(s) for the
ON
variable’s type? Answer with one or more of int, double, char, boolean,
CHAPTER 7 | MORE
Programming Exercises
7.5 Run the following main method. Describe what happens. Based on what you
know about the range of the type byte, why do you think this occurs?
publicƒstaticƒvoidƒmain(String[]ƒargs)
{ƒforƒ(byteƒbƒ=ƒ0;ƒbƒ<=ƒ128;ƒbƒ+=ƒ1)
ƒƒ{ƒSystem.out.println(b);
ƒƒƒƒUtilities.sleep(30);
ƒƒ}
}
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 387
387
(figure 7-9) 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20 2 4 6 8 10 12 14 16 18 20
Multiplication tables 3 6 9 12 15 18 21 24 27 30 3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40 4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50 5 10 15 20 25 30 35 40 45 50
6 12 18 24 30 36 42 48 54 60 6 12 18 24 30 36 42 48 54 60
7 14 21 28 35 42 49 56 63 70 7 14 21 28 35 42 49 56 63 70
8 16 24 32 40 48 56 64 72 80 8 16 24 32 40 48 56 64 72 80
9 18 27 36 45 54 63 72 81 90 9 18 27 36 45 54 63 72 81 90
10 20 30 40 50 60 70 80 90 100 10 20 30 40 50 60 70 80 90 100
7.8 Rewrite the SimpleBot class to use an enumeration for the directions.
7.9 Write a class that implements IMeter but contains no methods whatsoever.
Compile the class. What error message or messages does your compiler give
you concerning the missing methods?
7.10 Write a class named BustedBot that extends RobotSE. A BustedBot is unre-
liable, occasionally turning either right or left (apparently at random) before
moving. The probabilities of turning are given as two values (the probability of
turning left and the probability of turning right) when the BustedBot is con-
structed. Write a main method that demonstrates your class.
Programming Projects
7.11 Explore the documentation for becker.xtras.comboLock. Write a class
named CombinationLock that implements the IComboLock interface. Run it
with the graphical user interface provided in becker.xtras.comboLock.
ComboLockGUI. The result should be as shown in Figure 7-10. (Hint: This
project is easier than the gas pump example.)
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 388
388
VARIABLES AND METHODS
(figure 7-10)
7.12 Implement a Counter class that could be used as part of the admission pro-
gram for a carnival. A Counter object will keep track of how many people
have entered the carnival so far. Each time a person enters, the increment
method will be called. A query, getCount, will return the number of people
who entered so far. A command, reset, will reset the counter back to zero to
begin counting the next day. Write a main method to test your class.
7.13 Implement a class, FuelUse, to track the fuel use in an automobile. The
fillTank method is called each time fuel is added to the automobile. It
requires two arguments: the amount of fuel added and the distance driven since
the last time the tank was filled. Provide two queries. One, getMileage,
returns the miles per gallon or liters per 100 km (depending on your local con-
vention) since record keeping began. The other query, getTripMileage,
returns the miles per gallon or liters per 100 km since the most recent invoca-
tion of the command resetTrip. Return -1 if mileage is requested when no
miles have actually been traveled. Write a main method to test your class.
7.14 Explore the documentation for becker.xtras.grapher. The provided
graphical user interface, GrapherGUI, will display the graph of a mathematical
function when given a class that implements one of the interfaces IFunction,
IQuadraticFunction, or IPolynomialFunction (see Figure 7-11).
(figure 7-11)
Graphing a mathematical
function
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 389
389
publicƒintƒgetBalance()
publicƒStringƒgetOwner()
publicƒvoidƒdeposit(doubleƒhowMuch)
publicƒvoidƒwithdraw(doubleƒhowMuch)
publicƒvoidƒpayInterest(doubleƒrate)
The last method adds one month’s interest by multiplying the rate divided by
12 times the current balance and adding the result to the current balance. Write
a test harness.
7.17 Explore the documentation for becker.xtras.radio. Write two classes, one
named RadioTuner that extends Radio and implements the ITuner interface,
and another named Main that runs the program. The result should be similar
to Figure 7-12. The graphical user interface will use RadioTuner to keep track
of the current frequency, to search up and down for the next available fre-
quency, and to remember up to five preset frequencies.
7 Chapter C5743 40143.ps 11/30/06 1:30 PM Page 390
390
VARIABLES AND METHODS
figure 7-12
(figure 7-13)
So far, our programs have usually required writing only a single class plus the main
method. Almost any program of consequence, however, involves at least several classes
that work together—or collaborate—to solve the problem. In fact, most of our pro-
grams already have this property of collaboration. For example, the Robot class col-
laborates with City and Intersection objects, and the Meter class collaborates
with the GasPumpGUI that displays it. However, the mechanics of these collaborations
have usually been hidden.
Now that we have many programming tools at our disposal, we will move away from
the robot examples. The rest of the book uses examples involving a Person class, a
program for a charitable organization, games, and others.
391
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 392
392
CHAPTER 8 | COLLABORATIVE CLASSES
In this section, we will develop a simple Person class, first as a single class using the
techniques we’ve seen so far, but then using collaborating classes. Complex concepts can
be modeled more easily using collaborating classes because they can divide the work.
Our simple Person class will be oriented toward registering births and deaths, perhaps
within a government, an insurance company, or a genealogical program. It will model
a person’s name, mother, father, birth date, and death date. Of course, it will need a
constructor and some accessor methods. We’ll also be interested in a daysLived
method. If the person has died, daysLived returns the number of days between his
birth and death dates. If the person is still alive, it returns the number of days between
his birth and the current date.
Building on what we have already learned, it’s not hard to imagine how a Person class
could be constructed. A suggested class diagram is shown in Figure 8-1, and an initial
test harness is shown in Listing 8-1.
Listing 8-1: The beginnings of a test harness for the Person class
1 importƒbecker.util.Test;
2
3 publicƒclassƒPersonƒextendsƒObject
4 {
5 ƒƒ// instance variables and methods omitted
6
7 ƒƒ// Test the class.
8 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
9 ƒƒ{ƒPersonƒpƒ=ƒnewƒPerson("Joseph Becker",ƒ
10 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ"Jacob B. Becker",ƒ"Elizabeth Unruh",ƒ1900,ƒ6,ƒ14);
11
12 ƒƒƒƒp.setDeathDate(1901,ƒ6,ƒ14);
13 ƒƒƒƒTest.ckEquals("exactly 1 year",ƒ365,ƒp.daysLived());
14 ƒƒƒƒp.setDeathDate(1901,ƒ6,ƒ13);
15 ƒƒƒƒTest.ckEquals("1 year less a day",ƒ364,ƒp.daysLived());
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 393
393
16 ƒƒƒƒp.setDeathDate(1902,ƒ6,ƒ15);
17 ƒƒƒƒTest.ckEquals("2 years plus a day",ƒ365*2+1,ƒp.daysLived());
18 ƒƒ}
19 }
+Person(String aName,
String dad, String mom,
int bYear, int bMonth, int bDay)
+Person(String aName,
String dad, String mom,
int bYear, int bMonth, int bDay,
int dYear, int dMonth, int dDay)
+int daysLived( )
+String getFather( )
+String getMother( )
+String getName( )
+void setDeathDate(int dYear,
int dMonth, int dDay)
...
All of the methods shown in the class diagram should be easy to write and test with the
exception of daysLived. The test harness chooses several easy ages to calculate—
exactly one year old, a year less one day, and two years plus a day. Many other combi-
nations would be worth testing, but these three make a good start.
After considerable thought, we might come up with pseudocode for daysLived that
appears to solve the problem:
394
CHAPTER 8 | COLLABORATIVE CLASSES
days = 0
for each full year lived
{ days = days + days in the year (remember leap years!)
}
This is a complicated algorithm and some problems haven’t been solved yet (finding KEY IDEA
the number of days between January 1 and a given date, getting today’s date, and Delegate peripheral
determining if a year is a leap year). Furthermore, these details are not part of the main details to a separate
class.
purpose of the class: maintaining information about a person. The Person class would
be easier to write and maintain if the details related to dates were in a separate class.
Using a separate class for dates is also a good idea because working with dates is a
common activity. Having a separate class allows us to write and debug the class once
but use it in many classes. For these reasons, we should either write our own Date
class or find one that has already been written. For both of these scenerios, we need to
learn to write the Person class to make effective use of a date class; this is the primary
focus of this chapter.
In fact, Java provides classes to deal with dates. One is GregorianCalendar in the
package java.util. It is rather complex to use, however. A simpler class is found in
becker.util and is called DateTime. We’ll use this class to simplify our implemen-
tation of Person.
One possible class diagram for DateTime is shown in Figure 8-2. The diagram is
abbreviated because, as the name implies, the class also handles time. This aspect has
been omitted from the class diagram.
The first constructor in this class creates an object corresponding to the current date,
the second constructor allows you to create an object for a specific date, and the third
creates a copy of the specified DateTime object. The add methods allow the date to be
adjusted, either forward or backward in time. The daysUntil method calculates the
number of days between two dates.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 395
395
Listing 8-2 shows a simple program to calculate and print Luke’s age, in days. It uses
two of the constructors and the query daysUntil to calculate the number of days
from Luke’s birthday until the current date.
Running this program on the day this paragraph was written gives an answer of
5,009 days.
Listing 8-2: A simple program to calculate and print someone’s age, in days
ch08/lukesAge/
1 importƒbecker.util.DateTime;
2
3 publicƒclassƒMain
4 {ƒpublicƒstaticƒvoidƒmain(String[]ƒargs)ƒ
5 ƒƒ{
6 ƒƒƒƒDateTimeƒlukesBDƒ=ƒnewƒDateTime(1990,ƒ10,ƒ1);
7 ƒƒƒƒDateTimeƒtodayƒ=ƒnewƒDateTime();
8
9 ƒƒƒƒintƒdaysOldƒ=ƒlukesBD.daysUntil(today);
10 ƒƒƒƒSystem.out.println("Luke is "ƒ+ƒdaysOldƒ+ƒ" days old.");
11 ƒƒ}
12 }
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 396
396
CHAPTER 8 | COLLABORATIVE CLASSES
Using the DateTime class, we can replace six instance variables in our original class
with only two—one to represent the birth date and another to represent the death date.
Besides eliminating instance variables, some of the code from the Person class can
now be delegated to the DateTime class. This is like a high-level manager delegating
work to one of her employees. Delegation can make more effective use of the resources
available.
In Listing 8-3, this delegation of work occurs at line 45. The daysLived method uses KEY IDEA
the daysUntil method in DateTime by calling this.birth.daysUntil, which is Collaborative classes
just like calling lukesBD.daysUntil (line 9, Listing 8-2) except that luke was a tem- are all about getting
someone else to do
porary variable within the main method. Here, we use this to access the instance
the work.
variable referring to the DateTime object. In both cases, we are asking a DateTime
object to perform a service on our behalf—and if DateTime can do it for us, we don’t
have to do it ourselves.
But we’re getting ahead of ourselves. Lines 12 and 13 of Listing 8-3 show the declara-
tion of the two DateTime objects to store the birth and death dates. These declarations
are like other instance variable declarations except that instead of a primitive type such
as int, they use the name of a class or interface.
Listing 8-3: An implementation of Person that collaborates with the DateTime class
ch08/collabPerson/
1 importƒbecker.util.Test;
2 importƒbecker.util.DateTime;
3
4 /** Represent a person.
5 *
6 * @author Byron Weber Becker */
7 publicƒclassƒPersonƒextendsƒObject
8 {
9 ƒƒprivateƒStringƒname;ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// person's name
10 ƒƒprivateƒStringƒmother;ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// person's mother's name
11 ƒƒprivateƒStringƒfather;ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// person's father's name
Has-a (Composition)
12 ƒƒprivateƒDateTimeƒbirth;ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// birth date
13 ƒƒprivateƒDateTimeƒdeathƒ=ƒnull;ƒƒƒƒƒƒƒƒƒ// death date (null if still alive)
14
15 ƒƒ/** Represent a person who is still alive. */
16 ƒƒpublicƒPerson(StringƒaName,ƒStringƒmom,ƒStringƒdad,ƒ
17 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒintƒbYear,ƒintƒbMonth,ƒintƒbDay)
18 ƒƒ{ƒthis(aName,ƒmom,ƒdad,ƒbYear,ƒbMonth,ƒbDay,ƒ0,ƒ0,ƒ0);
19 ƒƒ}
20
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 397
397
The instance variable birth is initialized in line 30 to refer to a new DateTime object.
The form of its initialization is like all the objects we’ve constructed except that we use
this to access the instance variable assigned the new value. The birth date is always
dependent on information passed to the constructor’s parameters and is therefore
always performed in the constructor.
KEY IDEA We say birth “refers” to an object rather than “contains” an object. This is a subtlety
Variables refer to that we’ll explore in detail in Section 8.2. Until then, we’ll use the appropriate language
objects rather than for accuracy even though it hasn’t been fully explained.
containing them.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 398
398
CHAPTER 8 | COLLABORATIVE CLASSES
null Values
Unlike birth, death may or may not refer to an object, depending on whether the KEY IDEA
person has already died. Lines 13 and 32 address the issue of what to do with a person Use null when there
who hasn’t died. The declaration in line 13 assumes that the person has not died and is no object to which
the variable can refer.
initializes death to the special value null. null can be assigned to any reference vari-
able and means that the variable does not refer to any object at all. If it turns out that
the person has died, the variable is reinitialized in line 32 with a DateTime object.
This example represents a common situation: A reference variable is needed but some-
times no object is appropriate to store there. At those times, use null. In this case,
storing null means that the person has not yet died. We can determine if the person
has died by comparing death with null using the ==ƒand != operators. This is
shown in line 39. If the death date is null, the person is still alive and the temporary
variable endDate is assigned the current date. Otherwise, endDate is assigned the
date the person died.
Null values can lead to trouble for beginning and experienced programmers alike. The
problem stems from assuming the variable refers to an object when it does not. For
example, suppose you want to know how many days have passed since a person died.
A natural approach is to add the following method to Person:
publicƒintƒdaysSinceDeath()
{ƒDateTimeƒtodayƒ=ƒnewƒDateTime();
ƒƒreturnƒthis.death.daysUntil(today);
}
If death refers to a DateTime object, this works as desired. However, if death con- KEY IDEA
tains null, executing this code will result in a NullPointerException. An excep- Variables containing
tion stops the program and prints a message that contains helpful information for null can’t be used
finding the problem. Adding a line that calls daysSinceDeath to the main method in to call methods.
Exceptionƒinƒthreadƒ“main”ƒjava.lang.NullPointerException
ƒƒƒƒƒƒƒƒatƒPerson.daysSinceDeath(Person.java:53)
ƒƒƒƒƒƒƒƒatƒPerson.main(Person.java:75)
This message says that the problem was a NullPointerException (which means we KEY IDEA
tried to use a null value as if it referred to an object). Furthermore, it tells us that it Exceptions give
occurred in the method we added (daysSinceDeath), which would appear in Listing 8- useful information to
3 at line 53. Note that the error message tells us the filename and line number. If we’re help find the error.
curious about why the program was executing daysSinceDeath in the first place, the
subsequent line(s) trace the execution all the way back to the main method.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 399
399
KEY IDEA We have used class diagrams regularly to give an overview of an individual class. These
Class diagrams show diagrams can also be used to show the relationships between collaborating classes. In fact,
the relationships we’ve already seen class diagrams showing such collaborating classes: when we extended
between one class to form a new one with additional capabilities (see Sections 2.2 and 3.5.3). In
collaborating classes.
that situation, we generally place the superclass above the subclass and connect the two
with a closed arrow pointing to the superclass. A generic example is shown in Figure 8-3.
Subclass
attributes
methods
However, the Person class does not extend DateTime (nor is the reverse true), and so
we use a different diagramming convention. This convention uses an open-headed
arrow from one class to the other. The tail of the arrow is the class containing the
instance variable and the head of the arrow is the class representing the variable’s type.
Usually the classes are drawn side by side, if possible. A class diagram for the Person
class in Figure 8-4 serves as an example.
400
CHAPTER 8 | COLLABORATIVE CLASSES
Another feature of the diagram is the multiplicity near the arrowhead. The 1..2 in the
diagram shows that each Person object uses at least one but no more than two
DateTime objects. A class diagram will show each class only once, no matter how Has-a (Composition)
many objects are actually created using the classes. In general, the first number is the
minimum number of objects that will be used, and the second number is the maximum
number that will be used in the running program.
Other multiplicities are common. 1 is an abbreviation for 1..1 and means that exactly one
object is used. An asterisk (*) is used to mean “many.” An asterisk by itself is an abbrevia-
tion for 0..* meaning “anywhere from none to many.” If there will always be at least one
but possibly many, use 1..*. An arrow without an explicit multiplicity is assumed to be 1.
In Section 1.1.2, we briefly discussed the terms client and server. Here we see those
roles depicted graphically. The arrow goes from the client to the server. The client,
Person, requests a service such as finding the days until another date. The server,
DateTime, is the class or object that performs the service.
How do you know which diagramming convention to use? If you already have the Java
code, you examine the code. If the code says publicƒclassƒXƒextendsƒY, use the
“is-a” relationship shown in Figure 8-3. If the class has an instance variable referring
to an object, use the “has-a” relationship shown in Figure 8-4.
“Is-a” comes from the sentence “An X is a kind of Y.” For example, “a Harvester robot LOOKING AHEAD
is a kind of Robot” (see Listing 3-3) or “a Lamp is a kind of Thing” (see Listing 2-6). We’ll examine is-a
Other examples include “a Circle is a kind of Shape,” “an Employee is a kind of relationships more
Person,” and “an Automobile is a kind of Vehicle.” Given two classes, if a sentence carefully in Chapter 12.
like any one of these makes sense, then using extends and a diagram like Figure 8-3 is
often the right thing to do.
On the other hand, it’s more often the case that “an X has a Y.” In that case, we use the
“has-a” relationship, also called composition. “A Person has a birth date” or “a
GasPump has a Meter” or “an Automobile has an Engine.” Has-a relationships are
implemented by adding an instance variable in the class that “has” something and is
diagrammed similar to Figure 8-4.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 401
401
LOOKING BACK Passing object references as arguments is like passing an integer: declare a parameter
Overloading involves variable in the method’s declaration and pass a reference to an object when the method
two or more methods is called. For example, the setDeathDate method (lines 46–48 in Listing 8-3) could
with the same name be overloaded with another version of setDeathDate that takes an object reference as
but different
signatures. See
an argument:
Section 6.2.2.
publicƒvoidƒsetDeathDate(DateTimeƒdeathDate)
{ƒthis.deathƒ=ƒdeathDate;
}
Both this method and the original accomplish the same purpose: assigning a new
DateTime object to the death instance variable. The difference is in where the object
is constructed. In the original version, the method received the year, month, and day,
and then constructed the object itself. In this version, the client constructs the object.
We have been using temporary variables to refer to objects since our first program. In
our first program, we wrote the following lines:
8 Cityƒpragueƒ=ƒnewƒCity();
9 Thingƒparcelƒ=ƒnewƒThing(prague,ƒ1,ƒ2);
10 Robotƒkarelƒ=ƒnewƒRobot(prague,ƒ1,ƒ0,ƒDirection.EAST);
We didn’t mention that prague, karel, and parcel are all temporary variables refer-
ring to objects, but they are. They can be similarly used in any method, not just main.
However, remember that temporary variables only exist while the method containing
them is executing. As soon as the method is finished, so are the temporary variables.
Finally, a query may return an object reference as easily as it can return an integer. For
example, we could add a query to our Person class to get the person’s birth date.
Listing 8-4 shows an abbreviated version of the class.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 402
402
CHAPTER 8 | COLLABORATIVE CLASSES
7 publicƒclassƒPersonƒextendsƒObject
8 {ƒ...ƒ// instance variables omitted
12 ƒƒprivateƒDateTimeƒbirth;ƒƒƒƒƒƒƒƒƒƒƒƒ// birth date
13 ƒƒprivateƒDateTimeƒdeath;ƒƒƒƒƒƒƒƒƒƒƒƒ// death date (null if still alive)
ƒƒ...
51
52 ƒƒpublicƒDateTimeƒgetBirthDate()
53 ƒƒ{ƒreturnƒthis.birth;
54 ƒƒ}
55 }
A client could use this query to compare the ages of two persons, as in the following
example. Assume that luke and caleb both refer to Person objects.
1 DateTime lukesBD = luke.getBirthDate();
2 ifƒ(lukesBD.isBefore(caleb.getBirthDate())
3 {ƒSystem.out.println("Luke is older.");
4 }ƒelseƒifƒ(caleb.getBirthDate().isBefore(lukesBD))
5 {ƒSystem.out.println("Caleb is older.");
6 }ƒelse
7 {ƒSystem.out.println("Luke and Caleb are the same age.");
8 }
In line 1, the getBirthDate query is used to assign a value to the temporary variable
lukesBD.
The isBefore query is used in line 2 to compare two dates—Luke’s birth date and
Caleb’s birth date. In this case, Luke’s birth date is held in a temporary variable, but the
value to use for Caleb’s birth date is obtained directly from the relevant Person object
via our new query.
Line 4 shows that the object reference returned by getBirthDate does not even have KEY IDEA
to be saved in a variable before it can be used to call a method. Read the statement left Methods that return
to right. The first part, caleb, is a reference to a Person object. Any such reference references can be
can be used to call the methods in the object, including getBirthDate. This call chained together,
eliminating the need
returns a reference to a DateTime object. Any such reference, whether it is stored in a for some temporary
variable or returned by a query, can be used to call methods in the DateTime class, variables.
including isBefore. This query returns a Boolean value, so no further method calls
can be chained to the end of this expression.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 403
403
In this section, we’ve seen how to implement a class, Person, that collaborates with
another class, DateTime. This particular relationship is sometimes called the “has-a”
relationship because a person has a birth date and a death date. This relationship is
also called composition.
We have also seen that references to objects such as the birth date and death date can
be used much like integers and other primitive types. They can be used as instance vari-
ables, temporary variables, and parameter variables, and can be returned by queries.
Consider again the program to calculate Luke’s age in days, which appeared in Listing 8-2
and is reproduced in Listing 8-5. We’ll focus on two variables, lukesBD and daysOld. We
know that a variable stores a value; this was one of the basic concepts introduced in
Chapter 6, where variables were described as being like a box that has a name. Inside the
box is a value, such as 5009, that can be retrieved by giving the name of the variable.
1 importƒbecker.util.DateTime;
2
3 publicƒclassƒMainƒextendsƒObject
4 {ƒpublicƒstaticƒvoidƒmain(String[]ƒargs)ƒ
5 ƒƒ{
6 ƒƒƒƒDateTimeƒlukesBDƒ=ƒnewƒDateTime(1990,ƒ10,ƒ1);
7 ƒƒƒƒDateTimeƒtodayƒ=ƒnewƒDateTime();
8
9 ƒƒƒƒintƒdaysOldƒ=ƒlukesBD.daysUntil(today);
10 ƒƒƒƒSystem.out.println("Luke is "ƒ+ƒdaysOldƒ+ƒ" days old.");
11 ƒƒ}
12 }
At this point you might imagine daysOld and lukesBD as something like the illustra-
tions in Figure 8-5. The “box” for daysOld holds the value 5009 and the “box” for
lukesBD holds an object, represented with an object diagram.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 404
404
CHAPTER 8 | COLLABORATIVE CLASSES
This is an accurate enough description for daysOld, but not for lukesBD. lukesBD is
a reference variable, a variable that refers to an object rather than actually holding the
object. To understand what this means, we need to better understand the computer’s
memory.
8.2.1 Memory
Every computer has memory, where it stores information. This information includes
values stored in variables such as daysOld and lukesBD, objects, text, images, and
audio clips. Even the programs themselves constitute information stored in the com-
puter’s memory.
Memory is composed of many storage locations; these are the “boxes” we’ve described
that hold the information. Each location has its own address, numbered consecutively
beginning with 0. The address is how the computer program identifies which memory
location it should access. Each variable name in the program is associated by the Java
compiler with a specific memory address, as shown in Figure 8-6a. It shows the vari-
able daysOld associated with the memory address 5104. The current value of
daysOld, 5009, is in that location. Notice that every location has a value, even if it’s 0.
The point of this discussion is that objects are handled differently from primitive types,
such as integers. The variable lukesBD, for example, is associated with an address,
and its value is stored in a memory location just like daysOld. However, that memory
location does not store the object itself but the address of the object; that is, it refers to
the object, as shown in Figure 8-6b. Notice that the object takes up several memory
locations—one for each of the three instance variables.1
1 We are glossing over the fact that one location is only big enough to store a value between –128 and
127. A larger number, such as occupied by an int or an address, requires four locations. Every int
requires four locations, even if the actual value is between –128 and 127.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 405
405
Why not store the object at the address associated with lukesBD, as illustrated in
Figure 8-5? Why do we store the address of the object in lukesBD instead? The answer
involves efficiency—making the program run faster. If you need to pass an object as an
argument, for example, it is faster to pass a reference than to pass the entire object. A
reference is always the same size and does not occupy very much memory. Objects, on
the other hand, vary in length and can occupy a large amount of memory.
Fortunately, we can usually ignore addresses and memory locations, and let the com-
puter manage them. We only need to keep in mind that reference variables refer to an
object instead of hold the object directly. A simplified diagram, as shown in Figure 8-7
will be sufficient to do this.
References are often held in an object, as with the birth and death dates in a Person
object. In these cases, we can diagram the objects as shown in Figure 8-8.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 406
406
CHAPTER 8 | COLLABORATIVE CLASSES
8.2.2 Aliases
One way that reference variables are different from primitive variables is that it is pos-
sible to have several variables refer to the same object. For example, consider the fol-
lowing statements:
We can use either reference variable to invoke the object’s methods, as in the following
statements:
lukesBD.addYear(1);
annasBD.addYear(2);
Executing these statements changes the date for this object from 1990 to 1993.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 407
407
The question is, why would you want two variables that refer to the same object? The
example involving Luke’s and Anna’s birthdays is clear but rarely used. A closely
related example, however, occurs frequently. That is when a reference variable is
passed as an argument to a method. Consider the following method:
publicƒvoidƒadjustDate(DateTimeƒd)
{ƒd.addYear(2);
}
DateTimeƒlukesBDƒ=ƒnewƒDateTime(1990,ƒ10,ƒ1);
lukesBD.addYear(1);
this.adjustDate(lukesBD);
While the method adjustDate is executing, both lukesBD and the parameter vari-
able d refer to the same object. When adjustDate is called, the value in the argument,
lukesBD, is copied into the parameter variable, d. Once again, two variables contain
the address of the same object. Either one can be used to invoke the object’s methods,
and the net result of this three-line fragment is that the object’s year, 1990, is changed
to 1993.
Aliases can lead to dangerous situations. Consider the following code, where joseph
and esther are both instances of Person. They died eight years apart.
1 DateTimeƒdeathƒ=ƒnewƒDateTime(1974,ƒ1,ƒ11);
2 esther.setDeathDate(death);
3 death.addYears(8);
4 joseph.setDeathDate(death);
KEY IDEA Here, the programmer avoids constructing a new DateTime object. What is the effect
Aliases can be used to of this code? Because both esther and joseph refer to the same DateTime object,
change objects one of their death dates will be wrong. In lines 1 and 2, esther’s death date is set cor-
unintentionally or rectly. However, when death is changed in line 3, esther’s death date inadvertently
maliciously.
changes as well because they both refer to the same object. Finally, the date is set for
joseph, resulting in the situation shown in Figure 8-10—a single DateTime object
that has three references to it and is shared by both esther and joseph.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 408
408
CHAPTER 8 | COLLABORATIVE CLASSES
(figure 8-10)
esther Person
Two Person objects
name: “Esther Unruh”
inadvertently sharing the
birth:
death same DateTime object
death: DateTime
year: 1982
joseph month: 1
day: 11
Person
name: “Joseph Becker”
birth:
death:
A similar danger can result from an accessor method that returns a reference. The
getBirthDate method (Section 8.1.6) returns a reference to the relevant DateTime
object. Once the client has that reference, it could use it to reset the birth date—per-
haps to a year that has not yet occurred.
DateTimeƒbirthƒ=ƒjoseph.getBirthDate();
birth.addYears(291);
A two-line example makes the error obvious, but such an error can also be separated
by many lines of code and be much more difficult to identify.
There are measures you can take to protect your code from aliasing errors. First, you LOOKING BACK
could verify that the referenced object is immutable, meaning it has no methods to Immutable classes
change its state. If the state can’t change, it doesn’t matter if the object is shared. were discussed in
Section 7.3.3.
Unfortunately, DateTime is not immutable, so this approach won’t work here.
String, a commonly used class, is immutable.
Second, the methods could avoid accepting or returning references in the first place. LOOKING AHEAD
The first version of setDeathDate, which takes integer values for the year, month, Listing 11-4 shows
and day, avoids this problem. Instead of having getBirthDate return a reference, how to use
determine why the client wants the reference. For example, if the purpose is to change DateTime to
make an immutable
the birth date, provide an updateBirthDate method that performs integrity checks Date class.
to ensure the new date is reasonable.
A third approach, and probably the most common, is to hope that the object’s clients
won’t cause problems with the references. This is good enough in many situations, par-
ticularly if the program is well tested. However, in safety-critical applications or an
application that may be the target of fraud, this approach is not sufficient.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 409
409
publicƒvoidƒsetDeathDate(DateTimeƒdeathDate)
{ƒDateTimeƒcopyƒ=ƒnewƒDateTime(deathDate.getYear(),
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒdeathDate.getMonth(),ƒdeathDate.getDay());
ƒƒthis.deathƒ=ƒcopy;
}
publicƒDateTimeƒgetBirthDate()
{ƒDateTimeƒcopyƒ=ƒnewƒDateTime(this.birth);
ƒƒreturnƒcopy;
}
Not only can an object have several variables referencing it, but it might have none.
Consider the following situation, illustrated in Figure 8-11. An object is created, but
then its reference is assigned a new value. The result is that the first object is garbage;
there is no way to access the object because there are no references to it.
DateTimeƒlukesBDƒ=ƒnewƒDateTime(1990,ƒ10,ƒ1);
...
lukesBDƒ=ƒnewƒDateTime(1994,ƒ1,ƒ28);
As in the rest of life, garbage is undesirable. It consumes computer memory but cannot
affect the running of the program because there is no way to access it. To address this
situation, the Java system periodically performs garbage collection. It scans the com-
puter’s memory for unreferenced objects, enabling the memory they consume to be
reused again when new objects are allocated. Because the memory can be reused,
“memory recycling” might be a better name than “garbage collection.”
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 410
410
CHAPTER 8 | COLLABORATIVE CLASSES
Testing two objects for equality is a bit tricky. Suppose you have the situation shown in
Figure 8-12a.
If you want to check whether Anna and Luke were born on the same day, you might
write the following statement:
ifƒ(annasBDƒ==ƒlukesBD)
{ƒƒ// what to do if they have the same birthday
This is, after all, what you would write to compare two integer variables. For example,
if annasAge and lukesAge are two integer variables containing the ages of Anna and
Luke, then the following code tests whether both variables contain the same value.
ifƒ(annasAgeƒ==ƒlukesAge)
{ƒƒ// what to do if they are the same age
If they both contain 18, for example, the == operator returns true.
The statement ifƒ(annasBDƒ==ƒlukesBD) also tests whether both variables contain KEY IDEA
the same value. In this case, however, the values being compared are object references, Comparing object
not the objects themselves. In other words, the test will be true if annasBD and references with ==
lukesBD both contain the same address in memory and thus refer to exactly the same returns true if they
refer to exactly the
object. A situation where this is true is shown in Figure 8-12b.
same object.
Sometimes this behavior is exactly what is needed. For example, in Chapter 10, we will
search lists of objects. We may want to know if a specific object is in the list or not, and
a test containing == is the tool to use. This approach to equality is called object identity.
In the case of comparing birth dates, what we really need is object equality, or equivalence.
We want to compare two date objects and determine if they have the same meaning. In the
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 411
411
Testing for equivalence is done with a method such as the following in the
DateTime class:
publicƒbooleanƒisEquivalent(DateTimeƒother)
{ƒreturnƒotherƒ!=ƒnullƒ&&ƒƒƒ// Make sure other actually refers to an object!
ƒƒƒƒƒƒƒƒƒthis.yearƒ==ƒother.getYear()ƒ&&ƒ
Equivalence Test
ƒƒƒƒƒƒƒƒƒthis.monthƒ==ƒother.getMonth()ƒ&&
ƒƒƒƒƒƒƒƒƒthis.dayƒ==ƒother.getDay();
}
The test for null protects against a NullPointerException occurring later in the
method.
After the test for null comes a series of tests to ensure that all the relevant fields in the
two objects are equivalent. If the relevant fields are primitive types, as shown here, use
== for the test. If they are reference fields, use either an isEquivalent method that
you’ve written or equals for provided classes.
This method could be used to test whether annasBD and lukesBD refer to objects with
equivalent dates by writing one of the following statements:
ifƒ(annasBD.isEquivalent(lukesBD))ƒ...
or
ifƒ(lukesBD.isEquivalent(annasBD))ƒ...
KEY IDEA This version of isEquivalent is more verbose than necessary. So far we have only
Code in a given class accessed private instance variables using this. However, Java allows us to access the
can use the private private members of any object belonging to the same class. That is, inside the
instance variables of
DateTime class, we can also access the instance variables for other—the DateTime
any instance of
that class. object passed as an argument. Using this fact, the method can be rewritten as follows:
publicƒbooleanƒisEquivalent(DateTimeƒother)
{ƒreturnƒotherƒ!=ƒnullƒ&&ƒƒƒ// make sure other actually refers to an object!
ƒƒƒƒƒƒƒƒƒthis.yearƒ==ƒother.yearƒ&&ƒ
ƒƒƒƒƒƒƒƒƒthis.monthƒ==ƒother.monthƒ&&
ƒƒƒƒƒƒƒƒƒthis.dayƒ==ƒother.day;
}
Overriding equals
LOOKING AHEAD The Object class has a method named equals that is meant to test for equivalence.
equals is discussed Most classes should provide a method named equals that overrides the one in
again in Object. Unfortunately, technicalities in doing so are difficult to explain without
Section 12.4.2.
knowing about polymorphism, the topic of Chapter 12.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 412
412
CHAPTER 8 | COLLABORATIVE CLASSES
1. Read the description of what the program is supposed to do, highlighting the nouns (figure 8-13)
and noun phrases. These are the objects your program must declare. If there are any Object-based design
objects that cannot be directly represented using existing types, define classes to
methodology
represent such objects.
2. Highlight the verbs and verb phrases in the description. These are the services. If a
service is not predefined:
a. Define a method to perform the service.
b. Place it in the class responsible for providing the service.
3. Apply the services from Step 2 to the objects from Step 1 in a way that solves
the problem.
Program design is as much art as science. The methodology leaves room for interpreta-
tion, and programming experience helps with recognizing and implementing common
design patterns. Nevertheless, these basic steps have proven helpful to object-oriented
programmers of all experience levels and on all sizes of projects. In fact, the larger the
project, the more help these steps are in getting started.
The opening paragraph of Section 8.3 is our description of what the program is sup-
posed to do.
The first step in the methodology is to use nouns and noun phrases to identify the rel-
evant classes to solve the problem. A noun is a person, a place, a thing, or an idea. The
most important nouns in the description are alarm clock, alarm, and time. Other
nouns include program, message, console, and sound.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 413
413
Class Relationships
KEY IDEA Sometimes the less important nouns go with another noun. For example, message and
Sometimes a noun sound go with Alarm (“when an alarm is due, it will print a message…and play a
represents an sound”). They will appear as instance variables in the Alarm class. The “has-a” test
attribute, not a class. from Section 8.1.3 also applies here: “An Alarm has-a message to display” and “an
Alarm has-a sound to play.”
The noun time applies in two ways. First, time is linked to Alarm in the statement
“rings an alarm…when it’s time,” and “when one of the alarms is due” implies time.
The “has-a” test makes sense, too: “An Alarm has-a time when it rings.”
KEY IDEA Second, time is used by the AlarmClock class to keep track of the current time. The
A solid arrow in a instance of DateTime will be a temporary variable, not an instance variable.
class diagram
indicates an instance In addition, the alarm clock has up to four alarms. Again, the appearance of the word
variable. A dotted line has indicates the presence of instance variables in the AlarmClock class.
indicates a temporary
variable or parameter. Putting these observations together results in the classes, attributes, and class relation-
ships shown in Figure 8-14. The class diagram also includes a class holding the main
method where execution begins.
AlarmClockMain
2 See http://java.sun.com/docs/books/tutorial/sound/index.html
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 414
414
CHAPTER 8 | COLLABORATIVE CLASSES
Step 2 in the object-based design methodology is to identify the services required in the KEY IDEA
classes by analyzing the verbs and verb phrases. Verbs are action words such as ring, run, Verbs in the
set, and print. Verbs are used in the program description as “ring an alarm,” “remind you specification often
when,” “set the alarm,” “run (the program),” “print a message,” and “play a sound.” identify services in
the program’s
Some of these verbs are different descriptions of the same thing. For example, an alarm classes.
That still leaves setting the alarms, which sounds like it might be a service of the
AlarmClock class, and running the program. This phrase is often a generic way of
saying we should execute the program. In this case, however, we actually need a
method that keeps the time for the clock. We’ll name it run.
These services and the classes to which they are assigned are also shown in Figure 8-14.
Now let’s turn to implementing these methods, beginning with the Alarm class. We KEY IDEA
will defer the sound until later; our first version will “ring” the alarm by only printing Defer nonessential
a message. features until after the
core features are
The constructor is passed the hour and minute that the alarm should ring and the mes- working.
sage that should print. We’ll use a DateTime object internally to represent the time the
alarm should ring. The time and message must be remembered until they are needed by
the ring method and are therefore saved in instance variables.
publicƒclassƒAlarmƒextendsƒObject
{
ƒƒprivateƒDateTimeƒwhen;
ƒƒprivateƒbooleanƒhasRungƒ=ƒfalse; Has-a (Composition)
ƒƒprivateƒStringƒmsgƒ=ƒ"";
415
KEY IDEA The AlarmClock class has three fundamental things to do: keep the current time, ring
Asking and answering the alarms at the correct times, and provide a way to set the alarms. We’ll start with the
questions is a useful run method, which keeps the current time. It will also call a helper method to ring the
technique, even if you
alarms, if appropriate. This method is not trivial, so we’ll return to the expert-and-
are programming by
yourself. novice format of earlier chapters.
Novice Keep track of the current time. And if it’s time for one of the alarms to ring, it
needs to ring it.
Novice Not really. As time passes, it will need to check again and again whether it is
time to ring an alarm.
Novice It needs to figure out the current time and check if the alarms should be rung.
Expert What’s the negation of that condition? That tells us whether the loop should
continue.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 416
416
CHAPTER 8 | COLLABORATIVE CLASSES
Novice Hey. It sounds like you’re leading me through the four-step process for build- LOOKING BACK
ing a loop. Step 1 is to identify the actions that must be repeated to solve the The four-step process
problem; Step 2 is to identify the test that must be true when the loop stops for constructing a
loop is discussed in
and to negate it; Step 3 is to assemble the loop; and Step 4 is determining what
Section 5.1.2.
comes before or after the loop to complete the solution.
Expert You’re absolutely right. Now, what about negating the test in Step 2?
Novice The loop continues as long as there is at least one alarm left to ring.
As for Step 3, I’d like to start with pseudocode. It’s easier than thinking in KEY IDEA
Java right away. Something like this, perhaps? Pseudocode helps
you think about the
whileƒ(number of alarms left > 0) algorithm without
{ƒget the current time distracting Java
ƒƒcheckƒalarmsƒandƒringƒifƒit’sƒtheƒrightƒtime details.
}
Expert Excellent. The fourth step was to think through what needs to come before or
after the loop. What do you think?
Novice I don’t think we need to do anything after the loop. Before the loop, we’ll need
to initialize some variables or something to control the loop.
Expert Yes. We could use an instance variable to count the number of alarms that have
not been rung. When we set an alarm, we’ll increment the counter; when we
ring an alarm, we’ll decrement it. Given that, can you code a solution in Java?
Novice I think so. I’m going to assume a helper method to check and ring the alarms KEY IDEA
for me. That will keep this method simpler. Keep methods short.
Use helper methods
publicƒvoidƒrun() to reduce complexity.
{ƒDateTimeƒcurrTimeƒ=ƒnewƒDateTime();
ƒƒwhileƒ(this.numAlarmsLeftƒ>ƒ0)
ƒƒ{ƒcurrTimeƒ=ƒnewƒDateTime();
ƒƒƒƒthis.checkAndRingAlarms(currTime);
ƒƒ}
}
Novice Pretty good. With the help of the four-step process for building loops and the
pseudocode, I’m pretty confident run will do what it is supposed to do.
Expert I agree. I do have one suggestion, however. Let’s insert a call to the sleep
method inside the loop. Your loop probably runs thousands of times per sec-
ond. We could slow it down with a sleep command, giving the computer
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 417
417
KEY IDEA Novice Great idea. One thing is bothering me, though. Testing this method is going to
Think about testing be really hard because it runs in real time. If we set an alarm for 3:30 in the
from the beginning. afternoon and it’s only 10 in the morning now, we’ll have to wait 51⁄2 hours to
see if the program works!
Expert That is a problem. Normally we want to test the same code that makes up the
finished solution. Here, however, we may need to make a slight change to
make testing easier.
If we’re not testing, we’ll continue to calculate the current time as you sug-
gested earlier. Creating a new instance of DateTime will keep the time accu-
rate because that constructor actually uses the computer’s clock.
Novice If we use a parameter, the method calling run can decide how fast the time
should pass. Then our new method would look like this:
publicƒvoidƒrun(intƒsecPerSec)
{ƒDateTimeƒcurrTimeƒ=ƒnewƒDateTime();
ƒƒwhileƒ(this.numAlarmsLeftƒ>ƒ0)
ƒƒ{ƒifƒ(this.TESTING)
ƒƒƒƒ{ƒcurrTime.addSeconds(secPerSec);
ƒƒƒƒ}ƒelse
ƒƒƒƒ{ƒcurrTimeƒ=ƒnewƒDateTime();
ƒƒƒƒ}
ƒƒƒƒthis.checkAndRingAlarms(currTime);
ƒƒƒƒUtilities.sleep(1000);ƒ// sleep one second real time
ƒƒ}
}
Expert Good. Now, what does your helper method, checkAndRingAlarms, need to do?
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 418
418
CHAPTER 8 | COLLABORATIVE CLASSES
Novice It will check each alarm’s time against the current time. If it’s time for the
alarm to ring, it will call its ring method. Or, in pseudocode (because I know
you’re going to ask):
We’ll need a couple of more tests for the other alarms. I’m assuming the
alarms are stored in four instance variables. Seems pretty simple to me.
Expert Actually, I think I see two problems. The first problem is when there is no
alarm set. How can you check whether its time matches? If you tried, I think
you would get a NullPointerException.
The second problem is that you are assuming that only one alarm becomes LOOKING BACK
due at any given time. Remember the Cascading-if pattern? It says that only The Cascading-if
one of the groups of statements will be executed. If two alarms happen to be pattern was
set for the same time, only the first will ring. discussed in
Section 5.3.3.
Novice So we could have four separate groups of statements, each one like this:
Expert Can you improve this? Is the nested if statement really necessary? Do you
really need to repeat almost the same code four times?
Novice Aha. We can use short-circuit evaluation. If the first part of the “and” is LOOKING BACK
false, Java won’t even bother to check the second part. And we can put the Short-circuit
whole thing in a method to avoid the code duplication. Like this: evaluation was
discussed in
privateƒvoidƒcheckOneAlarm(Alarmƒalarm,ƒDateTimeƒcurrTime) Section 5.4.3.
{ƒifƒ(alarm !ƒ=ƒnullƒ&&ƒalarm.isTimeToRing(currTime))
ƒƒ{ƒalarm.ring();
ƒƒƒƒthis.numAlarmsLeftƒ-=ƒ1;
ƒƒ}
}
Expert Good. I see you’ll need to add a method, isTimeToRing, to the Alarm class. KEY IDEA
I like the way you’re asking that class to figure out the answer for you. It’s the Put methods in the
one with the needed data. Asking Alarm for the answer seems better than ask- same class as the
data they use.
ing it for its time and then doing the computation yourself.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 419
419
privateƒvoidƒcheckAndRingAlarms(DateTimeƒcurrTime)
{ƒthis.checkOneAlarm(this.alarm1,ƒcurrTime);
ƒƒthis.checkOneAlarm(this.alarm2,ƒcurrTime);
ƒƒthis.checkOneAlarm(this.alarm3,ƒcurrTime);
ƒƒthis.checkOneAlarm(this.alarm4,ƒcurrTime);
}
Novice We already know we’ll have four instance variables. I think we need to just
check each one in turn to see if it’s null. If it is, we can save the alarm in that
variable. A cascading-if should work. Of course, we also need to construct
the Alarm itself.
publicƒvoidƒsetAlarm(intƒhr,ƒintƒmin,ƒStringƒmsg)
{ƒAlarmƒtheAlarmƒ=ƒnewƒAlarm(hr,ƒmin,ƒmsg);
ƒƒifƒ(this.alarm1ƒ==ƒnull)
ƒƒ{ƒthis.alarm1ƒ=ƒtheAlarm;
ƒƒ}ƒelseƒifƒ(this.alarm2ƒ==ƒnull)
ƒƒ{ƒthis.alarm2ƒ=ƒtheAlarm;
ƒƒ}ƒelseƒifƒ(this.alarm3ƒ==ƒnull)
ƒƒ{ƒthis.alarm3ƒ=ƒtheAlarm;
ƒƒ}ƒelseƒifƒ(this.alarm4ƒ==ƒnull)
ƒƒ{ƒthis.alarm4ƒ=ƒtheAlarm;
ƒƒ}ƒ
}
Expert Looks good. But aren’t you forgetting something? We made an assumption
earlier that we had a count of the number of alarms yet to ring. This seems
like the place to include it.
this.numAlarmsLeft++;
Expert One more detail to consider for setAlarm. What happens if we try to set five
alarms?
Novice Right now, absolutely nothing happens. The cascading-if statement doesn’t
have any tests that match and there is no else clause. I think the user should
know about the error, so I’ll add a warning in an else clause, as follows:
ƒƒ...
ƒƒ}ƒelseƒifƒ(this.alarm4ƒ==ƒnull)
ƒƒ{ƒthis.alarm4ƒ=ƒtheAlarm;
ƒƒ}ƒelse
ƒƒ{ƒSystem.out.println("Too many alarms.");
ƒƒ}
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 420
420
CHAPTER 8 | COLLABORATIVE CLASSES
Expert This is a fine solution for now, but throwing an exception would be better. I’m
sure you’ll learn how soon.
All these ideas come together in Listing 8-6 and Listing 8-7.
The isTimeToRing method in the Alarm class is mentioned in the dialogue but not
discussed thoroughly. In this application, we dare not compare two times for equality to
see if the alarm should ring because it’s possible that the time might be skipped over—
particularly given the time acceleration that we built into the run method. Instead, we
need to check if the time for the alarm has passed and the alarm has not yet been rung.
This requires an extra instance variable at line 10 in the Alarm class that is checked in
the isTimeToRing method (line 28) and changed in the ring method (line 37).
421
30
31 ƒƒ/** Alert the user. */
32 ƒƒpublicƒvoidƒring()
33 ƒƒ{ƒthis.when.setFormatInclude(DateTime.TIME_ONLY);
34 ƒƒƒƒthis.when.setFormatLength(DateTime.SHORT);
35 ƒƒƒƒStringƒtimeƒ=ƒthis.when.format();
36 ƒƒƒƒSystem.out.println(timeƒ+ƒ": "ƒ+ƒthis.msg);
37 ƒƒƒƒthis.hasRungƒ=ƒtrue;
38 ƒƒ}
39 }
422
CHAPTER 8 | COLLABORATIVE CLASSES
29 ƒƒ* Each second of real time advances this clock the given number of seconds. With
30 ƒƒ* a value of 3600 one "day" takes about 24 seconds of elapsed time. */
31 ƒƒpublicƒvoidƒrun(intƒsecPerSec)
32 ƒƒ{ƒDateTimeƒcurrTimeƒ=ƒnewƒDateTime();
33
34 ƒƒƒƒwhileƒ(this.numAlarmsLeftƒ>ƒ0)
35 ƒƒƒƒ{ƒifƒ(this.TESTING)
36 ƒƒƒƒƒƒ{ƒcurrTime.addSeconds(secPerSec);
37 ƒƒƒƒƒƒ}ƒelse
38 ƒƒƒƒƒƒ{ƒcurrTimeƒ=ƒnewƒDateTime();
39 ƒƒƒƒƒƒ}
40
41 ƒƒƒƒƒƒthis.checkAndRingAlarms(currTime);
42 ƒƒƒƒƒƒUtilities.sleep(1000);ƒ// sleep one second real time
43 ƒƒƒƒ}
44 ƒƒ}
45
46 ƒƒ// Check each alarm. Ring it if it's time.
47 ƒƒprivateƒvoidƒcheckAndRingAlarms(DateTimeƒcurrTime)
48 ƒƒ{ƒthis.checkOneAlarm(this.alarm1,ƒcurrTime);
49 ƒƒƒƒthis.checkOneAlarm(this.alarm2,ƒcurrTime);
50 ƒƒƒƒthis.checkOneAlarm(this.alarm3,ƒcurrTime);
51 ƒƒƒƒthis.checkOneAlarm(this.alarm4,ƒcurrTime);
52 ƒƒ}
53
54 ƒƒ// Check one alarm. Ring it if it's time.
55 ƒƒprivateƒvoidƒcheckOneAlarm(Alarmƒalarm,ƒDateTimeƒcurrTime)
56 ƒƒ{ƒifƒ(alarmƒ!=ƒnullƒ&&ƒalarm.isTimeToRing(currTime))
57 ƒƒƒƒ{ƒalarm.ring();
58 ƒƒƒƒƒƒthis.numAlarmsLeft1-=1;
59 ƒƒƒƒ}
60 ƒƒ}
61
62 ƒƒ/** Set an alarm to ring at the given time today. A maximum of four alarms may be set.
63 ƒƒ* @param hr The hour the alarm should ring.
64 ƒƒ* @param min The minute of the hour the alarm should ring.
65 ƒƒ* @param msg Why the alarm is being set */
66 ƒƒpublicƒvoidƒsetAlarm(intƒhr,ƒintƒmin,ƒStringƒmsg)
67 ƒƒ{ƒAlarmƒtheAlarmƒ=ƒnewƒAlarm(hr,ƒmin,ƒmsg);
68 ƒƒƒƒifƒ(this.alarm1ƒ==ƒnull)
69 ƒƒƒƒ{ƒthis.alarm1ƒ=ƒtheAlarm;
70 ƒƒƒƒ}ƒelseƒifƒ(this.alarm2ƒ==ƒnull)
71 ƒƒƒƒ{ƒthis.alarm2ƒ=ƒtheAlarm;
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 423
423
72 ƒƒƒƒ}ƒelseƒifƒ(this.alarm3ƒ==ƒnull)
73 ƒƒƒƒ{ƒthis.alarm3ƒ=ƒtheAlarm;
74 ƒƒƒƒ}ƒelseƒifƒ(this.alarm4ƒ==ƒnull)
75 ƒƒƒƒ{ƒthis.alarm4ƒ=ƒtheAlarm;
76 ƒƒƒƒ}ƒelse
77 ƒƒƒƒ{ƒSystem.out.println("Too many alarms.");
78 ƒƒƒƒ}
79
80 ƒƒƒƒthis.numAlarmsLeft++;
81 ƒƒ}
82
83 ƒƒ// For testing
84 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
85 ƒƒ{ƒAlarmClockƒclockƒ=ƒnewƒAlarmClock(true);
86
87 ƒƒƒƒclock.setAlarm(10,ƒ30,ƒ"Coffee break");
88 ƒƒƒƒclock.setAlarm(11,ƒ00,ƒ"Call Amy");
89 ƒƒƒƒclock.setAlarm(17,ƒ30,ƒ"Turn off the computer and get a life!");
90
91 ƒƒƒƒclock.run(3600);ƒ
92 ƒƒ}
93 }
The hard part is over. The last step in the methodology is to solve the problem using
the methods we created for the various classes. For the alarm clock problem, we can
use a main method that constructs an AlarmClock object, sets alarms, and then calls
the run method. A sample is shown in Listing 8-8.
424
CHAPTER 8 | COLLABORATIVE CLASSES
Listing 8-8: A main method to run the alarm clock program (continued)
9 ƒƒ{ƒAlarmClockƒclockƒ=ƒnewƒAlarmClock(false);
10
11 ƒƒƒƒclock.setAlarm(10,ƒ30,ƒ"Coffee break");
12 ƒƒƒƒclock.setAlarm(11,ƒ00,ƒ"Call Amy");
13 ƒƒƒƒclock.setAlarm(17,ƒ30,ƒ"Turn off the computer and get a life!");
14
15 ƒƒƒƒclock.run(1);
16 ƒƒ}
17 }
There are various subclasses of Exception that are more specific about the excep-
tional circumstance. For example, adding a fifth alarm when our alarm clock can only
handle four is attempting to put the object into an illegal state. In such a circumstance,
the IllegalStateException is applicable.
The original setAlarm method used a cascading-if statement that concluded with the
following code:
74 ƒƒƒƒ}ƒelseƒifƒ(this.alarm4ƒ==ƒnull)
75 ƒƒƒƒ{ƒthis.alarm4ƒ=ƒtheAlarm;
76 ƒƒƒƒ}ƒelse
77 ƒƒƒƒ{ƒSystem.out.println("Too many alarms.");
78 ƒƒƒƒ}
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 425
425
The constructor’s argument is a string describing in more detail what caused the prob-
lem. The result of throwing this exception is shown in Figure 8-15.
(figure 8-15)
Exception message
printed after attempting to
add a fifth alarm
ifƒ(hrƒ<ƒ0ƒ||ƒhrƒ>ƒ23)
{ƒthrowƒnewƒIllegalArgumentException(
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ"Hour = "ƒ+ƒhrƒ+ƒƒ"; should be 0–23, inclusive.");
}
These checks are especially important in constructors where the arguments are often
used to initialize instance variables.
The information printed when an exception is thrown is very useful for debugging. For
example, one run of the alarm clock program produced the exception message shown
in Figure 8-16.
(figure 8-16)
426
CHAPTER 8 | COLLABORATIVE CLASSES
The nine lines following it, each beginning with “at,” make up a stack trace. A stack
trace follows the execution from the exception back to main, listing all of the methods
that have not yet completed executing. Each line has the following form:
atƒ«packageName».«className».«methodName»(«fileName»:«line»)
The alarm clock program’s classes are not in a package, so that part is blank for the last
three lines.
The last line of the stack trace tells us that the main method in the AlarmClock class
called a method at line 87. The method it called is shown on the line above it,
setAlarm. If we look at line 87 in Listing 8-7, we can verify that main calls the
setAlarm method.
The second-to-last line of the stack trace tells us that setAlarm called a method at line 67
in AlarmClock.java. The third-to-last line tells us that method was Alarm.<init>.
This refers to the initialization that occurs when an instance of Alarm is constructed,
including the initialization of instance variables. In this case, it occurred at line 20 in
Alarm.java. That line calls the setTime method in the DateTime class. The rest of the
method calls shown in the stack trace are for code in libraries we used.
It’s usually most fruitful to debug our code beginning with the line closest to the
exception—that is, Alarm.java at line 20. It reads as follows:
20 ƒƒƒƒthis.when.setTime(min,ƒhr,ƒ0);
Java has two types of exception—checked and unchecked. Checked exceptions are excep-
tions from which the program may be able to recover; in addition, programmers are
required to include code to check for them. Unchecked exceptions should be thrown only
when they result from a program bug. Programmers are not required to check for them.
IllegalArgumentException and IllegalStateException are two examples of
unchecked exceptions. Unchecked exceptions include Error, RuntimeException, and
their subclasses. All other exceptions are checked.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 427
427
(figure 8-17)
In a Java program, such an error would likely be discovered when it constructs a URL
object. The URL constructor takes a string, such as the one typed by the user in the previ-
ous figure. If an error is found, the URL constructor throws a MalformedURLException.
This fact is included in the online documentation.
Programmers can check for an exception and handle it with code derived from the fol-
lowing template:
try
{ƒ«statementsƒthatƒmayƒthrowƒanƒexception»
}ƒcatchƒ(«ExceptionType1»ƒ«name1»)
{ƒ«statementsƒtoƒhandleƒexceptionsƒofƒtypeƒExceptionType1»
}ƒcatchƒ(«ExceptionType2»ƒ«name2»)
{ƒ«statementsƒtoƒhandleƒexceptionsƒofƒtypeƒExceptionType2»
...
}ƒcatchƒ(«ExceptionTypeN»ƒ«nameN»)
{ƒ«statementsƒtoƒhandleƒexceptionsƒofƒtypeƒExceptionTypeN»
}ƒfinally
{ƒ«statements that are always executed»
}
The try block contains the statements that the program must try to execute and that may
throw an exception. There is a catch block for each exception to handle. The catch
blocks are formatted and executed similar to a cascading-if statement. When an excep-
tion is thrown, Java starts with the first catch block and works its way downward. It
executes the statements in the first catch block where «ExceptionType» matches the
exception thrown or is a superclass of the thrown exception.
For example, the mixture of pseudocode and Java in Listing 8-9 shows how to handle
the MalformedURLException thrown by the URL constructor.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 428
428
CHAPTER 8 | COLLABORATIVE CLASSES
Listing 8-9: A mixture of pseudocode and Java showing how an exception can be caught
1 privateƒvoidƒloadPage()
2 {ƒStringƒurlStringƒ=ƒget the url typed by the user
3 ƒƒtry
4 ƒƒ{ƒURLƒurlƒ=ƒnewƒURL(urlString); // can throw MalformedURLException
5 ƒƒƒƒuseƒurlƒto load the page // can throw IOException
6 ƒƒ}ƒcatchƒ(MalformedURLExceptionƒex)
7 ƒƒ{ display a dialog box describing the error and asking the user to try again
8 ƒƒ}ƒcatchƒ(IOExceptionƒex)
9 ƒƒ{ display a dialog box describing the error
10 ƒƒ}
11 }
If the URL constructor in this example throws an exception, the statements following it KEY IDEA
in the try block (using the URL) are not executed. When an exception is thrown, exe- An exception skips
cution resumes with the nearest catch block. over code between
the line throwing it
Because malformedURLexception extends IOException, the order of the catch and a matching
catch statement.
clauses is important. If IOException is listed first, it will handle both kinds of excep-
tions. When listing multiple catch clauses, always list the subclasses (most specific
exceptions) first and the superclasses (most general exceptions) last.
The names in the catch’s parentheses are much like a parameter variable declaration. In
the previous example, ex is a variable that can be used within the catch clause. Recall
that an exception is an object, and ex can be used to access its methods. For example, the
getMessage method returns the string that was passed to the exception’s constructor.
The printStackTrace method prints the stack trace. It is often followed with the state-
ment System.exit(1), which causes the program to terminate immediately. Without
the call to exit, the program would resume after the try-catch statement.
The finally clause shown in the template is optional. If included, the code it contains KEY IDEA
will always be executed if any of the code in the try block is executed. The finally Code in the finally
clause is executed even if an exception is thrown, whether or not it is handled in a clause is always
executed if code in
catch clause. It’s also executed if a return, break, or continue statement is exe-
the try block has
cuted within the try block to end it early. executed.
Methods often can’t handle the exceptions thrown by the methods they call. They
could catch the exceptions, but can’t do anything constructive to respond to the error.
In these cases, the exceptions should be propogated up the call stack. This is exactly
what happened in Figure 8-16. The computeTime method threw an exception. It’s
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 429
429
When a checked exception is allowed to propogate like this, the method must declare
that fact with the throws keyword. For example, suppose the loadPage method in
Listing 8-9 is not an appropriate place to display a dialog box. The method can be
rewritten as follows:
privateƒvoidƒloadPage()
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒthrowsƒMalformedURLException,ƒIOException
{ƒStringƒurlStringƒ=ƒget the url typed by the user
ƒƒURLƒurlƒ=ƒnewƒURL(urlString); // can throw MalformedURLException
ƒƒuseƒurlƒto load the page // can throw IOException
}
The throws clause alerts everyone who might use this method that it can throw the
listed exceptions. The clause is required for checked exceptions. If it is omitted, the
compiler will issue an error message with the following format:
«className»:«lineNum»:ƒunreportedƒexceptionƒ«exceptionName»;
mustƒbeƒcaughtƒorƒdeclaredƒtoƒbeƒthrown
The reference to “must be caught” means to include the code in a try-catch state-
ment. The alternative, “declared to be thrown,” means to change the method signature
to include the keyword throws, as shown earlier.
We can use our new expertise with exceptions to add sound to the alarm clock pro-
gram. One way that Java works with sound is via the AudioClip class. An
AudioClip can be loaded from a file using the .wav, .au, or .midi formats (but not
.mp3, unfortunately). There may be appropriate sound files already on your computer,
or you can create your own with a program such as Audacity, a free sound editor
found at http://audacity.sourceforge.net/.
The location of the sound file is specified with a URL and can be either on the Web or
on your disk.
Listing 8-10 shows the additions to the Alarm class to accommodate sound. Four
changes are required:
➤ Lines 3 and 4
import the Applet, AudioClip, URL, and
MalformedURLException classes.
➤ Line 8 declares a class variable, sound. It’s a class variable so that all the
Alarm instances can share the same sound.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 430
430
CHAPTER 8 | COLLABORATIVE CLASSES
➤ Lines 13–24 load the sound from a location on the Web. A URL is required,
which may throw a MalformedURLException,1 and so a try-catch state-
ment is required. If the exception is thrown, lines 21–22 print a stack trace to
aid debugging and exit the program. Because the sound is shared among all
instances of Alarm, it only needs to be loaded once. The if statement at line
14 prevents it from loading more than once.
➤ Line 31 actually plays the sound. An AudioClip has three methods: play to
play a sound, stop to stop a sound currently playing, and loop to play a
sound repeatedly. Sounds play in their own thread. Line 31 starts that thread,
but then execution of the program continues while the sound plays.
The part of this code most likely to cause a problem is specifying the URL for the
sound file. If the form of the URL is correct but there is no sound file actually at that
location, nothing will notify you; the program just won’t play a sound. The best way to
avoid this problem is to first locate the file using a Web browser. Then cut and paste the
URL from the browser’s address bar to the program.
The sound file may also be loaded from your disk drive using a URL similar to the
following:
URLƒurlƒ=ƒnewƒURL("file:///D:/Robots/examples/ch08/alarmSound/ringin.wav");
431
22 ƒƒƒƒƒƒƒƒSystem.exit(1);
23 ƒƒƒƒƒƒ}
24 ƒƒƒƒ}
25 ƒƒ}
26
27 ƒƒpublicƒvoidƒring()
28 ƒƒ{ƒ// Same as Listing 8-6.
29
30 ƒƒƒƒ// Play the sound.
31 ƒƒƒƒAlarm.sound.play();
32 ƒƒ}
33 }
Four million alarms seems excessive, but other programs could easily have a collection
of 4 million or more objects. Consider an inventory program for a large chain of stores,
for example. When our collections of similar objects grow beyond four or five, we need
better techniques than we used in AlarmClock.
Fortunately, Java provides a set of classes for maintaining collections of objects. These
classes are used when objects in a collection need to be treated in a similar way: a col-
lection of Alarm objects that need to be checked and perhaps rung, a collection of
Student objects that need to be enrolled in a course, or a collection of Image objects
that need to display on a computer monitor. The objects maintained by these collec-
tions are usually called the elements of the collection.
432
CHAPTER 8 | COLLABORATIVE CLASSES
Collection objects cannot hold primitive types, only objects. We’ll discuss a way KEY IDEA
around that limitation in Section 8.5.4. Collections hold
objects, not
These collection classes are sophisticated, and covering all the details would require primitives.
several chapters. Therefore, we will focus on constructing the objects; adding and
removing elements, plus a few other useful methods; and processing all the elements
(for example, checking all the Alarm objects to see if one should be rung). We’ll look
at one example of each kind of collection. We’ll look at a list class first in some detail.
We will go faster when we examine sets and maps because much of what we learn with
lists will also apply to them.
The approach taken in this textbook assumes that you are using Java 5.0 or higher. KEY IDEA
Previous versions of Java have these classes, but they are more difficult to use without This section assumes
the advances made in Java 5.0 you are using Java 5.0
or higher.
A list is probably the most natural collection class to use for our AlarmClock pro-
gram. It can hold any kind of object (sets and maps have some restrictions) and allows
us to easily process all of the elements or to get just one.
There are two distinct ways to write a list class—ArrayList and LinkedList. Both
are in the java.util package, meaning that you’ll need to import from that package
if you want to use the classes. ArrayList is the one we’ll study here. By the end of
Chapter 10, you will be able to write a simple version of ArrayList. By the end of
your second computer science course, you should be able to write your own version of
LinkedList.
Lists such as ArrayList keep its elements in order. It makes sense to speak of the first KEY IDEA
element or the last element. Like a String, an individual element is identified by its The size query
index—a number greater than or equal to zero and less than the number of elements in returns the number of
the list. The number of elements in the list can be obtained with the size query. elements in the list.
Construction
The type of a collection specifies the collection’s class and the class of object it holds.
For example, one type that could hold a collection of Alarm objects is
ArrayList<Alarm>. The type of objects held in the collection is placed between angle
brackets. This type can be used to declare and initialize a variable, as follows:
ArrayList<Alarm>ƒalarmsƒ=ƒnewƒArrayList<Alarm>();
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 433
433
KEY IDEA In the AlarmClock class shown in Listing 8-7, the declaration of the four Alarm
A collection allows instance variables in lines 10–13 can be replaced with the following line:
you to access many
objects using only privateƒArrayList<Alarm>ƒalarmsƒ=ƒnewƒArrayList<Alarm>();
one variable.
Furthermore, we are no longer limited to just four alarms.
Adding Elements
ch08/alarmsWithLists/
The power of using a collection class becomes evident in the setAlarm method. In
Listing 8-7, we devote lines 68–78 to assigning an alarm to one of the four instance
variables—11 lines. Even so, we’re limited to only four alarms. For each additional
alarm, we need to add an instance variable and two more lines in the setAlarm
method.
Using an ArrayList to store the alarms reduces lines 68–78 to a single line:
this.alarms.add(theAlarm);
The add method just shown adds the new alarm to the end of the list. An overloaded
version of add allows you to state the index in the list where the alarm should be
added. Like Strings, an ArrayList numbers the positions in its list starting with 0.
Therefore, the following line adds a new alarm in the third position:
this.alarms.add(2,ƒtheAlarm);
The alarms at indices 0 and 1 come before it. Objects at indices 2 and larger are moved
over by one position to make room for the new object. Figure 8-18 illustrates inserting
a new Alarm for 11:00 at index 2.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 434
434
CHAPTER 8 | COLLABORATIVE CLASSES
The index for add must be in the range 0..size(). Positions can’t be skipped when
adding objects. For example, you can’t add an object at index 2 before there is data at
indices 0 and 1. Doing so results in an IndexOutOfBoundsException.
A single element of the collection can be accessed using the get method and specifying
the object’s index. For example, to get a reference to the third alarm (which is at index 2
because numbering starts at 0), write the following statements:
AlarmƒanAlarmƒ=ƒthis.alarms.get(2);
anAlarm.ring();ƒƒƒƒƒƒƒƒƒƒ// do something with the alarm
As with any other method that returns a reference, you aren’t required to assign the
reference to a variable before calling a method. We could condense the previous two
statements to a single line:
this.alarms.get(2).ring();
An element can be replaced using the set method. Its parameters are the index of the
element to replace and the object to put there. For example, Figure 8-19 illustrates the
change made by the following code fragment:
AlarmƒoldAlarmƒ=ƒnull;
AlarmƒnewAlarmƒ=ƒnewƒAlarm(11,ƒ15,ƒ"Meeting with Mohamed");
oldAlarmƒ=ƒthis.alarms.set(2,ƒnewAlarm);
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 435
435
Before using the set method After using the set method
Notice that the element at index 2 now refers to the new alarm. The set method
returns a reference to the element that is replaced, which is assigned to oldAlarm.
An element can be removed from the ArrayList with the remove method. Its only argu-
ment is the index of the element to remove. After removing the element, any elements in
subsequent positions are moved up to occupy the now open position—the opposite of
what add does. Like set, remove returns a reference to the removed element.
There are many other methods in the ArrayList class and its superclasses. Table 8-1
lists the name and purpose of some of the most useful methods. E represents the type of
elements stored in this particular collection.
The contains and indexOf methods depend on the element’s class overriding the
equals method to test for equivalence. As noted in Section 8.2.4, we don’t have the
tools to do this yet for the classes we write. Provided classes such as String,
DateTime, and others should meet this requirement.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 436
436
CHAPTER 8 | COLLABORATIVE CLASSES
boolean add(E elem) Add the specified element to the end of this list. Some of the most useful
Return true. methods in the
ArrayList class. E is
void add(int index, E elem) Insert the specified element at the specified index
in this list. 0ƒ≤ indexƒ< size(). the type of the elements
boolean contains(Object elem) Return true if this list contains the specified element.
int indexOf(Object elem) Search for the first element in this list that is equal
to elem, and return its index or -1 if there is no
such element in this list.
E remove(int index) Remove and return the element at the given index.
0IJ index < size().
E set(int index, E elem) Replace the element at the given position in this
list with elem. Return the old element. 0IJ index <
size().
The last detail needed to replace the four Alarm variables with a list is checking each
alarm to see if it’s time to ring it. In Listing 8-7, we did this in lines 47–52. Each line
calls a helper method to check one of the alarms. That means 4 alarms, 4 lines of code;
400 alarms, 400 lines of code.
There are three distinct ways3 to process all of the elements in an ArrayList. We’ve
already seen the basic tools for one of them: the get and size methods. We can use
them in a for loop to get each element in turn:
1 privateƒvoidƒcheckAndRingAlarms(DateTimeƒcurrTime)
2 {ƒforƒ(intƒindexƒ=ƒ0;ƒindexƒ<ƒthis.alarms.size();ƒindex++)
3 ƒƒ{ƒAlarmƒanAlarmƒ=ƒthis.alarms.get(index);
Process All Elements
4 ƒƒƒƒthis.checkOneAlarm(anAlarm,ƒcurrTime);
5 ƒƒ}
6 }
3 The third way uses iterators, a topic we won’t be covering in this textbook.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 437
437
A loop to process all of the elements in a collection is so common that Java 5.0 intro-
duced a special version of the for loop just to make these situations easier. It is some-
times called a foreach loop—the body of the loop executes once for each element in the
collection.
Using a foreach loop to process each alarm results in the following method:
privateƒvoidƒcheckAndRingAlarms(DateTimeƒcurrTime)
{ƒfor(AlarmƒanAlarmƒ:ƒthis.alarms)
ƒƒ{ƒthis.checkOneAlarm(anAlarm,ƒcurrTime);
Process All Elements
ƒƒ}
}
for(«elementType»ƒ«varName»ƒ:ƒ«collection»)
{«statementsƒusingƒvarName»
}
The statement includes the keyword for, but instead of specifying a loop index, the
forƒeach loop declares a variable, «varName», of the same type as the objects con-
tained in «collection». The variable name is followed with a colon and the collec-
tion that we want to process. «varName» can only be used within the body of the loop.
A version of AlarmClock that uses an ArrayList is shown in Listing 8-11. Note that
changes are shown in bold. Documentation is identical to Listing 8-7, so it is omitted.
4 We don’t say ArrayList will handle any number because eventually your computer would run out
438
CHAPTER 8 | COLLABORATIVE CLASSES
11 ƒƒprivateƒfinalƒbooleanƒTESTING;
12
13 ƒƒpublicƒAlarmClock(booleanƒtest)
14 ƒƒ{ƒ// Same asƒListingƒ8-7.
15 ƒƒ}
16
17 ƒƒpublicƒvoidƒrun(intƒsecPerSec)
18 ƒƒ{ƒ// Same as Listing 8-7.
19 ƒƒ}
20 ƒƒ
21 ƒƒprivateƒvoidƒcheckAndRingAlarms(DateTimeƒcurrTime)
22 ƒƒ{ƒfor(AlarmƒanAlarmƒ:ƒthis.alarms)
23 ƒƒƒƒ{ƒthis.checkOneAlarm(anAlarm,ƒcurrTime);
24 ƒƒƒƒ}
25 ƒƒ}
26
27 ƒƒprivateƒvoidƒcheckOneAlarm(Alarmƒalarm,ƒDateTimeƒcurrTime)
28 ƒƒ{ƒ// Same as Listingƒ8-7.
29 ƒƒ}
30
31 ƒƒpublicƒvoidƒsetAlarm(intƒhr,ƒintƒmin,ƒStringƒmsg)
32 ƒƒ{ƒAlarmƒtheAlarmƒ=ƒnewƒAlarm(hr,ƒmin,ƒmsg);
33 ƒƒƒƒthis.alarms.add(theAlarm);
34 ƒƒƒƒthis.numAlarmsLeft++;
35 ƒƒ}
36
37 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
38 ƒƒ{ƒ// Same as Listingƒ8-7.
39 ƒƒ}
40 }
Class Diagrams
Someone drawing a class diagram for AlarmClock, as shown in Listing 8-11, would
probably draw a diagram as shown in Figure 8-20a. However, collection classes like
ArrayList appear so often in Java programs and their function is so well known that
most programmers prefer to draw the abbreviated class diagram shown in Figure 8-20b.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 439
439
Like a list, a set also manages a collection of objects. There are two important differences:
➤ A set does not allow duplicate elements. Sets ignore attempts to add an ele-
ment that is already in the set.
➤ The elements are not ordered. None of the methods in HashSet take an index
as an argument.
KEY IDEA These restrictions don’t affect the AlarmClock class—each alarm is unique and indi-
Sets do not allow vidual alarms are not important; they are all processed as a group. In fact, changing
duplicates. ArrayList to HashSet in line 8 of Listing 8-11 is all that is needed to convert that
program to use a set.
LOOKING AHEAD So how might we exploit the specific properties of a set? We could use it, for example,
Processing files is a to count the number of unique strings in a file. About two dozen lines of code are
major topic of enough to discover that William Shakespeare’s play Hamlet contains 7,467 unique
Chapter 9. “words.” (Words is quoted because the program doesn’t remove punctuation or num-
bers, meaning that “merry” and “merry?” are considered different words.)
Construction
We’ll use an instance of the HashSet class to count the words. An instance of
HashSet is constructed just like ArrayList—specify the type of the elements you
want it to manage in angle brackets. In this case, we’ll store our words as strings.
HashSet<String>ƒwordsƒ=ƒnewƒHashSet<String>();
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 440
440
CHAPTER 8 | COLLABORATIVE CLASSES
Useful Methods
Words can be added to this set with the add method. If the word is already there, it will
be ignored.
To add many words, we should read them from a file—the topic of Section 9.1. Until
then, we can add some words from Hamlet manually:
words.add(“to”);
words.add(“be”);
words.add(“or”);
ch08/collections/
words.add(“not”);
words.add(“to”);
words.add(“be”);
The size method returns the number of elements in the set. Given the previous six
calls to add, size would return 4.
The contains method will return true if the set contains the given object and false
otherwise. Other useful methods are summarized in Table 8-2.
booleanƒadd(Eƒelem) Add the specified element to this set. Return true Some of the most useful
if the element was already present. methods in the HashSet
class (E is the type of the
voidƒclear()ƒ Remove all of the elements from this set.
elements)
booleanƒcontains(Objectƒelem)ƒ Return true if this set contains the specified element.
We can print all of the words in the set using a forƒeach loop, just as we processed all KEY IDEA
of the elements in the ArrayList earlier. A set’s forƒeach
loop works the same
forƒ(Stringƒwƒ:ƒwords) way as for a list.
{ƒSystem.out.print(wƒ+ƒ“ƒƒ“);
}
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 441
441
Limitations
HashSet uses a technique known as hashing, in which elements are stored in an order
defined by the element’s hashCode method. The hash code is carefully constructed to
make operations such as contains and remove faster than for an ArrayList. When
the elements are printed, however, they appear in an order that seems random.
hashCode is inherited from the Object class. As defined there, no two objects are
considered equal or equivalent. If two elements in your set should be considered equiv-
alent (for example, two different date objects both representing the same date), the
equals and hashCode methods must both be overridden. Unfortunately, overriding
hashCode is beyond the scope of this textbook. However, you should have no problem
using HashSet if you either use it with a set of unique objects or use it with provided
classes, such as String or DateTime.
(figure 8-21)
Key Value
Key-value pairs Sue 578-3948
Fazila 886-4957
Jo 1-604-329-1023
Don 578-3948
Rama 886-9521
With these associations between keys and values, we can ask questions such as “What’s
the phone number for Don?” We use the key, “Don,” to look up the associated value,
“578-3948.”
KEY IDEA Notice that all the keys are unique; that’s a fundamental requirement of a map. If we
The keys in any given have two friends named “Don” we must distinguish between them, perhaps by adding
map must be unique. initials or last names. However, the associated values do not need to be unique. In this
example, Don and Sue both appear in the mapping even though they have the same
phone number.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 442
442
CHAPTER 8 | COLLABORATIVE CLASSES
Java provides two classes implementing a map, TreeMap and HashMap. Each one has
different advantages and disadvantages. HashMaps have the advantage of being some-
what faster but require a correct implementation of the hashCode method. On the
other hand, TreeMaps keep the keys in sorted order but require a way to order the ele-
ments. We’ll use a TreeMap to build a simple phone book.
Construction
When declaring and constructing a TreeMap object, the types for both the keys and the
values must be specified. For our simple phone book, we’ll use Strings for both the
keys and the values:
TreeMap<String,ƒString>ƒphoneBookƒ=ƒ
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒnewƒTreeMap<String,ƒString>();
Although this example happens to use strings for both keys and values, that need not
be the case. The types of the keys and values are often different and must be a reference
type—not a primitive type like int, double, or char.
How can you figure out that TreeMap needs two types to define it but that ArrayList
and HashSet require only one? Look at the class documentation. Figure 8-22 shows the
beginning of the online documentation for TreeMap, which includes TreeMap<K,ƒV> in
large type. The two capital letters between the angle brackets indicate that two types are
needed when a TreeMap is constructed. Finding out that K stands for the type of the key
and V stands for the type of the value is, unfortunately, not as easy to figure out from the
documentation.
There is one restriction on the type of the key. Because TreeMap keeps the keys in
sorted order, it needs a way to compare them. It relies on the key’s class to implement
the Comparable interface. The keys are then known to have a compareTo method.
String and DateTime both implement the interface and can be used as keys.
You can tell if a class implements Comparable by looking at the “All Implemented LOOKING AHEAD
Interfaces” line in the documentation. You can see an example of this line in Figure 8-22. Writing your own
Also, if you look at the documentation for Comparable, it will list the classes in the Java classes that
implement the
library that implement it.
Comparable
interface will be
discussed in
Section 12.5.1.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 443
443
Useful Methods
Pairs are added to a map with the put method. It takes a key and a value as arguments:
phoneBook.put("Sue", "578-3948");
phoneBook.put("Fazila", "886-4957");
If the key already exists in the map, the value associated with that key will be replaced
by the new value.
A value can be retrieved with the get method. The key of the desired value is passed as
an argument. For example, after executing the following line:
Stringƒnumberƒ=ƒphoneBook.get("Sue");
the variable number will contain “578-3948” (assuming the associations shown in
Figure 8-21). It’s similar to accessing an element in a list except that instead of specify-
ing the element’s index, you specify the element’s key.
The remove method takes a key as its only argument and removes both the key and its
associated value.
Like a list and a set, a map has isEmpty, clear, and size methods. Instead of con-
tains, it has two methods: containsKey and containsValue, which both return a
Boolean result. These methods are summarized in Table 8-3.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 444
444
CHAPTER 8 | COLLABORATIVE CLASSES
voidƒclear()ƒ Remove all of the key-value pairs from this mapping. Some of the most useful
methods in the TreeMap
booleanƒcontainsKeyƒ Return true if this mapping contains the specified key. class (K is the type of the
ƒƒƒƒƒƒƒƒ(Objectƒelem)
keys; V is the type of the
booleanƒcontainsValueƒ Return true if this mapping contains the specified value. values)
ƒƒƒƒƒƒƒƒ(Objectƒelem)
Vƒput(Kƒkey,ƒVƒvalue) Associate the specified key with the specified value in this
mapping. Return the value previously associated with the
key or null if there wasn’t one.
Vƒremove(Objectƒkey) Remove and return the value associated with the specified
key, if it exists. Return null if there was no mapping for
the key.
Processing all the elements in a map is more complicated than a list or a set because
each element is a pair of objects rather than just one thing.
One approach is to use the keySet method to get all of the keys in the map as a set.
We can then loop through all of the keys using the forƒeach loop. As part of the pro-
cessing, we can also get the associated value, as shown in the following example:
Completed Program
The completed telephone book program is shown in Listing 8-12. It uses a Scanner
object in 27 and 30 to obtain a name from the program’s user. Using Scanner effec-
tively is one of the primary topics of the next chapter.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 445
445
446
CHAPTER 8 | COLLABORATIVE CLASSES
What if we want to store integers or characters or some other primitive type in one of
the collection classes? For example, we might need a set of the prime numbers (integers
that can only be divided evenly by 1 and itself). If we write
HashSet<int>ƒprimeNumbersƒ=ƒnewƒHashSet<int>();
the Java compiler will give us a compile-time error, perhaps with the cryptic message
“unexpected type.” The problem is that the compiler is expecting a reference type—the
name of a class—between the angle brackets. int, of course, is a primitive type.
We can get around this by using a wrapper class. It “wraps” a primitive value in a class.
A simplified wrapper class for int is as follows:
publicƒclassƒIntWrapperƒextendsƒObject
{ƒprivateƒintƒvalue;
ƒƒpublicƒIntWrapper(intƒaValue)
ƒƒ{ƒsuper();
ƒƒƒƒthis.valueƒ=ƒaValue;
ƒƒ}
ƒƒpublicƒintƒintValue()
ƒƒ{ƒreturnƒthis.value;
ƒƒ}
}
Fortunately, Java provides a wrapper class for each of the primitive types: Integer,
Double, Boolean, Character, and so on. These are in the java.lang package,
which is automatically imported into every class.
HashSet<Integer>ƒprimesƒ=ƒnewƒHashSet<Integer>();
The Java compiler will automatically convert between an int and an instance of KEY IDEA
Integer when using primes. For example, consider the program in Listing 8-13. In lines Java 5.0 automatically
12–17, the add method takes an int, not an instance of Integer. The contains converts between
primitive values and
method in line 25 is the same. Before Java 5.0 the programmer needed to manually
wrapper classes.
include code to convert between primitives and wrapper objects.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 447
447
448
CHAPTER 8 | COLLABORATIVE CLASSES
First, GUIs are constructed from a library of components. You’ve already used a number of
these: JFrame, JPanel, JComponent, JButton, and so on. JFrame typically collaborates
with JPanel to organize a number of components to display. JPanel collaborates with
one or more components such as JButton to display information in the right format.
For example, consider the program written in Section 6.7. It displays three tempera-
tures using a custom thermometer component. The class diagram in Figure 8-23 shows
that the JFrame has-a JPanel to help organize the components it displays. The
JPanel has-a Thermometer to actually display a temperature. In fact, the JPanel has
a number of Thermometer objects. Finally, the Thermometer class is-a JComponent.
The two classes collaborate to provide a standard set of services with the customized
appearance provided by paintComponent.
* Thermometer
-int MIN_TEMP
JPanel -int MAX_TEMP
-int temp
-ArrayList components
+Thermometer( )
+JPanel( ) +void paintComponent(Graphics g)
+void add(JComponent comp) +void setTemperature(int newTemp)
The collaboration between these classes allows each to have a specific focus. Focused
classes are easier to understand, write, debug, and maintain.
Collaborating classes are also used with modern graphical user interfaces via the
Model-View-Controller pattern. This pattern splits a program into three collaborating
classes or groups of classes.
➤ The model is responsible for modeling the current problem. For example, the
AlarmClock class we wrote earlier models the problem of keeping the current
time and determining when to ring the alarms, but has little to do with dis-
playing anything to the user.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 449
449
8.7 PATTERNS
➤ The view shows relevant information in the model to the user. In an alarm
clock program, the view is the class (or group of classes) that show the user
what time it is and when the alarms are due to ring. This is information that
the view obtains from the model.
➤ The controller is responsible for gathering input from the user and using it to
modify the model, for example, by changing the current time or the time when
an alarm is due to ring. When the controller changes the model, the view
should also change to show the new information.
The view and the controller work together closely and are known as the user interface.
The relationships between these three groups of classes are shown in Figure 8-24. The
eye represents the user observing the model via the view. The mouse represents the user
changing the model via the controller. The arrow between the controller and the view
indicates that the controller may call methods in the view, but the view has no need to
interact with the controller. The two arrows from the user interface to the model indi-
cate that both the view and the controller will have reason to call methods in the
model. The last arrow is dotted to indicate that the model will call methods in the user
interface, but in a limited and controlled way.
(figure 8-24)
User Interface
The view and controller
interact with the user and View
the model
Model
Controller
8.7 Patterns
450
CHAPTER 8 | COLLABORATIVE CLASSES
Solution: Identify one or more subsets of methods and instance variables that form a
cohesive concept. Make each subset into a separate helper class that the original class
can use to solve the overall problem. The original class will likely have one or more
instance variables referring to instances of the helper classes. A general pattern is
shown in the following code:
publicƒclassƒ«className»...
{ƒprivateƒ«helperClassName»ƒ«var1»;
ƒƒpublicƒ«className»(...)
ƒƒ{ƒ// initialize the helper class
ƒƒƒƒthis.«var1»ƒ=ƒ...
ƒƒ}
ƒƒ...ƒ«methodName»(...)
ƒƒ{ƒ// use the helper class
ƒƒƒƒ...ƒthis.«var1».«methodName»...
ƒƒ}
}
(figure 8-25)
className helperClassName
helperClassName var1 Class diagram resulting
from Has-a (composition)
??? methodName(...)
pattern
Consequences: The individual classes will become smaller and more focused on a par-
ticular task, making them easier to write, test, debug, and modify.
Related Pattern: The Has-a pattern is a special case of the Instance Variable pattern,
where the instance variable is an object reference.
Context: A method is required to test whether two objects are equivalent to each other
in value.
Solution: Write a method, isEquivalent, that takes one of the objects as an argu-
ment and tests all the relevant fields for equivalence. In general,
publicƒclassƒ«className»ƒ...
{ƒprivateƒ«primitiveType»ƒ«relevantField1»
ƒƒ...
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 451
451
8.7 PATTERNS
ƒƒprivateƒ«referenceType»ƒ«relevantField2»
ƒƒ...
ƒƒpublicƒbooleanƒisEquivalent(«className»ƒother)
ƒƒ{ƒreturnƒotherƒ!=ƒnullƒ&&
ƒƒƒƒƒƒƒƒthis.«relevantField1»ƒ==ƒother.«relevantField1»ƒ&&
ƒƒƒƒƒƒƒƒ...
ƒƒƒƒƒƒƒƒthis.«relevantField2».isEquivalent(
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒother.«relevantField2»)ƒ&&
ƒƒƒƒƒƒƒƒ...;
ƒƒ}
}
where == is used for primitive fields and either isEquivalent or equals is used for
objects.
Consequences: The method will determine whether two objects are equivalent by test-
ing all the relevant fields for equivalence. Using isEquivalent may give unexpected
results with methods such as contains in Java’s collection classes. Those classes
assume that equals has been properly overridden, but that requires concepts first dis-
cussed in Chapter 12.
Related Patterns:
➤ The Equivalence Test pattern is a specialization of the Predicate pattern.
➤ The Equals pattern (Section 12.7.3) is a better choice than this pattern, once
the details of implementing equals have been mastered.
Context: Your method detects an exceptional event that is most appropriately handled
by the method’s client.
Solution: Create an exception object to report details of the exceptional event and use
Java’s throw statement, as follows:
ifƒ(«testForErrorCondition»)
{ƒthrowƒnewƒ«exceptionName»(«stringDescription»);
}
Consequences: Clients of the called method are informed of the exceptional event and
may be able to recover if the exception is handled. In the case of a checked exception
such as FileNotFoundException, clients must either handle the exception or declare
that they throw it.
Related Pattern: Thrown exceptions may be caught and handled with the Catch an
Exception pattern.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 452
452
CHAPTER 8 | COLLABORATIVE CLASSES
Context: You are calling a method that can throw an exception. You want to handle
the exception to protect the program’s users from the consequences of the problem.
Solution: Catch the exception using a try-catch statement and the following template:
try
{ƒ«statementsƒthatƒmayƒthrowƒanƒexception»ƒ
}ƒcatchƒ(«exception_1ƒe»)
{ƒ«statementsƒtoƒhandleƒexcepton_1»
}ƒcatchƒ(«exception_2ƒe»)
{ƒ«statementsƒtoƒhandleƒexcepton_2»
}
Consequences: Exceptions that are thrown by statements within the try clause are
handled in the matching catch clause, if one exists. If there is no matching catch
clause, the exception is propagated to the caller. The catch clauses are evaluated in
order, with the result that the most specific exceptions should appear first and the most
general exceptions later.
Related Pattern: Exceptions are thrown with the Throw an Exception pattern.
Context: The same operation must be performed on all the objects in a collection.
The exact form of the pattern depends on the type of collection. For a list, the follow-
ing code may be used.
forƒ(intƒiƒ=ƒ0;ƒiƒ<ƒ«collection».size();ƒi++)
{ƒ«elementType»ƒelementƒ=ƒ«collection».get(i);
ƒƒ«statementsƒtoƒprocessƒelement»
}
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 453
453
forƒ(«elementType»ƒelementƒ:ƒ«collection»)
{ƒ«statementsƒtoƒprocessƒelement»
}
forƒ(«keyType»ƒkeyƒ:ƒ«collection».getKeySet())
{ƒ«valueType»ƒvalueƒ=ƒ«collection».get(key);
ƒƒ«statementsƒtoƒprocessƒkeyƒandƒvalue»
}
Consequences: Using a collection to handle multiple objects of the same type can make
lots of code much simpler, especially code that processes each of the elements in turn.
Related Patterns:
➤ The Process All Elements pattern is related to the Process File pattern
(Section 9.9.3) and will be recast using arrays in Section 10.8.1.
➤ Processing all the characters in a String is similar to this pattern, although
the forƒeach loop is not applicable in that setting.
454
CHAPTER 8 | COLLABORATIVE CLASSES
a helper
to class
ity in
unc t ional
rs f
facto
is also known as “Has-a”
composition
is diagrammed with
is diag
db
ramme
d with
te
rela
by
be
ted
can
ela
instance
er
nb
may be variables
ca
reference may b
variables e
temporary
may
two classes via be variables
are
con
ed
m
era sed
te
ste ay be
ss s
tai
parameter
e
sev alia
df
acc
na
l
or variables
by y b e
eq
are
ua
lit
ma
yw
address in ith returned from
objects have an memory a method
are t
ested
for e ==
q uival
ma ed
ence
ad
y b to
with
d
methods such as
e
equals or isEquivalent
include
collections ArrayLists
includ
e
incl
ude HashSets
TreeMaps
Written Exercises
8.1 A book has a title, an author, and a call number. A library patron has a name
and an ID number, and may or may not have a book checked out.
a. Draw a class diagram for Patron where only a single book may be checked
out at any given time. Include the methods necessary to check out a book,
and print which book (if any) the patron has.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 455
455
(figure 8-26)
Client Account
Partial class diagram for
-String name -double balance
a bank +Account acct
-int pin +Account( )
+void deposit(double amt)
+Client(Account a) +void withdraw(double amt)
+void deposit(int pin, double amt) +void getBalance( )
+void withdraw(int pin, double amt)
+double getBalance(int pin)
+Account getAccount( )
Suppose you are a programmer working on the Bank class, which contains ref-
erences to objects representing all of the bank’s clients. Explain three ways in
which you could transfer money from one client’s account to your account
without knowing the client’s PIN. In each case, explain how this security hole
could be closed.
Assume the programmer who implemented Client and Account knows noth-
ing of the dangers of using aliases.
Programming Exercises
8.3 Consider the program in Listing 8-2. According to the surrounding text, it was
used to find that Luke was 5,009 days old on the day that paragraph was writ-
ten. Modify the program to print the date the paragraph was written.
8.4 Consider a FuelBot class. It extends Robot and uses a FuelTank. Each time
the robot moves, it will use 0.4 liters of fuel from the tank. The tank holds 3
liters of fuel when it is full. If the robot comes to an intersection with a Thing
on it, refill its tank. If the robot ever runs out of fuel, it breaks.
a. Draw a class diagram that includes the Robot, FuelBot, and FuelTank
classes. Include variables and methods.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 456
456
CHAPTER 8 | COLLABORATIVE CLASSES
457
Programming Projects
LOOKING BACK
8.8 Implement a program to run at a checkout counter in a store. The main
Dotted lines between method will create the CheckOut object and then give it a number of Items to
classes mean a class
check out. The CheckOut object will be able to produce an itemized receipt.
uses another class but
doesn’t hold an (Hint: You can use an ArrayList or a String to build the receipt as items are
instance variable to it. sold.) A partial class diagram is shown in Figure 8-27.
See Section 8.2.2.
(figure 8-27)
CheckOutMain CheckOut
Partial class diagram for
-double totalSale
checking out items at -double receipt
+void main(...)
a store
+CheckOut(double taxRate)
Item +void sell(Item anItem)
+double getTotalSale( )
-String descr +double getTaxes( )
-double price +String getReceipt( )
8.9 A class diagram for another store’s checkout counter is shown in Figure 8-28.
Write a program where the main method creates a CheckOut object and a
Customer object, complete with a number of Items to buy. Call the checkout
method to generate an itemized receipt. Print the receipt.
8 Chapter C5743 40143.ps 11/30/06 1:31 PM Page 458
458
CHAPTER 8 | COLLABORATIVE CLASSES
(figure 8-28)
CheckOutMain CheckOut
Another partial class
diagram for checking out
+void main(...) +CheckOut(double taxRate) items at a store
+String checkout(Customer c)
Customer
* Item
-ArrayList items
-String descr
+Customer( ) -double price
+void addItem(Item i)
+Item getItem(int i)
+int numItems( )
8.10 A checkbook has an opening balance and zero or more checks. Each check has a
check number, the name of the person or company who can cash it, an amount,
and a memo. A checkbook should be able to return information about a check,
given its check number. It should also be able to give the current balance.
a. Would you implement this program using a list, set, or map? Why?
b. Without prejudicing your answer to part (a), draw a class diagram assuming
the program uses a map.
c. Implement the classes as shown in part (b). Include a main method in the
Checkbook class to test it.
8.11 Modify the prime number program in Listing 8-13 to include all the prime
numbers less than 10,000. Obviously, you want the program to calculate these
values. The most straightforward approach is to consider every integer between
2 and 10,000. If the integer is prime, add it to the set. How do you test if the
integer i is prime? Divide i by every number between 2 and i-1. If the remain-
der is 0 for any of them, i is not prime. The % operator yields the remainder of
an integer division.
An equivalent test that is more efficient is to only divide by the prime numbers
less than i—that is, the numbers that are already in your set of prime numbers.
Use this more efficient approach to calculate the prime numbers.
8.12 Write a main method that repeatedly asks the user for the URL of a sound file,
downloads it, and plays it. You will need to use the newAudioClip method in
the java.applet.Applet class along with the java.applet.AudioClip
and java.net.URL classes, among others. Unfortunately, these classes won’t
play .mp3 files. There is a .wav file you may test your program with at
www.learningwithrobots.com/downloads/WakeupEverybody.wav. It’s
a large file but only plays for a few seconds.
The sample solution is less than 30 lines of code. You will need to handle at
least one checked exception.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 459
Computers excel at managing large amounts of data. The data might consist of credit
card transactions at your bank, student records at your school, the card catalog at your
local library, or a song on your computer. Such data is stored in a file on a hard drive
or similar device.
In this chapter, we will learn how to work with files: How to open a file, process the
data, and close the file.
These same techniques form the basis for interacting with the program’s user via text.
The program displays text to the user; the user types text to the program. Learning
these basic techniques still has value, even in an age of graphical user interfaces.
Writing a graphical user interface is difficult and, for many programs, simply isn’t
worth the trouble. Text interfaces are often sufficient.
459
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 460
460
OUTPUT
Each time you visit a Web page with your browser, the Web server responsible for deliv- KEY IDEA
ering that page records what it does in a log. On a busy Web server, this log can grow to Files are often
include millions of records. On the computer hosting my personal home page, one week organized using
of log entries during a quiet time of year resulted in more than 360,000 records. A record records.
refers to one entry within the file consisting of several related pieces of information. Each
piece of information in a record is called a field. For example, a typical record1 in the
server’s log contains the information shown in Table 9-1. This particular record shows
that someone at the University of Massachusetts looked at a graphic on my Web page on
August 19, 2005.
128.119.246.74 The IP address, or Internet Protocol address, of the computer Information from a
requesting the Web page. typical record in a Web
server’s log
vinci5.cs.umass.edu The host name of the computer requesting the Web page. The
host name and the IP address are largely interchangeable. One
is easier for computers; the other is easier for people.
2005/8/19@11:24:14 The date and time the Web page was served.
GET The command that came from the browser requesting the page.
Other commands include POST (used for pages with forms) and
PUT (used for uploading data).
/~bwbecker/mandel/ The specific file that was requested. In this case, it isn’t a Web
Gods_Eye_Heart.GIF page at all but a graphic that is part of a Web page. Once you
know the name of the Web server (www.cs.uwaterloo.ca), you
can reconstruct the requested URL and look at it with a browser
(www.cs.uwaterloo.ca/~bwbecker/mandel/Gods_Eye_Heart.GIF ).
200 The completion code. A code that begins with 2 indicates that
the request completed normally.
In this chapter, we will write a series of programs that can be used to explore a Web
server’s log. If you have a personal home page, you may want to obtain a log to see
what you can learn about who is accessing your page and how frequently your page is
requested.
1 The format of the record has been adjusted slightly. The program that does so is included with the
examples for this chapter in the directory formatLog. The changes consist of removing several unin-
teresting fields, looking up the IP address to obtain the host name, and reformatting the date.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 461
461
The program in Listing 9-1 provides a first look at a program that processes a file,
which involves three important steps:
➤ Lines 13–21 locate the file on the disk drive and construct a Scanner object to
obtain the information it contains. This process is called opening a file.
➤ Lines 24–29 process the file one record at a time, printing selected records. It
uses two methods in the Scanner class, hasNextLine and nextLine.
Obtaining data from a file is called reading a file. The information obtained
from the file is called the program’s input.
➤ Line 32 closes the file when it is no longer being used.
Listing 9-1: A program to read a Web server’s log and print records containing a given string
ch09/processLines/
1 importƒjava.io.File;
2 importƒjava.io.FileNotFoundException;
3 importƒjava.util.Scanner;
4
5 /** Read a Web server's log record by record. Print those records that contain the
6 * substring "bwbecker".
7 *
8 * @author Byron Weber Becker */
9 publicƒclassƒReadServerLog
10 {
11 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
12 ƒƒ{ƒ// Open the file.
13 ƒƒƒƒScannerƒinƒ=ƒnull;
14 ƒƒƒƒtryƒ
15 ƒƒƒƒ{ƒFileƒfileƒ=ƒnewƒFile("server_log.txt");
Open File for Input
16 ƒƒƒƒƒƒinƒ=ƒnewƒScanner(file);ƒ
17 ƒƒƒƒ}ƒcatchƒ(FileNotFoundExceptionƒex)ƒ
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 462
462
OUTPUT
CHAPTER 9 | INPUT AND
Listing 9-1: A program to read a Web server’s log and print records containing a given string
(continued)
18 ƒƒƒƒ{ƒSystem.out.println(ex.getMessage());
19 ƒƒƒƒƒƒSystem.out.println("in "ƒ+ƒSystem.getProperty("user.dir"));
20 ƒƒƒƒƒƒSystem.exit(1);
21 ƒƒƒƒ}
22
23 ƒƒƒƒ// Read and process each record.
24 ƒƒƒƒwhileƒ(in.hasNextLine())
25 ƒƒƒƒ{ƒStringƒrecordƒ=ƒin.nextLine();
26 ƒƒƒƒƒƒifƒ(record.indexOf("bwbecker")ƒ>=ƒ0) // author's Web pages
Process File
27 ƒƒƒƒƒƒ{ƒSystem.out.println(record);
28 ƒƒƒƒƒƒ}
29 ƒƒƒƒ}
30
31 ƒƒƒƒ// Close the file.
32 ƒƒƒƒin.close();
33 ƒƒ}
34 }
Opening a File
Conceptually, opening a file is simple. All we want to do is execute the following two lines:
Fileƒfileƒ=ƒnewƒFile("server_log.txt");
Scannerƒinƒ=ƒnewƒScanner(file);
The first line creates a File object that describes where the program should look for
the file named server_log.txt. The second line creates an object used to access the
file at that location.
If only it were that simple. In reality, things can go wrong. The most common problem, LOOKING BACK
and the only one that throws a checked exception, is when the file is not at the expected Exceptions were
location. The programmer may have misspelled the name as serverlog.txt, the file discussed in
may have been moved, the program may be running in an unexpected location, or the file Section 8.4.
may not have been created yet. In any of these cases, the Scanner constructor will throw
a FileNotFoundException. Handling this exception expands the two lines we need to
execute into nine lines in Listing 9-1.
463
This code results in a message similar to the one shown in Figure 9-1. The message says
the system started looking for the file with a disk drive labeled D:. That drive has a
directory named Robots. Inside Robots is a directory named examples, which con-
tains a directory named ch09. Inside ch09 is processLines. That is the directory
where the program expected to find the file named server_log.txt.
For the time being, we’ll assume that in such circumstances you will simply move the
file to the directory where the system expects to find it. Later, we will learn how to
open files in other locations.
(figure 9-1)
Example of the
message printed when a
file is not found
Processing a File
Lines 24–29 in Listing 9-1 are responsible for processing the data in the file. Many
files, including the server log, are organized as one record per line of text, as shown in
Figure 9-2. The requested filenames are shortened so that each record fits on one line.
The nextLine method, used in line 25 of Listing 9-1, retrieves one line from the file.
With each repetition of the loop, it obtains the next line. This continues as long as
hasNextLine returns true. When the last line has been read, hasNextLine will
return false and the loop will stop.
Finally, the if statement contained within the loop prints out only those lines that con-
tain the string bwbecker—that is, it prints out the log records pertaining to the
author’s Web pages.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 464
464
OUTPUT
Closing a File
CHAPTER 9 | INPUT AND
Files use significant resources. Closing the file with the close method (line 32) allows
the system to free up those resources for other uses.
The previous program simply displays selected records in the console window. If a KEY IDEA
large number of records are selected, the first records will scroll out of view long before Writing a file is the
the last records are displayed. An alternative is for the program to copy the selected opposite of reading it.
records to their own file. The process of creating a file and placing records in it is called
writing a file. The information written is called the program’s output. The terms input
and output are often used together and abbreviated as I/O.
The program in Listing 9-2 is the same as the previous program except that it writes
the selected records to a file named bwbecker.txt instead of printing them on the
console. As with reading, there are three steps to writing the file:
➤ The file is opened at line 18 by constructing an instance of PrintWriter.
This object opens the file and provides methods for writing data to it. Like
opening a file to read it, a FileNotFoundException can be thrown.
Therefore, the constructor call is placed inside the try-catch statement but
the variable, out, is declared earlier, in line 15.
➤ The selected records are written to the file, one record at a time, in line 29. The
PrintWriter class provides the same methods as System.out, including
print, println, and printf.
➤ Finally, the file is closed at line 36.
465
KEY IDEA Java does not always write information to the file immediately. By collecting informa-
Ensure that all data is tion from several calls to print and println and writing them all at once, substantial
written by calling gains in efficiency can be realized. This process is called buffering. Some information
close before the
may not be written to the file at all if the program ends at the wrong time. To prevent
program ends.
this, you should always call the close method after you are done writing to the file. It
is an error to call a print method after close has been called.
What happens if the preceding code is executed again and the file bwbecker.txt
already exists? The existing file and all the information within it will be deleted, as a
new file with the same name is created.
Sometimes you would rather append new data to the end of an existing file. In that
case, an extra step is required. Replace line 18 with the following two lines:
FileWriterƒfwƒ=ƒnewƒFileWriter("bwbecker.txt",ƒtrue);
outƒ=ƒnewƒPrintWriter(fw);
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 466
466
OUTPUT
The first line constructs an object that opens the file so that new data will be appended LOOKING AHEAD
CHAPTER 9 | INPUT AND
to it. However, the FileWriter’s methods only write individual characters; we still Java’s I/O classes are
want to be able to use the print methods in PrintWriter. Fortunately, the two designed to work
together. More details
classes can work together to provide this capability.
in Section 9.7.
If your program uses these two lines but the specified file does not exist, a new file will
be created.
10ƒ002D9249ƒComputer 1595.99
5ƒ293E993CƒKeyboard 24.99
12ƒ0003922MƒMonitor 349.99
The two programs examined in this chapter so far read such files as lines of text. In
fact, text has a richer structure.
A file is a sequence of characters. The characters that are displayed visibly on the
screen include letters, numbers, and punctuation, such as y, M, 8, and ?. Each of these
is represented in the computer using a unique value.
Some characters are less obvious, such as spaces. They are represented on the screen as KEY IDEA
empty space. In the computer, however, they are represented by a value, just as M and y Every character, even
are represented by a value. For clarity, we will often show a space as a single dot in the spaces, corresponds
middle of the line ( . ). Most word processors have a similar feature to help users to a value.
Another less obvious character is the tab character. Like the space character, a tab is
also displayed by blank space. The length of that blank space, however, depends on a
number of factors. But no matter how long the space is, it is represented in the com-
puter as a single value. For clarity, we will show a tab character with a small arrow: →.
Finally, the end of a line is also represented by a character. The exact value used
depends on the computer’s operating system, and some use a sequence of two charac-
ters. Fortunately, the Scanner and PrintWriter classes allow us to ignore this detail
most of the time. We will refer to this character as the newline character. It is displayed
on the screen by moving the insertion point—the point where the next character is dis-
played—to the left side of the screen and down one line. For clarity, we will show the
newline character as a down and left arrow: ↵.
The space, tab, and newline characters are collectively known as whitespace because
they appear as white space when printed on a white sheet of paper.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 467
467
With these conventions, the three inventory records are shown as follows:
10.002D9249.Computer→1595.99↵
5.293E993C.Keyboard→24.99↵
12.0003922M.Monitor→349.99↵
u
KEY IDEA Lines of text are often divided into groups of characters called tokens. The characters
Lines are divided into that divide one token from the next are called delimiters. The most common delimiters
tokens separated by are white space characters. Using white space as delimiters, the previous lines each con-
delimiters. tain four tokens. Dividing the line into tokens enables us to obtain the information it
contains more flexibly.
The Scanner class provides methods to read a file token by token as well as line by
line. The next method will read the next token, returning it as a String. Calling
the next method on the inventory records will return, in order, the strings 10,
002D9249, Computer, and so on.
Another method, nextInt, will attempt to read the next token and convert it to an
integer before returning it. If the next token can’t be converted to an integer, nextInt
will throw an InputMismatchException. A third method, nextDouble, behaves
similarly except that it attempts to convert the next token to a double value. These
methods can be described as data acquisition methods because they are used to acquire
data from the file.
A program fragment that reads the inventory records and prints a simple report is
shown in Listing 9-3. It assumes a Scanner object named in has already been created.
Listing 9-3: A program fragment that reads the tokens in an inventory record
2 Actually, the end of the file is not a character in the same way that a space or newline is a character.
468
OUTPUT
CHAPTER 9 | INPUT AND
Listing 9-3: A program fragment that reads the tokens in an inventory record (continued)
As this program reads the file, the Scanner object maintains a cursor that marks its
position. The cursor divides the file into two parts: the part that has already been read,
and the part that has not. The cursor is positioned just before the first character when
(table 9-2)
the file is opened.
Tracing the partial
Table 9-2 traces part of the execution of the previous program. It shows the position of execution of the
the cursor with a diamond (♦) in the column labeled “Input.” program fragment in
Listing 9-3
♦10.002D9249.Computer→1595.99↵
2ƒwhileƒ(in.hasNextLine())ƒ
♦10.002D9249.Computer→1595.99↵
3ƒ{ƒintƒquantityƒ=ƒin.nextInt();ƒƒƒƒƒƒ
10♦.002D9249.Computer→1595.99↵ƒ 10ƒ
4ƒƒƒStringƒpartNumƒ=ƒin.next();ƒ
10.002D9249♦.Computer→1595.99↵ƒ 10 002D9249
5ƒƒƒStringƒdescriptionƒ=ƒin.next();
6ƒƒƒdoubleƒcostƒ=ƒin.nextDouble();
7ƒƒƒin.nextLine();ƒƒƒƒƒƒƒ
10ƒƒSystem.out.printf...
2ƒwhileƒ(in.hasNextLine())ƒƒƒƒƒƒƒ
469
3ƒ{ƒintƒquantityƒ=ƒin.nextInt();ƒƒƒƒƒƒƒ
The Scanner class contains methods to read and interpret the next token for many
types. They all behave essentially the same as nextInt:
➤ Skip delimiting characters.
➤ Examine the characters up to the next delimiter.
➤ If the examined characters can be interpreted as the specified type, move the cur-
sor beyond them and return the token as the specified type. If the characters can-
not be interpreted as the specified type, throw a InputMismatchException
and leave the cursor’s position unchanged.
KEY IDEA The exception is the nextLine method. It does not skip leading white space and
nextLine does not returns the rest of the line rather than a token.
skip leading white
space. Other next The most commonly used data acquisition methods in the Scanner class are shown in
methods do. Table 9-3. In this table, each method is followed by a description and examples.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 470
470
OUTPUT
(table 9-3)
CHAPTER 9 | INPUT AND
Data acquisition
methods in the
Scanner class
Method Description and Examples
intƒnextInt() Examines the next token in the input, skipping any leading delimiters. If the token can be
ƒƒ interpreted as an int, the cursor is moved past the token and the int value is returned.
Otherwise, an InputMismatchException is thrown and the cursor is not moved. Examples:
Please note that the last example contains a newline character ↵ in the middle of the line. A text
editor would show this as two lines.
doubleƒnextDouble() Like nextInt, but attempts to interpret the token as a double. Examples:
ƒƒ
Initial Situation Returns Final Situation
ABC♦..10.5.DEF↵ 10.5 ABC..10.5♦.DEF↵
ABC♦.-1.5E3.DEF↵ -1500.0 ABC.-1.5E3♦.DEF↵
ABC♦.10.DEF↵ 10.0 ABC.10♦.DEF↵
ABC♦.ten.DEF↵ Exception ABC♦.ten.DEF↵
booleanƒnextBoolean() Like nextInt, but attempts to interpret the token as a boolean. Examples:
ƒƒ
Initial Situation Returns Final Situation
ABC♦..true.DEF↵ true ABC..true♦.DEF↵
ABC♦.FALSE↵ false ABC.FALSE♦↵
ABC♦.truest.DEF↵ Exception ABC♦.truest.DEF↵
StringƒnextLine() Reads and returns as a String all the characters from the cursor up to the next newline
ƒƒ character or the end of the file, whichever comes first. Moves the cursor past the characters that
were read and the following newline, if there is one. nextLine does not skip leading delimiters.
Examples:
471
For an example of using a data availability method, consider again the Web server log.
Normally, the last token of the record is an integer specifying the size of the data
served. However, if the server encounters an error and cannot serve the requested data,
the log will contain a dash (-). If nextInt is called on such a record, an exception will
be thrown.
Listing 9-4: A code fragment to read individual tokens in a Web server’s log, accounting for either
an integer size or a dash (-) in the last token
1 ƒƒwhile(in.hasNextLine())
2 ƒƒ{ƒStringƒipAddressƒ=ƒin.next();
3 ƒƒƒƒStringƒhostNameƒ=ƒin.next();
4 ƒƒƒƒStringƒwhenƒ=ƒin.next();
5 ƒƒƒƒStringƒcmdƒ=ƒin.next();
6 ƒƒƒƒStringƒurlƒ=ƒin.next();
7 ƒƒƒƒintƒcompletionCodeƒ=ƒin.nextInt();
8
9 ƒƒƒƒ// Read the size of the served page. Set size to 0 if there was an error recorded.
10 ƒƒƒƒintƒsizeƒ=ƒ0;
11 ƒƒƒƒifƒ(in.hasNextInt())
12 ƒƒƒƒ{ƒsizeƒ=ƒin.nextInt();ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// Read the size.
13 ƒƒƒƒ}ƒelse
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 472
472
OUTPUT
CHAPTER 9 | INPUT AND
Listing 9-4: A code fragment to read individual tokens in a Web server’s log, accounting for either
an integer size or a dash (-) in the last token (continued)
An excellent way to address these issues is to write helper methods. An even better KEY IDEA
solution is to write a new class so that each record can be represented as an object. The Represent records as
helper methods go in that class. objects.
Instance variables in ServerRecord correspond to the fields in the record. Each field
is stored in the appropriate variable when it is read.
Note that the date and time from the record is stored as a DateTime object.
Furthermore, the DateTime class has a constructor taking a Scanner object as a para-
meter. This allows the ServerRecord constructor to quickly and easily delegate read-
ing the date and time to the DateTime class in line 25.
This technique assumes that the constructor is called with the Scanner’s cursor posi- KEY IDEA
tioned immediately before the record. When the constructor finishes executing, the The constructor
cursor must be immediately after the record, ready for the next record to be read. begins and ends with
the cursor at the
beginning of a record.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 473
473
The ServerRecord class also includes a method named write that writes the record to a
file in the same format in which it was read. This allows the program to read its own files.
write takes a PrintWriter object as its parameter. As with the reading of the file, the
responsibility for opening and closing the file rests with the calling code (see Listing 9-6).
474
OUTPUT
CHAPTER 9 | INPUT AND
Listing 9-6: Client code to read server records and write selected records to a file
ch09/processRecords/
1 importƒjava.io.*;
2 importƒjava.net.*;
3 importƒjava.util.Scanner;
4
5 /** Read a Web server's access log. Write selected records to a file.
6 *
7 * @author Byron Weber Becker */
8 publicƒclassƒReadServerRecords
9 {
10 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
11 ƒƒ{ƒ// Open the files.
12 ƒƒƒƒScannerƒinƒ=ƒnull;
13 ƒƒƒƒPrintWriterƒoutƒ=ƒnull;
14 ƒƒƒƒtryƒ
Open File for Input
15 ƒƒƒƒ{ƒinƒ=ƒnewƒScanner(newƒFile("server_log.txt"));ƒ Open File for Output
16 ƒƒƒƒƒƒoutƒ=ƒnewƒPrintWriter("largeFiles.txt");
17 ƒƒƒƒ}ƒcatchƒ(FileNotFoundExceptionƒex)ƒ
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 475
475
18 ƒƒƒƒ{ƒSystem.out.println(ex.getMessage());
19 ƒƒƒƒƒƒSystem.out.println("in "ƒ+ƒSystem.getProperty("user.dir"));
20 ƒƒƒƒƒƒSystem.exit(1);
21 ƒƒƒƒ}
22
23 ƒƒƒƒ// Read and process each record.
24 ƒƒƒƒwhileƒ(in.hasNextLine())
Process File 25 ƒƒƒƒ{ƒServerRecordƒsrƒ=ƒnewƒServerRecord(in);
26 ƒƒƒƒƒƒifƒ(sr.getSize()ƒ>=ƒ25000)
27 ƒƒƒƒƒƒ{ƒsr.write(out);
28 ƒƒƒƒƒƒ}
29 ƒƒƒƒ}
30
31 ƒƒƒƒ// Close the files.
32 ƒƒƒƒin.close();
33 ƒƒƒƒout.close();
34 ƒƒ}
35 }
KEY IDEA Many files are used by more than one program. For example, the Web server writes the
Every program using log file while various reporting programs read it. These programs need to agree on how
a file must agree on the file is organized: the order of the fields within the record, which delimiters are used
the file’s format. to separate tokens, and so on. The organization of the file is known as the file format.
To better appreciate the effect the file format has on the program, let’s consider again
the simple file format for the computer store inventory file. Recall that it had four
fields, as shown in the following example records:
10ƒ002D9249ƒComputer 1595.99
5ƒ293E993CƒKeyboard 24.99
12ƒ0003922MƒMonitor 349.99
476
OUTPUT
However, this code assumes that each field consists of a single token. If the description were
CHAPTER 9 | INPUT AND
LCDƒMonitor instead of simply Monitor, this would not work because in.next()
would read LCD. The call to nextDouble() would attempt to turn the string Monitor
into a double, and fail.
The simplest way to handle this change is to change the file format. By putting single KEY IDEA
token fields such as quantity, price, and part identifier first, and putting the multiple Simple changes to the
token field (description) last, the description can be read using nextLine; in other file format can make a
big difference in the
words, order the record as shown in the following example:
code that reads it.
12ƒ0003922Mƒ349.99ƒLCDƒMonitor
However, suppose that there is a second multiple token field, such as the name of the
supplier. If we simply add it on to the end of the record, we have no reliable way of
knowing where one field ends and the next begins unless we use a different delimiter that
does not appear in either field, such as a colon (:). This is shown in the following record:
12ƒ0003922Mƒ349.99ƒLCDƒMonitorƒ:ƒACMEƒComputerƒDistributors
Such a record could be read with code such as the following. It reads the description a
token at a time, building up the description until the delimiter is found. It then reads the
last multiple token field with nextLine, trimming off any leading or trailing blanks.
publicƒInventory3(Scannerƒin)
{ƒ// Code to read quantity, part identifier, and price is omitted
ƒƒthis.descriptionƒ=ƒ"";
ch09/fileFormat/
ƒƒStringƒtokenƒ=ƒin.next();
ƒƒwhileƒ(!token.equals(":"))
ƒƒ{ƒthis.descriptionƒ+=ƒ"ƒ"ƒ+ƒtoken;
ƒƒƒƒtokenƒ=ƒin.next();
ƒƒ}
ƒƒthis.distributorƒ=ƒin.nextLine().trim();
}
The Scanner class takes this idea one step further by allowing us to specify the delim-
iters it uses. If we replace each white space delimiter with a colon, for example, then
even multiword phrases are treated as a single token. Consider the following record:
12:M0003922:349.99:LCDƒMonitor:ACMEƒComputerƒDistributors:
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 477
477
9.3 USING
This record can be read by calling in.useDelimiter(“:”) immediately after open-
ing the file. The ServerRecord constructor can now read each of the tokens with a
THE
single call, as follows:
FILE CLASS
publicƒInventory4(Scannerƒin)
{ƒthis.quantityƒ=ƒin.nextInt();
ƒƒthis.partIDƒ=ƒin.next();
ch09/fileFormat/
ƒƒthis.priceƒ=ƒin.nextDouble();
ƒƒthis.descriptionƒ=ƒin.next();
ƒƒthis.distributorƒ=ƒin.next();
ƒƒin.nextLine();ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// Move to the next line of the file.
}
Because newline characters are no longer delimiters, the colon at the end of the record
and the call to nextLine are required.
KEY IDEA There is one more file format variation that bears mentioning: Simply place each mul-
Design the file format tiple token field like description and distributor on its own line. No law
to make your code requires a record to use only one line. This simple idea of placing each multiple token
easy to read, write, field on its own line helps keep both the file and the code easy to read, write, and
and understand.
understand. To make the file easier to read, you may want to place a blank line
between each pair of records.
9.3.1 Filenames
You can’t name a file anything you want because some characters are not allowed. The
Windows operating system, for example, does not allow a filename to contain any of
the following characters: \ / : * ? " < > |.
Filenames often have an extension, such as .txt. An extension is whatever follows the
last period in the name, and is often used to identify the kind of information stored in
the file. For example, a file with an extension of .html contains a Web page, whereas
a file with an extension of .jpg means it contains a graphic.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 478
478
OUTPUT
Modern computers use a hierarchical system for locating directories and files. The hier-
archy is depicted as an upside-down tree with branches, as shown in Figure 9-3.
Directories can contain either files (white) or other directories (green). For example,
the directory cs101 contains two other directories, A09 and A10. The directory A09
contains four files, including ServerRecord.java. AO9 also includes a directory,
logs, which includes three additional files.
(figure 9-3)
D:
Hierarchical file system in
which folder icons
represent directories and
Comm... cs101 cs241
boxes represent files
A09 A10
test_log.txt log_03.txt
An absolute path begins with the root of the tree (D:) and specifies all of the directories
between it and the desired file. For example, the hierarchy shown in Figure 9-3 con-
tains two files named Main.java. The following statement uses an absolute path to
specify one of them:
Fileƒfƒ=ƒnewƒFile("D:/cs101/A09/Main.java");
The directories in the path are separated with a special character, typically / (Unix and
Macintosh) or \ (Windows). Java will accept either, but / is easier because \ is Java’s
escape character for strings.
Files can also be specified with a relative path from the program’s working directory.
Suppose the current working directory is A09. A name without a prefix specifies a file
in that directory—for example, test_log.txt. You can also name a file in a subdi-
rectory of the working directory—for example, logs/log_01.txt. The special name
.. specifies the parent directory. The following statement uses a relative path to spec-
ify the initialization file in A10:
Fileƒinitƒ=ƒnewƒFile("../A10/explorer.ini");
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 479
479
9.3 USING
Relative paths are most useful when the program’s location and the file’s location are
related. If the program moves to a new location (such as submitting it electronically to
THE
be marked), the file should also move. Absolute paths are more useful when the loca-
FILE CLASS
tion of the file is independent of the location of the program using it.
Knowing your program’s working directory is a key to using relative paths effectively.
You can find it with the following statement:
System.out.println(System.getProperty("user.dir"));
The System class maintains a map of keys and properties for the running program.
The string “user.dir” is the key for the working directory property. Other keys
include “user.name” (the user’s account name); “os.name” (the computer’s operat-
ing system); and “line.separator” (the character or sequence of characters separat-
ing lines in a file, represented earlier with ↵).
The File constructor can have either an absolute or a relative path as its argument.
The resulting object represents a path to a file or a directory. The file or directory may
or may not exist.
A File object can both answer a number of useful questions about the path it repre-
sents and perform a number of operations on the file system. Some of these operations
are summarized in Table 9-4. Technically, a directory is a special kind of file. The
online documentation often uses “file” to refer to either; Table 9-4 does the same.
Summary of methods in booleanƒcanRead() Determines whether this program has permission to read
the File class from the file.
File getParentFile() Gets a File object representing this file’s parent directory.
Returns null if this file doesn’t have a parent.
480
OUTPUT
Summary of methods in
booleanƒ Determines whether the path specifies a directory.
the File class
ƒƒisDirectory()
This section and the next will discuss how to interact with the user of your program to
modify how the program behaves. As an example, we will modify the program in
Listing 9-6, which currently processes a server log, printing only those records result-
ing from serving a file larger than or equal to 25,000 bytes. Our new program will ask
the user which log file to process and the minimum served file size to report. In partic-
ular, we want to implement the following pseudocode:
Fortunately, the techniques we learned to read from a file can also be used to read
information from the console. We still use the Scanner class, but we construct the
Scanner object slightly differently, as follows:
Scannerƒcinƒ=ƒnewƒScanner(System.in);
System.in is an object similar to System.out. Scanner uses it to read from the con-
sole. Unlike opening a file, we are not required to catch any exceptions.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 481
481
9.4 INTERACTING
Before we implement the pseudocode discussed earlier, consider the sample program
shown in Listing 9-7. It illustrates the important elements of reading from the console.
The result of running this program is shown in Figure 9-4.
WITH
Listing 9-7: A short program demonstrating reading from the console
USERS
ch09/readConsole/
1 importƒjava.util.Scanner;
2
3 publicƒclassƒReadConsole
4 {
5 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
6 ƒƒ{ƒScannerƒcinƒ=ƒnewƒScanner(System.in);
7
8 ƒƒƒƒSystem.out.print("Enter an integer: ");
9 ƒƒƒƒintƒaƒ=ƒcin.nextInt();
10 ƒƒƒƒSystem.out.print("Enter an integer: ");
11 ƒƒƒƒintƒbƒ=ƒcin.nextInt();
12
13 ƒƒƒƒSystem.out.println(aƒ+ƒ" * "ƒ+ƒbƒ+ƒ"ƒ=ƒ"ƒ+ƒaƒ*ƒb);
14 ƒƒ}
15 }
The program begins by creating a new Scanner object used to read from the console.
It’s named cin, short for “console input.”
KEY IDEA At lines 8 and 10, the program prints a prompt for the user. The prompt informs the
Prompt the user when user that input is expected. In Figure 9-4, the user responded to the first prompt with
input is expected. 3↵. That is, the user entered the digit 3 and the Enter key. The Enter key is the user’s
cue to the program that it should read the input and process it. The program waits to
read the input until Enter is pressed. The online documentation uses the term block,
which means to wait for input.
(figure 9-4)
482
OUTPUT
(figure 9-5)
CHAPTER 9 | INPUT AND
The program can be protected from such errors with the code shown in Listing 9-8.
The loop in lines 9–19 verifies that the next token is an integer. If it is not, the program
reads it and displays a helpful message. This action gives the user the opportunity to try
again. When an integer is entered, it is read in line 12, and the loop ends with the
break in line 14.
LOOKING BACK
The need to repeat essentially identical code to read the second integer suggests that a
Class methods
method should be written. Such a method does not rely on any instance variables and were discussed in
can therefore be a class method. In fact, a whole set of similar methods will be needed. Section 7.5.2
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 483
483
9.4 INTERACTING
We can place them in a class named Prompt and call them as shown in the following
example:
WITH
KEY IDEA Listing 9-9 shows the beginning of the class. Notice that line 9 declares a static
USERS
The Prompt class (class) variable used to read from the console. One consequence of this decision is that
must be used for all of all input from the console must be obtained with the methods in this class. Using more than
the console input—or one Scanner object to read from the same source (that is, System.in) will not work reli-
none of it.
ably.
LOOKING AHEAD Listing 9-9 also includes the methods forInputFile and forInputScanner. The
The problem set will first method uses the File class to verify that a string entered by the user specifies a file
ask you to add to this that exists and can be read by this program. The second method uses the first to open
library. the specified file using Scanner. Putting this code in its own class has the following
advantages:
➤ We can avoid writing it anew for each program that asks the user for a file or
integer to process.
➤ We can put the try-catch statement here, rather than cluttering the main
program with it.
➤ If the methods need enhancing or debugging, there is only one place that
requires attention.
484
OUTPUT
CHAPTER 9 | INPUT AND
Listing 9-9: A class providing error-checked reading of an integer and a filename (continued)
20 ƒƒƒƒƒƒƒƒreturnƒanswer;
21 ƒƒƒƒƒƒ}ƒelse
22 ƒƒƒƒƒƒ{ƒStringƒinputƒ=ƒPrompt.in.nextLine();
23 ƒƒƒƒƒƒƒƒSystem.out.println("Error: "ƒ+ƒinput
24 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ+ƒ" not recognized as an integer such as '10' or '-3'.");
25 ƒƒƒƒƒƒ}
26 ƒƒƒƒ}
27 ƒƒ}
28
29 ƒƒ/** Prompt the user for a file to use as input.
30 ƒƒ* @param prompt The prompting message for the user.
31 ƒƒ* @return A File object representing a file that exists and is readable. */
32 ƒƒpublicƒstaticƒFileƒforInputFile(Stringƒprompt)
33 ƒƒ{ƒwhileƒ(true)
34 ƒƒƒƒ{ƒSystem.out.print(prompt);
35 ƒƒƒƒƒƒStringƒnameƒ=ƒin.nextLine().trim();
36 ƒƒƒƒƒƒFileƒfƒ=ƒnewƒFile(name);
37 ƒƒƒƒƒƒifƒ(!f.exists())
38 ƒƒƒƒƒƒ{ƒSystem.out.println("Error: "ƒ+ƒnameƒ+ƒ" does not exist.");
39 ƒƒƒƒƒƒ}ƒelseƒifƒ(f.isDirectory())
40 ƒƒƒƒƒƒ{ƒSystem.out.println("Error: "ƒ+ƒnameƒ+ƒ" is a directory.");
41 ƒƒƒƒƒƒ}ƒelseƒifƒ(!f.canRead())
42 ƒƒƒƒƒƒ{ƒSystem.out.println("Error: "ƒ+ƒnameƒ+ƒ" is not readable.");
43 ƒƒƒƒƒƒ}ƒelse
44 ƒƒƒƒƒƒ{ƒreturnƒf;
45 ƒƒƒƒƒƒ}
46 ƒƒƒƒ}
47 ƒƒ}
48 ƒƒ
49 ƒƒ/** Prompt the user for a file to use as input.
50 ƒƒ* @param prompt The prompting message for the user.
51 ƒƒ* @return A Scanner object ready to read the file specified by the user. */
52 ƒƒpublicƒstaticƒScannerƒforInputScanner(Stringƒprompt)
53 ƒƒ{ƒtryƒ
54 ƒƒƒƒ{ƒreturnƒnewƒScanner(Prompt.forInputFile(prompt));
55 ƒƒƒƒ}ƒcatchƒ(FileNotFoundExceptionƒex)ƒ
56 ƒƒƒƒ{ƒ// Shouldn't happen, given the work we do in forInputFile.
57 ƒƒƒƒƒƒSystem.out.println(ex.getMessage());
58 ƒƒƒƒƒƒSystem.exit(1);
59 ƒƒƒƒ}
60 ƒƒƒƒreturnƒnull;ƒ// for the compiler
61 ƒƒ}
62 }
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 485
485
9.4 INTERACTING
Using Prompt
The completed program for interacting with the user to ask for a specific Web server
log file to process and the minimum size of returned page to print in a report is shown
WITH
in Listing 9-10. Notice that it uses the Prompt class in lines 11 and 14.
USERS
Listing 9-10: A program that processes a Web server log based on user input
ch09/userIO/
1 importƒjava.util.Scanner;
2
3 /** List files in a user-specified Web server log that meet a minimum size criteria.
4 * Report the number of files that are printed.
5 *
6 * @author Byron Weber Becker */
7 publicƒclassƒListFilesBySizeƒ
8 {
9 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
10 ƒƒ{ƒ// Prompt for the file to process.
11 ƒƒƒƒScannerƒinƒ=ƒPrompt.forInputScanner("Web server log name: ");
12
13 ƒƒƒƒ// Get the minimum size from the user.
14 ƒƒƒƒintƒminSizeƒ=ƒPrompt.forInt("Minimum served file size: ");
15
16 ƒƒƒƒ// Process the files.
17 ƒƒƒƒintƒcountƒ=ƒ0;
18 ƒƒƒƒwhileƒ(in.hasNextLine())
19 ƒƒƒƒ{ƒServerRecordƒsrƒ=ƒnewƒServerRecord(in);
20 ƒƒƒƒƒƒifƒ(sr.getSize()ƒ>=ƒminSize)
21 ƒƒƒƒƒƒ{ƒSystem.out.println(sr.toString());
22 ƒƒƒƒƒƒƒƒcount++;
23 ƒƒƒƒƒƒ}
24 ƒƒƒƒ}
25
26 ƒƒƒƒ// Close the input file and report the count.
27 ƒƒƒƒin.close();
28 ƒƒƒƒSystem.out.println(countƒ+ƒ" files served were at least "ƒ
29 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ+ƒminSizeƒ+ƒ" bytes.");
30 ƒƒ}
31 }
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 486
486
OUTPUT
The techniques for interacting with users shown in the previous section are adequate if
the user must always enter the same information in the same order each time the pro-
gram is run. There are many programs, however, for which more flexibility is desired.
One way to achieve more flexibility (without the work of implementing a graphical
user interface) is to write a command interpreter. A command interpreter repeatedly
waits for the user to enter a command, and then it executes the command.
Furthermore, the structure of the program will make it easy to add functionality. An
example of running the program is shown in Figure 9-6. The prompt for a command is >.
Information required by the command is entered on the same line. As you can see, com-
mands are usually terse.
(figure 9-6)
487
Unfortunately, a pair of commands like host and host <string> complicates matters.
The problem lies with determining whether the user has entered a string following the
host command. It would seem that calling hasNext() would easily resolve that ques-
tion, but it doesn’t—Scanner will keep looking up to the end of the “file” to see if
there is a token present. But when scanning System.in (the console), there is no end
of file. Scanner waits for whatever the user types in next (ignoring white space,
including Enter). When the user does type something, Scanner returns true.
hasNextLine has similar issues.
We can solve this problem by reading the input a line at a time—but then we have to
find out what is on the line, as indicated by the following pseudocode:
KEY IDEA Now there is the problem of extracting the information on the line to find the com-
The Scanner class mand (such as host, min, or p) and the argument (such as googlebot.com or 1500000),
can also process if there is one. Fortunately, Scanner can scan strings in addition to files. For example,
strings. the following code will print “hostƒtrueƒgooglebot.com”.
Scannerƒsƒ=ƒnewƒScanner("hostƒgooglebot.com");
System.out.print(s.next());
System.out.print(s.hasNext());
System.out.print(s.next());
Scannerƒsƒ=ƒnewƒScanner("host");
System.out.print(s.next());
System.out.print(s.hasNext());
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 488
488
OUTPUT
By creating a Scanner for each line we read, we can determine exactly what is on it.
CHAPTER 9 | INPUT AND
Scannerƒcinƒ=ƒnewƒScanner(System.in);
whileƒ(the quit command has not been received)
{ƒSystem.out.print(">ƒ");
ƒƒStringƒlineƒ=ƒcin.nextLine();
ƒƒScannerƒlineScannerƒ=ƒnewƒScanner(line);
ƒƒStringƒcmdƒ=ƒlineScanner.next();
ƒƒifƒ(cmd is "host" and line has another token)
ƒƒ{ƒremember given hostname for next searchƒ
ƒƒ}ƒelseƒifƒ(cmd is "host")
ƒƒ{ƒnext search will be for all hosts
ƒƒ}ƒelseƒifƒ(cmd is "min" and line has an integer)
ƒƒ{ƒremember given minimum size for the next search
ƒƒ}ƒelseƒifƒ(cmd is "p" and line has another token)
ƒƒ{ƒprocess the given file with the settings given by previous commands
ƒƒ...
ƒƒ}ƒelse
ƒƒ{ƒerror message
ƒƒ}
}
These ideas are implemented in the class shown in Listing 9-11. The command inter-
preter is at lines 23–31; it delegates the task of executing the commands to executeCmd,
lines 35–59.
The heart of the actual application is the processFile method. It’s overloaded, with
one method taking a String parameter (the filename) and another taking a Scanner
object. The first one handles the messy details of opening the file and then calls the sec-
ond one, which actually does the work. It reads each record in the log, printing and
counting those that match the criteria. The task of deciding which records match is del-
egated to includeRecord.
LogExplorer works as shown, but the design could be improved by separating the
user interface from the rest of the program. Section 9.5.3 explains how.
489
490
OUTPUT
CHAPTER 9 | INPUT AND
Listing 9-11: The LogExplorer program with an integrated command interpreter (continued)
52 ƒƒƒƒƒƒ{ƒthis.displayRecƒ=ƒline.nextBoolean();
53 ƒƒƒƒƒƒ}ƒelseƒifƒ(cmd.equals("dn")ƒ&&ƒline.hasNextBoolean())
54 ƒƒƒƒƒƒ{ƒthis.displayNumƒ=ƒline.nextBoolean();
55 ƒƒƒƒƒƒ}ƒelse
56 ƒƒƒƒƒƒ{ƒSystem.out.println("Command '"ƒ+ƒlineƒ+ƒ"' not recognized.");
57 ƒƒƒƒƒƒ}
58 ƒƒƒƒ}
59 ƒƒ}
60
61 ƒƒ/** Process a log file via the specified Scanner object. Display and count each record that
62 ƒƒ* matches the criteria set previously.
63 ƒƒ* @param in A scanner for the input file to process. */
64 ƒƒprivateƒvoidƒprocessFile(Scannerƒin)
65 ƒƒ{ƒintƒcountƒ=ƒ0;
66 ƒƒƒƒwhileƒ(in.hasNextLine())
67 ƒƒƒƒ{ƒServerRecordƒsrƒ=ƒnewƒServerRecord(in);
68 ƒƒƒƒƒƒifƒ(this.includeRecord(sr))
69 ƒƒƒƒƒƒ{ƒifƒ(this.displayRec)
70 ƒƒƒƒƒƒƒƒ{ƒthis.displayRecord(sr);
71 ƒƒƒƒƒƒƒƒ}
72 ƒƒƒƒƒƒƒƒcount++;
73 ƒƒƒƒƒƒ}
74 ƒƒƒƒ}
75 ƒƒƒƒifƒ(this.displayNum)
76 ƒƒƒƒ{ƒthis.displayCount(count);
77 ƒƒƒƒ}
78 ƒƒ}
79
80 ƒƒ/** Process the specified file.
81 ƒƒ* @param fName The name of the file to process. */
82 ƒƒprivateƒvoidƒprocessFile(StringƒfName)
83 ƒƒ{ƒScannerƒinƒ=ƒnull;
84 ƒƒƒƒtry
85 ƒƒƒƒ{ƒinƒ=ƒnewƒScanner(newƒFile(fName));
86 ƒƒƒƒƒƒthis.processFile(in);
87 ƒƒƒƒƒƒin.close();
88 ƒƒƒƒ}ƒcatchƒ(FileNotFoundExceptionƒex)
89 ƒƒƒƒ{ƒƒSystem.err.println("Can't find file "ƒ+ƒ
90 ƒƒƒƒƒƒƒƒƒƒSystem.getProperty("user.dir")ƒ+ƒ"/"ƒ+ƒfNameƒ+ƒ".");
91 ƒƒƒƒ}
92 ƒƒ}
93
94 ƒƒ/** Determine whether a record should be included in the report. Include records that meet
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 491
491
95 ƒƒ* ALL the specified criteria. If a criterion wasn't set (for example, no client host name was
96 ƒƒ* specified), we need a way to ignore it, typically with an "or" condition. */
97 ƒƒprivateƒbooleanƒincludeRecord(ServerRecordƒsr)
98 ƒƒ{ƒreturnƒ(this.searchHost.length()ƒ==ƒ0ƒ||ƒƒ
99 ƒƒƒƒƒƒƒƒƒƒƒƒsr.hostnameContains(this.searchHost))ƒ
100 ƒƒƒƒƒƒƒƒƒƒƒƒ&&ƒsr.getSize()ƒ>=ƒthis.minSize;
101 ƒƒ}
102
103 ƒƒ/** Display one record to the user.
104 ƒƒ* @param sr The record to display. */
105 ƒƒprivateƒvoidƒdisplayRecord(ServerRecordƒsr)
106 ƒƒ{ƒSystem.out.printf("%-15s %s%n",ƒ
107 ƒƒƒƒƒƒƒƒƒƒsr.getIPAddress(),ƒsr.getClientHostname());
108 ƒƒƒƒSystem.out.printf(" %5d%10d%5s %s%n",ƒ
109 ƒƒƒƒƒƒƒƒƒƒsr.getCompletionCode(),ƒsr.getSize(),
110 ƒƒƒƒƒƒƒƒƒƒsr.getCommand(),ƒsr.getURL());
111 ƒƒ}
112
113 ƒƒ/** Display the number of matching records.
114 ƒƒ* @param count The number of matching records to display. */
115 ƒƒprivateƒvoidƒdisplayCount(intƒcount)
116 ƒƒ{ƒSystem.out.println();
117 ƒƒƒƒSystem.out.println(countƒ+ƒ" records met the search criteria.");
118 ƒƒ}ƒ
119
120 ƒƒ/** Display a help message. */
121 ƒƒprivateƒvoidƒdisplayHelp()
122 ƒƒ{ƒfinalƒStringƒhelpFmtƒ=ƒ" %-14s %s%n";
123 ƒƒƒƒfinalƒPrintStreamƒoutƒ=ƒSystem.out;
124 ƒƒƒƒout.println("General Commands:");
125 ƒƒƒƒout.printf(helpFmt,ƒ"q",ƒ"Quit");
126 ƒƒƒƒout.printf(helpFmt,ƒ"help",ƒ"Display this help message");
127 ƒƒƒƒout.printf(helpFmt,ƒ"p <string>",ƒ"Process specified file");
128 ƒƒƒƒout.println();
129 ƒƒƒƒout.println("Commands that affect which records are included:");
130 ƒƒƒƒout.printf(helpFmt,ƒ"host <string>",ƒ"Hostnames including...");
131 ƒƒƒƒout.printf(helpFmt,ƒ"host",ƒ"All client hostnames");
132 ƒƒƒƒout.printf(helpFmt,ƒ"url <string>",ƒ"Requested URLs including...");
133 ƒƒƒƒout.printf(helpFmt,ƒ"url",ƒ"All URLs");
134 ƒƒƒƒout.printf(helpFmt,ƒ"min <int>",ƒ"Served pages with a minimum size");
135 ƒƒƒƒout.println();
136 ƒƒƒƒout.println("Commands that affect how records are displayed:");
137 ƒƒƒƒout.printf(helpFmt,ƒ"dn <boolean>",ƒ"Display number of records");
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 492
492
OUTPUT
CHAPTER 9 | INPUT AND
Listing 9-11: The LogExplorer program with an integrated command interpreter (continued)
The LogExplorer program shown in Listing 9-10 combines the code that solves the LOOKING BACK
problem (the model) with the code that interacts with the user (the user interface, com- Models, views, and
posed of a view and controller). By correctly separating these two aspects of the pro- controllers were
discussed in
gram, we can achieve the following benefits:
Section 8.6.2.
➤ Separating these aspects makes the program easier to understand. In all but the
simplest programs, it is easier to understand a program when each class has a
specific focus. In this case, the model should focus on determining which records
to display, and the user interface should focus on interacting with the user.
➤ Separating these aspects enables attaching different user interfaces to the
model without changing the model. For example, an experienced user may use
the program with a terse command interpreter that allows him to work
quickly. An inexperienced user may use the program with a command inter-
preter that reads a command and then prompts for additional information.
When a graphical user interface becomes available, the command interpreter
can be replaced by it without changing the model.
We can begin by reviewing Listing 9-11 and identifying the parts that have to do with
communicating with the user. They include the following:
➤ The cmdInterpreter method in lines 22–31
➤ The executeCmd method in lines 33–59
➤ The displayRecord method in lines 103–111
➤ The displayCount method in lines 113–118
➤ The displayHelp method in lines 120–140
➤ The processFile method’s error message at lines 89–91
➤ The other processFile method’s two if statements that determine whether
each record or the total number of records is displayed to the user (lines 69
and 75)
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 493
493
All of these elements will be moving to a new class named CmdInterpreter. With
these parts gone, there won’t be much left to the model (LogExplorer).
For these two classes to work together, LogExplorer will need to call the
displayRecord and displayCount methods in CmdInterpreter. Similarly, the
CmdInterpreter class will need to call methods such as processFile in the
LogExplorer class. This implies that we need to set the classes up as shown in Figure 9-7.
(figure 9-7)
LogExplorer CmdInterpreter
Structuring the -CmdInterpreter ui -LogExplorer model
LogExplorer program +LogExplorer( ) +CmdInterpreter(LogExplorer aModel)
+void addView(CmdInterpreter aUI) +void displayRecord(ServerRecord sr)
+void processFile(Scanner in) +void displayCount(int matches)
... ...
LOOKING BACK However, implementing this class diagram will not allow us to change user interfaces,
Interfaces were as we claimed earlier. With this implementation, the only user interface that can be
discussed in used with LogExplorer is a class named CmdInterpreter. To fix this, we will define
Section 7.6.
a Java interface declaring the methods displayRecord and displayCount. If
LogExplorer uses this interface, then any user interface that implements it can be
used with LogExplorer. Therefore, the high-level class diagram for the program will
be the one shown in Figure 9-8.
(figure 9-8)
LogExplorerView
Structuring the
LogExplorer program +void displayRecord(ServerRecord sr)
with a Java interface +void displayCount(int matches)
LogExplorer CmdInterpreter
-Log ExplorerView ui -LogExplorer model
+LogExplorer( ) +CmdInterpreter(LogExplorer aModel)
+void addView(LogExplorerView v) +void displayRecord(ServerRecord sr)
+void processFile(Scanner in) +void displayCount(int matches)
... ...
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 494
494
OUTPUT
The program can be set up with these relationships using the following code fragments.
CHAPTER 9 | INPUT AND
The main method has the responsibility to create both the LogExplorer and the
CmdInterpreter objects, as follows:
1 publicƒstaticƒvoidƒmain(String[]ƒargs)
2 {ƒLogExplorerƒexplorerƒ=ƒnewƒLogExplorer();
3 ƒƒCmdInterpreterƒcmdƒ=ƒnewƒCmdInterpreter(explorer);
4
5 ƒƒcmd.cmdInterpreter();ƒƒƒƒƒƒƒƒ// Run the command interpreter.
6 }
Finishing Up
Just a little bit of cleanup remains to complete the reorganization of the LogExplorer LOOKING AHEAD
program. Programming Project
9.10 asks you to
First, the original user interface simply set the searchHost and minSize instance complete the
variables directly. Now that these variables and the code that sets them are in different implementation.
classes, we’ll need to add some methods to LogExplorer to give appropriate access to
the variables. The methods, of course, are called from the user interface.
Second, the processFile method cannot remain private. The user interface will
need to call it at the appropriate times.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 495
495
KEY IDEA A better solution is to place reusable code—such as ServerRecord and Prompt—into
Most programming a library. A library is a collection of resources that are meant to be used in many dif-
languages have a way ferent programs. The concept of a library is implemented in Java with packages. A
to make a library of package is a collection of related classes that may contain subpackages. The classes in
reusable code. In
Java, it’s done with
becker.robots constitute a library, as do the classes in becker.util and
packages. java.awt. A program accesses the classes in a package with the import statement.
Understanding how programs are compiled and run is important background for
understanding how to use packages. Integrated development environments often hide
these details, so we begin with a brief tour of compiling and running programs using a
command line.
Most computers have a way to run programs from a command line. A simple program
(one that uses only imports from the java and javax packages or no imports at all)
can be compiled and run, as shown in Figure 9-9. The example is ListFilesBySize,
taken from Listing 9-10 with two minor changes. First, the ServerRecord class has
been modified to use a String instead of the DateTime class (to simplify the first
example; it will be put back afterwards). Second, the program itself has been modified
to print only the number of matching records to reduce the amount of output.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 496
496
OUTPUT
(figure 9-9)
CHAPTER 9 | INPUT AND
A single dot (.) is an abbreviation for the current working directory. Therefore, a more
succinct replacement for –classpath D:/cs101/ch09/userIO/ is –classpath..
With this background, we are now ready to return to understanding how to easily
reuse classes by placing them in packages.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 497
497
The package statement places a class into a named package. For example, the Robot
class begins with the following statement:
packageƒbecker.robots;
Classes in the becker.robots package can be used by including the familiar state-
ment importƒbecker.robots.*;.
The package name should be unique. The recommended way to make it unique is to
reverse your e-mail address. The author could use the reverse of bwbecker@
learningwithrobots.com, as shown in the following package statement:
packageƒcom.learningwithrobots.bwbecker;
The package statement must be the first statement in the class, before any import
statements or the class statement. However, comments may come before the
package statement.
The package statement places constraints on the location of the file containing the
source code. With the above package statement, the ServerRecord class must be
located in the file com/learningwithrobots/bwbecker/ServerRecord.java.
Notice that this path and name has three parts:
➤ The package name, but with the dots replaced with the directory separator
character /
➤ The name of the class, ServerRecord
➤ The extension, .java
This is shown in Figure 9-10. Notice that the path beginning with com/learn… is not
the entire path. We will need to tell the compiler about the first part, D:/cs101/.
(figure 9-10)
498
OUTPUT
In summary, compared to the program compiled in Figure 9-9, we need to make the
CHAPTER 9 | INPUT AND
following changes:
➤ Add a package statement to ServerRecord.java and a similar one to
Prompt.java.
➤ Move ServerRecord.java and Prompt.java to the locations specified by
their new package statements. The location of ServerRecord.java is
shown in Figure 9-10.
➤ Modify ListFilesBySize.java to import the classes in the new package.
(figure 9-11)
Compiling the
ListFilesBySize
program using packages
499
You may remember that we simplified ServerRecord somewhat to avoid using the
DateTime class. To address that issue, assume that ServerRecord once again
imports and uses becker.util.DateTime.
The complication is that we don’t have the source code to DateTime like we have for
ServerRecord. The .class files for DateTime, the robot classes, and many others
are all stored in a single file named becker.jar. Storing them all in a .jar file makes
them easier to distribute to other people. The .jar extension means Java Archive.
The javac command will need those .class files to compile the program. We tell it
where to look for them by adding the –classpath option. Likewise, the java com-
mand will need the classes to run the program. Therefore, we must add the .jar file to
its classpath as well. All this is shown in Figure 9-12. The .jar file is in the directory
D:/cs101/.
(figure 9-12)
We may also want to put our own classes into a .jar file to make them easier to man-
age. This is done with a command named jar, as shown in Figure 9-13.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 500
500
OUTPUT
(figure 9-13)
CHAPTER 9 | INPUT AND
The crucial differences from what we’ve done before are as follows:
➤ cd changes to the directory we’ve been using for the source and class path for
our package, D:/cs101.
➤ jar creates a new .jar file named cs101Lib.jar. The .class files to put
into it are specified with the last part of the command, *.class. The asterisk
(*) means to include every file ending in .class. In this situation, that would
be ServerRecord.class and Prompt.class.
➤ javac and java now use class paths that include the new .jar file instead of
D:/cs101/.
501
Take Scanner as an example. Scanner is an input stream because the program uses it
to get input from a source. It reads information as characters, either from a user or a
file, and is thus a character stream. Finally, Scanner is a processing stream because it
processes consecutive characters in the stream into higher-level constructs, such as an
entire string or an integer. To do this, it makes use of an underlying provider stream.
When we use Scanner to read from the console, for example, the provider stream is
System.in.
PrintWriter is categorized much like Scanner except that it is an output stream that
writes characters rather than bytes. It, too, is a processing stream because it processes
higher-level constructs into the individual characters it writes out. Like Scanner, it uses
an underlying provider stream to do the actual writing.
The philosophy of the Java I/O (input/output) library is that each class should do one
thing well, and that each class should combine easily with other classes to obtain the
strengths of both. Covering all the possibilities is beyond the scope of this textbook.
Instead, we will provide an orientation and direct interested readers to other sources,
such as the online Java Tutorial at http://java.sun.com/docs/books/tutorial/index.html.
The core class for character input is Reader. It’s core method is read, which reads one
or more characters from the source. Reader is extended to form four provider streams,
corresponding to four different kinds of sources:
➤ FileReader: reads characters from a file
➤ StringReader: reads characters from a String; ultimately, this allows you
to use next, nextInt, and similar methods in Scanner to read information
from a String as easily as you can from a file.
➤ CharArrayReader: similar to StringReader
➤ PipedReader: reads characters that are produced by another thread in the
program
Using these provider streams directly is not very easy. All they really provide is the abil-
ity to read a sequence of characters. A number of processing streams are provided to
help form those characters into useful information. To use a processing stream, you
must first construct a provider stream for it to use. Then, pass the provider stream to
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 502
502
OUTPUT
stream that has a method to read an entire line of characters at once. It could be used
like this:
The processing streams that work with character input streams include:
➤ BufferedReader: reads an entire line of characters at once
➤ LineNumberReader: reads an entire line at once and provides a count of the
lines read so far
➤ PushbackReader: allows a program to read some characters, examine them,
and then push them back on the stream to be read again at a later time
➤ Scanner: divides the input stream into tokens and provides conversion of
each token into appropriate types, such as int
Scanner is the most sophisticated of the processing streams. As we have already seen,
it provides many methods to convert the raw stream of characters into useful informa-
tion. It also has some convenience constructors to make it easier to use. For example,
one constructor takes a string as an argument and automatically uses it to construct a
StringReader to use as the source. Another constructor takes a File object as an
argument and automatically constructs a FileReader to use as the source.
There are many similarities between character input streams and character output
streams. Just as Reader is the core class for input, Writer is the core class for output,
providing a write method to write one or more characters to the sink. It is extended
four times, each class corresponding to four different kinds of sinks. The four sub-
classes are FileWriter, StringWriter, CharArrayWriter, and PipedWriter. A
FileWriter writes characters to a file, of course. A StringWriter appends the char-
acters to a string.
Only two interesting processing streams are associated with character output streams.
One is BufferedWriter, which collects a large number of characters and then writes
them all at once using its provider stream. BufferedWriter is typically combined
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 503
503
The other interesting processing stream is PrintWriter, which has already been dis-
cussed. It adds methods such as print, println, and printf to convert types such as
int and double into individual characters.
The structure of the byte input streams is similar to character input streams. A core
class, InputStream, is extended four times to provide bytes from files, string buffers,
byte arrays, and pipes. A similar set of classes provide processing streams to buffer the
stream, count the lines, and push back information previously read.
Similarly, the byte output streams mirror the character output streams. The core class,
OutputStream, is extended to write bytes to files, byte arrays, and pipes.
BufferedOutputStream and PrintStream are analogous to BufferedWriter and
PrintWriter.
(figure 9-14)
504
OUTPUT
Using a professional dialog box to obtain a filename from a user is easy, thanks to the
libraries that come with Java. The test program in Listing 9-12 displays a dialog box
like the one shown in Figure 9-14.
In lines 13–14, the dialog box is created and shown to the user. The method
showOpenDialog is meant for opening existing files. Another method, showSaveDialog,
is for saving a file. The difference is the text placed on the buttons and the title bar.
505
chooser.addChoosableFileFilter(
ƒƒƒƒƒƒƒƒƒƒnewƒFileExtensionFilter(".jpg",ƒ"jpg Graphics Files"));
chooser.addChoosableFileFilter(
ƒƒƒƒƒƒƒƒƒƒnewƒFileExtensionFilter(".gif",ƒ"gif Graphics Files"));
Listing 9-13: A filter used by JFileChooser to show only files with the specified extension
ch09/fileChooser/
1 importƒjavax.swing.filechooser.FileFilter;
2 importƒjava.io.File;
3
4 /** A class used to filter out some files so that JFileChooser only shows files with a
5 * specified extension.
6 *
7 * @author Byron Weber Becker */
8 publicƒclassƒFileExtensionFilterƒextendsƒFileFilter
9 {
10 ƒƒprivateƒStringƒext;
11 ƒƒprivateƒStringƒdescr;
12
13 ƒƒ/** Accept files ending with the given extension.
14 ƒƒ* @param extension The extension to accept (e.g., ".jpg")
15 ƒƒ* @param description A description of the file accepted */
16 ƒƒpublicƒFileExtensionFilter(Stringƒextension,ƒ
17 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒStringƒdescription)
18 ƒƒ{ƒsuper();
19 ƒƒƒƒthis.extƒ=ƒextension.toLowerCase();
20 ƒƒƒƒthis.descrƒ=ƒdescription;
21 ƒƒ}
22
23 ƒƒ/** Decide whether or not the given file should be displayed. In our case, include
24 ƒƒ* directories as well as files with a name ending in the specified extension.
25 ƒƒ* @param f A description of one file.
26 ƒƒ* @return True if the file should be displayed to the user; false otherwise. */
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 506
506
OUTPUT
CHAPTER 9 | INPUT AND
Listing 9-13: A filter used by JFileChooser to show only files with the specified extension
(continued)
27 ƒƒpublicƒbooleanƒaccept(Fileƒf)
28 ƒƒ{ƒreturnƒf.isDirectory()ƒ||ƒ
29 ƒƒƒƒƒƒƒƒƒƒƒf.getName().toLowerCase().endsWith(this.ext);
30 ƒƒ}
31
32 ƒƒ/** Return the description of the files accepted.
33 ƒƒ* @return A description of the files this filter accepts.*/
34 ƒƒpublicƒStringƒgetDescription()
35 ƒƒ{ƒreturnƒthis.descr;
36 ƒƒ}
37 }
The program in Listing 9-12 can be easily modified to display an image from a file. The
program currently prints the name of the file selected by the user (see lines 18–19). The
new program will replace lines 18–19 with the following code to create a component
that displays an image it reads from a file, and then shows that component in a frame.
Notice that the filename is obtained from the chooser and passed to ImageComponent’s
constructor.
The source code for ImageComponent is more interesting and is shown in Listing 9-14.
It is passed the name of the image file via the parameter in the constructor. A class from
the Java library, ImageIcon, is used to read the image from the file. Supported types of
images include .gif, .jpg, and .png. Once the image is loaded, the preferred size for
the component is set to the image’s size. If the preferred size is not set, the JFrame will
make it so small that it can’t be seen.
507
Listing 9-14: A new kind of component that displays an image from a file
ch09/displayImage/
1 importƒjava.awt.*;
2 importƒjavax.swing.*;
3
4 /** A component that paints an image stored in a file.
5 *
6 * @author Byron Weber Becker */
7 publicƒclassƒImageComponentƒextendsƒJComponent
8 {
9 ƒƒprivateƒImageIconƒimage;
10
11 ƒƒ/** Construct the new component.
12 ƒƒ* @param fileName The file where the image is stored. */
13 ƒƒpublicƒImageComponent(StringƒfileName)
14 ƒƒ{ƒsuper();
15 ƒƒƒƒthis.imageƒ=ƒnewƒImageIcon(fileName);
16 ƒƒƒƒthis.setPreferredSize(newƒDimension(
17 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒthis.image.getIconWidth(),ƒ
18 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒthis.image.getIconHeight()));
19 ƒƒ}
20
21 ƒƒ/** Paint this component, including its image. */
22 ƒƒpublicƒvoidƒpaintComponent(Graphicsƒg)
23 ƒƒ{ƒsuper.paintComponent(g);
24 ƒƒƒƒg.drawImage(this.image.getImage(),ƒ0,ƒ0,ƒnull);
25 ƒƒ}
26 }
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 508
508
OUTPUT
9.9 Patterns
CHAPTER 9 | INPUT AND
Solution: Open the file and use Scanner to obtain the individual tokens within the file.
The following template applies:
Scannerƒ«in»ƒ=ƒnull;
try
{ƒ«in»ƒ=ƒnewƒScanner(newƒFile(«fileName»));
}ƒcatchƒ(FileNotFoundExceptionƒex)
{ƒSystem.out.println(ex.getMessage());
ƒƒSystem.out.println("in "ƒ+ƒSystem.getProperty("user.dir"));
ƒƒSystem.exit(-1);
}
«statementsƒtoƒreadƒfile»
«in».close();
Consequences: The file is opened for reading. If the file does not exist, an exception is
thrown and the program stops.
Related Patterns:
➤ The Open File for Output pattern is used to write information to a file.
➤ The Process File pattern depends on this pattern to open the file.
Context: You need to process all of the records in a data file, one after another.
Solution: Use an instance of Scanner to provide the records, one at a time. A while loop
that tests for the end of the file controls the processing.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 509
509
9.9 PATTERNS
Scannerƒinƒ=ƒthis.openFile(«fileName»);
whileƒ(in.hasNextLine())
{ƒ«readƒoneƒrecord»
ƒƒ«processƒoneƒrecord»
}
in.close();
LOOKING AHEAD A record is often represented by an instance of a class. Reading it is often best done in
A factory method is a constructor or factory method belonging to that class.
simply a static
method that creates Consequences: The file is read from beginning to end. If it must be processed again, the
and returns an object. file must be reopened to reset the file cursor back to the beginning of the file.
Listing 12-11 contains
an example. Related Patterns:
➤ This pattern uses the Open File for Input pattern to open the file. In the above
template, the pattern would be implemented in the openFile method.
➤ The action of reading one record is often delegated using the Construct
Record from File pattern.
Writing this pattern is an exercise for the student in Written Exercise 9.2.
Context: Input from the user is required. Because the user may enter erroneous data, error
checking is appropriate.
Solution: The pattern to use depends on the amount of error checking required. If only the
correct type of data (integer, double, and so on) matters, then a simpler pattern will suffice.
In the following, «hasNext» is a method such as hasNextInt or hasNextDouble in
the Scanner class, and «next» is the corresponding method to get the next value, such as
nextInt or nextDouble.
Scannerƒinƒ=ƒnewƒScanner(System.in);
...
System.out.print(«initialPrompt»);
whileƒ(!in.«hasNext»()) // type might be Int or Double or …
{ƒin.nextLine(); // skip offending input
ƒƒSystem.out.print(«errorPrompt»);
}
«type»ƒ«varName»ƒ=ƒin.«next»();
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 510
510
OUTPUT
If the value entered matters as well, then a more complex pattern is appropriate:
CHAPTER 9 | INPUT AND
Scannerƒinƒ=ƒnewƒScanner(System.in);
...
«type»ƒanswerƒ=ƒ«initialValue»;ƒƒ// will contain the answer
booleanƒokƒ=ƒfalse;
System.out.print(«prompt»);ƒ
whileƒ(!ok)
{ƒifƒ(in.«hasNext»())
ƒƒ{ƒanswerƒ=ƒin.«next»();
ƒƒƒƒifƒ(«testForCorrectValues»)
ƒƒƒƒ{ƒokƒ=ƒtrue;
ƒƒƒƒ}ƒelse
ƒƒƒƒ{ƒSystem.out.print(«incorrectValueErrorPrompt»);
ƒƒƒƒ}
ƒƒ}ƒelse
ƒƒ{ƒSystem.out.print(«incorrectTypeErrorPrompt»);
ƒƒƒƒin.nextLine();
ƒ}
}
Consequences: The user will be repeatedly prompted until a correct value is input. There
is no provision for the user to cancel the operation or otherwise break out of the loop. Due
to the amount of code, this pattern is best contained in a method.
Related Pattern: This pattern may be used by the Command Interpreter pattern.
Context: You are writing a program in which the user can give commands from a prompt.
You need to interpret the commands and execute code corresponding to each one.
Getting the command from the user is often as simple as getting one word or token
using Scanner. The step to interpret and execute the command is usually performed
with a cascading-if statement. A helper method should be used if more than one or
two lines of code are needed to execute the command.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 511
511
Related Pattern: The Error-Checked Input pattern is an important part of making the
command interpreter respond appropriately to user errors.
Programs may also write information to either the console, using System.out, or a
file, using PrintWriter. The location of a file is specified by a path and filename.
Paths may be either relative to the current working directory or absolute.
It is usually appropriate to represent a record in a file with a class, delegating the input
and output to methods in the class. If several programs use the same file, it is appro-
priate to put the class into a package to make reuse easy.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 512
512
OUTPUT
path and
CHAPTER 9 | INPUT AND
to a
itten
i th
of
e wr
nw
b a file
n from
posed
ca read
tte
be
can
wri
is com
h e
ritten to t console
be
w
can be
can
the
ad from the
can be re
s from
text or data read
command
interpreter
read with
a
can be
personal library
a or package
ced in
reusable can be pla
code
such as
specialized
I/O classes
Problem Set
Written Exercises
9.1 Review Listing 9-4. Explain why the else-clause in lines 13–15 could be
removed with no change in the function of the program.
9.2 Write the Construct Record from File pattern. See Section 9.2.1 for back-
ground and examples.
9.3 Describe how to modify the command interpreter in Listing 9-11 to allow the
user to enter either a word or a single character for each command. For example,
to set the minimum file size, the user can enter m, M, min, or even MiN followed
by the desired size.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 513
513
PROBLEM SET
9.4 Consider writing a simple telephone book program, which will keep names and
telephone numbers of friends and businesses you call frequently in a file. The
program itself should repeatedly prompt for a name, displaying all of the
matching names and their associated telephone numbers.
a. Develop two different file formats for your program. Describe both, indicat-
ing which one you consider the better design and why.
b. Write pseudocode for the program. Include prompting, opening and closing
files, and the search algorithm.
c. Suppose the specification is modified so that the program prints only the
first name it finds, if it finds one at all. Consider the following pseudocode
and describe the bug(s) in each algorithm.
Programming Exercises
9.5 The package becker.xtras.hangman includes classes implementing the
game of Hangman. Figure 7-13 has an image of the user interface. Extend
SampleHangman and override the newGame() method to open a file, choose a
random phrase, and then call the other newGame method with the chosen
phrase. Create a file with the phrases. Create a main method, as shown in the
package overview, to run your program.
9.6 Write a program that reads a text file and writes it to a new file, performing
one of the following transformations. (Note: Other than the described transfor-
mation, the two files should be identical.)
a. Write the new file entirely in uppercase letters.
b. Double space the new file.
c. Make an identical copy of the file except for a statement at the end telling
how many characters, words (tokens), and lines it contains. For the purpose
of counting characters, ignore newline characters.
d. Reverse the characters in each line of the file. If the first line of input is “It
was a dark and stormy night.” the first line of output should be “.thgin
ymrots dna krad a saw tI”.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 514
514
OUTPUT
Programming Projects
9.7 Write a calculator that accepts input like the one shown in Figure 9-15. The
program has a variable that stores the calculation as performed so far. When a
line begins with a number, that number goes in to the variable. An operation
such as + or * is remembered so that the next number can be combined appro-
priately with the number currently stored in the variable. An equal sign causes
the current value of the variable to be printed. A line that starts with an opera-
tor such as / continues to use the number in the variable from previous opera-
tions. In addition to the operators shown, implement subtraction.
(figure 9-15)
Calculator program
9.8 Write your own version of the Prompt class (see Listing 9-9).
a. Implement the forInt, forInputFile, and forInputScanner shown in
Listing 9-9.
b. Implement forBoolean, forDouble, and forToken. Each take a prompt
as a parameter and return a boolean, double, or single String token,
respectively.
c. Implement forInt with three parameters: a prompt, a minimum value,
and a maximum value. The method returns an integer value between
the minimum and the maximum, inclusive. The method invocation
forInt(“Enterƒyourƒchoice”,ƒ1,ƒ5) should produce the prompt
“Enterƒyourƒchoiceƒ[1..5]:ƒ”. Entering text that is outside of this
range or is not an integer should produce a prompt explaining the error.
d. Implement forInt with two parameters: a prompt and a default value. The
method returns the integer value entered by the user, or the default value if
the user only hits Enter. Of course, if the user enters something else, an
appropriate error message is displayed and the user is given another oppor-
tunity. The method invocation forInt(“Enterƒyourƒchoice”,ƒ0)
should produce the prompt “Enterƒyourƒchoiceƒ(0):ƒ”. (Hint: To
detect only Enter, you will need to read the response using nextLine,
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 515
515
PROBLEM SET
removing leading and trailing blanks with the trim method in String. If
the result is not empty, you will need to determine if it contains an integer.
Check the constructors in Scanner for ideas.)
e. Implement forString with two parameters: a prompt and a list of valid
values. The method returns the entered string, but only if the response
appears in the list of valid values. If it does not, print the list of valid values
and ask the user to try again. (Hint: The list of valid values could be either a
string with appropriate delimiters—such as “|exit|stop|go|”—or one of
the collection classes discussed in Section 8.5.)
f. Put the Prompt class in an appropriately named package for easy reuse. In a
different directory, create a simple program that uses at least one of the
methods from your package. Run it.
9.9 Write a class named HangmanTextUI that can be used to play a text-based version
of Hangman. Your program will have a similar structure to the LogExplorer
program described in Section 9.5. You may use the SampleHangman class in
becker.xtras.hangman to implement the actual game.
9.10 Complete the reorganization of the model and user interface for the
LogExplorer program as described in Section 9.5.3.
a. Complete the class diagram shown in Figure 9-8.
b. If necessary, download the files for /ch09/logExplorer2/ and complete
the program it contains. It includes the original LogExplorer class plus the
LogExplorerView Java interface and a graphical user interface. The main
method will ask you which interface you would like to use.
Modifying the program as described will allow you to choose between the
command interpreter and the graphical user interface when the program
runs. The graphical user interface does not require any changes as long as
the methods named setSearchHost and setMinSize are provided in
LogExplorer.
9.11 Implement all the parts of Programming Exercise 9.6, providing a command
interpreter to specify which transformation should be performed. Commands
to the command interpreter should be of the following form:
reverseƒ<in>ƒ[<out>]
prefixƒ<str>ƒ<in>ƒ[<out>]
firstƒ<int>ƒ<in>ƒ[<out>]
<in> is the name of an input file. <out> is the name of an output file. Placing
<out> in square brackets means that entering an output file is optional, in
which case output is displayed on the screen. <str> and <int> indicate that a
string or an integer is expected, respectively.
9.12 Write a program to display a slide show on the computer’s display by combining
the programs in Section 9.8. Modify the ImageComponent class shown in
Listing 9-14 to include the method setImage(StringƒfileName). When
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 516
516
OUTPUT
called, the component should read the image from the named file and display it.
CHAPTER 9 | INPUT AND
Recall the function of repaint, as discussed in Section 6.7.2. Set the preferred
size of the component to 500 x 500 instead of basing it on a specific image.
a. Use JFileChooser to obtain a file named by the user. Each record in the
file will contain an image filename and the number of seconds to display the
image. The image files are assumed to be in the same directory as the file
listing them (which may be different from the program’s working directory).
b. Use JFileChooser to obtain the list of images to show. JFileChooser
can allow the user to select several files at the same time by holding down
the Shift or Control keys while selecting files. To enable this behavior, the
programmer must call the chooser’s setMultiSelectionEnabled method
before the chooser is shown. The list of files chosen can be retrieved with
the following statement:
List<File>ƒfNamesƒ=ƒArrays.asList(
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒchooser.getSelectedFiles());
Use a foreach loop, as discussed in Section 8.5.1, to access each file. Use
Thread.sleep to pause for two seconds between each image.
9.13 A cipher transforms text to conceal its meaning. One of the simplest ciphers is
the Caesar cipher, which replaces each letter in the message with the letter n
positions away in the alphabet, where n remains constant. For example, when
n = 3, A is replaced with D, B with E, C with F, and so on. Letters at the end of
the alphabet will wrap around to be replaced with letters at the beginning of
the alphabet. See Figure 9-16.
(figure 9-16)
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z Caesar cipher
517
PROBLEM SET
b. The Caesar cipher is easy to break because there are so few combinations to
try. One could easily write a program to simply try each value of n. A better
approach asks the user for a key, for example SMOKESTACK, and a value of
n. Insert the letters in the key into a string, ignoring duplicates. Then add all
the remaining characters to encode in order, skipping any that are already
present. For example, with the key SMOKESTACK, one would have the fol-
lowing string: SMOKETACBDFGHIJLNPQRUVWXYZ. Now encode the message
as with the Caesar cipher. Of course, this is more effective if the string con-
tains lowercase letters, digits, punctuation, and so on. It is also more effec-
tive if the key contains some of these characters as well. Write a program
implementing this encoding scheme. Verify that you can use the same pro-
gram to decode the message.
c. The keyed cipher in part (b) is still relatively easy to break using letter fre-
quencies. Assuming the coded message is written in English, the characters
that appear most often in the coded message are likely to be the most fre-
quently occurring English letters: E, T, A, R, and N.
One way around this is use a key again. Suppose the key is CIPHER and the
message to encode is MEETƒATƒDAWN. The first letter of the message, M, is
encoded as O using an offset of 2 because there are two letters between the
beginning of the alphabet and C, the first letter of the key. See Figure 9-17.
(figure 9-17) 2 2
First step of using a key A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Similarly, the second letter of the message, E, is encoded using the second
letter of the key to determine the offset. The offset is 8 because there are 8
letters between the beginning of the alphabet and I. Therefore E is encoded
as M. See Figure 9-18.
(figure 9-18) 8
8
Second step of using a key
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
Notice, however, that the second E in MEET is encoded differently than the
first one. That’s because the third letter of the key is P. Therefore, an offset
of 15 is used instead of an offset of 8, encoding E as T. See Figure 9-19.
9 Chapter C5743 40143.ps 11/30/06 1:35 PM Page 518
518
OUTPUT
(figure 9-19)
15
CHAPTER 9 | INPUT AND
15 Using an offset of 15 to
encode E as T
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
When the end of the key is reached, simply wrap around to the beginning to
encode the next letter of the message. Implement this improved algorithm in
the class Cipher3.java.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 519
Chapter 10 Arrays
Chapter Objectives
After studying this chapter, you should be able to:
➤ Store data in an array, access a single element, process all elements, search for a
particular element, and put the elements in order.
➤ Declare, allocate, and initialize an array.
➤ Handle changing numbers of elements in an array, including inserting a new ele-
ment and deleting an existing one.
➤ Enlarge or shrink the size of an array.
➤ Manipulate data stored in a multi-dimensional array.
We often work with lists in our daily lives: grocery lists, to-do lists, lists of books
needed for a particular course, the invitation list for our next party, and so on. To be
useful, computers must also work with lists: a list of the Thing objects in a City, a list
of concert tickets, or a list of bank accounts, to identify just a few.
There are several ways to implement lists in Java. One of the most fundamental
approaches is with an array, a kind of variable. Once a list is stored in the array we can
do many things: tick off the third item in our to-do list, print the entire list of books for
a course, search our list of invitations to verify that it includes James Gosling, or sort
the list alphabetically.
In Section 8.5, we studied classes in the Java library that are similar to arrays in that
they store a collection of objects. Some of these, such as ArrayList, are thinly dis-
guised arrays. Others, such as HashMap, provide more sophisticated ways to find
objects in the collection. But underneath it all, many of these classes use an array.
519
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 520
520
CHAPTER 10 | ARRAYS
cial is the list of “bigs” (the adults) and “littles” (the girls and boys) participating in the KEY IDEA
association. In this chapter we will consider a computer program that maintains a list There are many
of Person objects (see Figure 10-1) in an array. An array is a kind of variable that can algorithms that work
store many items, such as the items in a list. We will learn how to print the entire list of with lists of things.
people or just the people that meet certain qualifications, such as being a six-year-old
girl. We will learn how to search the list for a specific person and learn to find the per-
son that meets a maximum or minimum criterion (such as the oldest or youngest). Of
course, all these techniques will apply to lists of other kinds of objects as well.
(figure 10-1)
LOOKING BACK
The simplified version of Person, shown in Figure 10-1, uses two enumerations: Enumerations are new
Gender and Role. The first enumeration provides the values MALE and FEMALE; the in Java 1.5 and are
discussed in
Role enumeration provides the values BIG to represent an adult participant and LITTLE
Section 7.3.4.
to represent a young person. The pairWith command will pair this person with the per-
son, p, specified as a parameter. It does this by setting the pairName appropriately in
both objects.
Throughout this section, we will assume that we have an array named persons con-
taining a list of Person objects. In Section 10.2, we will learn how to create such a
variable and fill it with data.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 521
521
LOOKING BACK An object diagram for an array will require showing many Person objects. The
Object diagrams were diagram will become quite large if we use our usual format for each Person object
first discussed in (see Figure 10-2). To avoid this problem, we will abbreviate each person object in
Chapter 1. References
the diagram as shown in Figure 10-3.
were discussed in
Section 8.2.
(figure 10-3)
aPerson Steve, 1968/12/24, M, B
Abbreviated object
diagram
In both diagrams, the box labeled aPerson is a variable that refers to an object—the
round-cornered box labeled Person.
So, what does an array look like? Figure 10-4 shows a visualization of an array of
Person objects. The reference variable persons refers to an array object. The array
object refers to many Person objects. Each reference, called an element of the array, is
numbered beginning with zero. This number is called the index.
522
CHAPTER 10 | ARRAYS
Notice that an array is illustrated almost exactly like other kinds of objects. Similarities
include a variable, such as persons, that refers to the array object just as the variable
karel referred to a Robot object in earlier chapters. An array object contains a public
final instance variable named length, but has no methods. length stores the number
of elements in the array.
The crucial difference between arrays and objects is that the array has instance vari- KEY IDEA
ables that are accessed with square brackets and a number instead of a name. This is Elements in an array
illustrated in Figure 10-4 with variables named [0], [1], and so on. The numbering are numbered
always starts at zero. This language rule often causes beginning programmers grief beginning with zero.
because most people naturally begin numbering with one. Furthermore, the indices run
from zero to one less than the number stored in length. For example, in Figure 10-4,
length is 8 but the indices run from 0 to 7.
The fact that the elements in the array are numbered gives them an order. It makes KEY IDEA
sense to speak of the first element (the element numbered 0), the second element, and Each element has an
the last element. index giving its
position in the array.
System.out.println(aPerson.getName());
Printing the name of the first person in our array is almost as easy. Instead of only nam-
ing the variable, we name the array and the position of the element we want:
System.out.println(persons[0].getName());
The index of the desired element is given by appending square brackets to the name of KEY IDEA
the array. The index appears between the brackets. You may use the result in exactly Arrays are indexed
the same ways that you use a variable of the same type. with square brackets
and an integer
Here is another code fragment that shows the persons array in use. In each case, expression.
persons is followed by the index of a specific element in the array.
1 // Check if Kathleen (see Figure 10-4) is a "Big"
2 ifƒ(persons[3].getRole()ƒ==ƒRole.BIG)
3 {ƒSystem.out.println(persons[3].getName()ƒ+ƒ" is a Big.");
ƒ4 }
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 523
523
The effect of the reference assignment in line 3 is just like assigning references between
non-array variables and is traced in Figure 10-5. Assigning a reference from an array to
an appropriately named temporary variable can make code much more understandable.
kathy
kathy = persons[3];
length 4
Ken, 1997/8/7, M, L
[0]
[1] Beth, 1993/8/27, F, L
[2]
[3] Kathleen, 1979/5/4, F, B
kathy
References stored in an array may also be passed as arguments. For example, Kathleen and
Beth could be paired as Big and Little Sisters with the following sequence of statements:
524
CHAPTER 10 | ARRAYS
However, because elements of an array can be used just like a regular variable, we KEY IDEA
could also pair Kathleen and Beth this way: The golden rule for
arrays: Do unto an
// Pair Kathleen and Beth array element as you
persons[3].pairWith(persons[2]); would do unto a
variable of the
Finally, we can also assign a reference to an array element. For example, suppose same type.
Kathleen is replaced by her friend Claire. The following code constructs an object to
represent Claire and then replaces the reference to Kathleen’s object with a reference to
Claire’s object.
Personƒcƒ=ƒnewƒPerson("Claire",ƒnewƒDateTime(1981,4,14),
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒGender.FEMALE,ƒRole.BIG);
persons[3]ƒ=ƒc;
c Claire, 1981/4/14, F, B
persons[3] = c;
length 4
Ken, 1997/8/7, M, L
[0]
[1] Beth, 1993/8/27, F, L
[2]
Kathleen, 1979/5/4, F, B
[3]
c Claire, 1981/4/14, F, B
The object modeling Kathleen will be garbage collected unless another variable is ref- LOOKING BACK
erencing it. When an object has
no references to it,
the resources it uses
are recycled. See
Section 8.2.3.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 525
525
We can easily exchange, or swap, two elements in an array. For example, suppose we
wanted to switch the places of Ken and Beth within the array. A temporary variable is
needed to store a reference to one of the elements while the swap is taking place. A
method to perform a swap follows. It takes two arguments, the indices of the two ele-
ments to swap. Note that we are now assuming that persons is an instance variable.
classƒBigBroBigSisƒextendsƒObject
{ƒ...ƒpersonsƒ...
ch10/bbbs/
/** Swap the person object at index a with the object at index b. */
ƒƒpublicƒvoidƒswap(intƒa,ƒintƒb)
ƒƒ{ƒPersonƒtempƒ=ƒthis.persons[a];
ƒƒƒƒthis.persons[a]ƒ=ƒthis.persons[b];
ƒƒƒƒthis.persons[b]ƒ=ƒtemp;
ƒƒ}
}
After the swap method finishes executing, the temporary variable temp will cease to
exist. The object it referenced, however, is still referenced by one element in the array
and will not be garbage collected.
526
CHAPTER 10 | ARRAYS
this.persons[a] = this.persons[b];
temp
length 4
Ken, 1997/8/7, M, L
[0]
[1] Beth, 1993/8/27, F, L
[2]
[3] Kathleen, 1979/5/4, F, B
this.persons[b] = temp;
temp
length 4
Ken, 1997/8/7, M, L
[0]
[1] Beth, 1993/8/27, F, L
[2]
[3] Kathleen, 1979/5/4, F, B
length 4
Ken, 1997/8/7, M, L
[0]
[1] Beth, 1993/8/27, F, L
[2]
[3] Kathleen, 1979/5/4, F, B
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 527
527
Accessing an element of an array using a number may not seem particularly helpful. We
could, after all, simply declare many variables that just have a number in each name:
Personƒperson00;
Personƒperson01;
Personƒperson02;
But consider printing the name of each person in the list. Without an array, we would
need statements for each named variable:
System.out.println(person00.getName());
System.out.println(person01.getName());
System.out.println(person02.getName());
ƒƒƒ…
If the list contained 1,000 people, the method to print their names would have about
1,000 lines. What a pain!
KEY IDEA Fortunately, an array’s index may be a variable—or any other expression that evaluates
Arrays may be to an integer. This is where the power of arrays really becomes apparent. By putting the
indexed with println statement inside a loop that increments a variable index, we can print the
variables.
entire array with only three lines of code—no matter how many elements are in it.
KEY IDEA One item of note in this code fragment is the test in the for loop. The length of an
The number of array can always be found with the array’s public final instance variable, length. If
elements in an array the array is as illustrated in Figure 10-4, this.persons.length will return 8, the
can be found with number of elements in the array. The index, i, takes values starting with 0 and ending
.length.
with 7, one less than the array’s length. The length of the array is 8 but the index of the
KEY IDEA last element is one less, 7. This is surely one of the most confusing aspects of arrays for
The last index is one beginning programmers.
less than the length
of the array. So far we have encountered three different mechanisms to find the number of elements
in a collection. Arrays use the public instance variable, length. The number of char-
acters in a string is found with a method, length(). Finally, Java’s collection classes
such as ArrayList and HashMap also use a method to find the number of elements,
but it has a different name, size().
Another task that uses a loop to access each element in turn is to calculate the average
age of the people in the array. For this task, we will use a variable to accumulate the
ages while we loop through the array. After we have added all the ages, we’ll divide by
the length of the array to find the average age.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 528
528
CHAPTER 10 | ARRAYS
The variable sumAges has the role of a gatherer: It gathers all the individual ages
together. That value is then used to find the average age.
The loop controlling the index, i, is exactly the same in calcAverageAge as it was in
the example to print all the names. This looping idiom—starting the index at 0 and
incrementing by one as long as it is less than the length of the array—is extremely com-
mon when using arrays. Using it should become an automatic response for every pro-
grammer confronted with processing all the elements in an array.
You may remember that processing each element was also a common activity when
using the collection classes, such as ArrayList and HashSet. In that situation, we
used the foreach loop introduced with Java 1.5. The foreach loop also works with
arrays. The following loop is equivalent to the one used in calcAverageAge, shown
earlier.
forƒ(Personƒpƒ:ƒthis.persons)
{ƒsumAgesƒ=ƒsumAgesƒ+ƒp.getAge();
}
Process All Elements
The foreach loop is a generalized loop designed for use with unordered data structures
such as maps and trees, for which asking for element n makes no sense. Hence, a foreach
loop has no index. Instead, one element from the collection is provided for each iteration
of the loop until all of the elements have been processed.
Programmers should be familiar with both looping styles. To emphasize this, we’ll
alternate between the two.
The method just written, calcAverageAge(), does not seem nearly as useful as a
method to find the average age of only the littles or only the bigs. In the previous exam-
ple, we added the age of every element in the array. To find the average age of only the
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 529
529
By adding the if statement inside the loop, we restrict its effects to only those elements
that match the test. We process the matching elements. Notice that this pattern is very
similar to the Process All Elements pattern.
LOOKING AHEAD Of course, by changing the test in the if statement, we change which objects we
We’ll learn how to process. By changing the body of the if statement, we change how they are processed.
generalize these For example, the following code fragment prints all the “bigs” who have not been
methods with paired with a “little.”
interfaces and
polymorphism in // Print the names of unpaired "bigs"
Chapter 12. forƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.persons.length;ƒi++)
{ƒPersonƒpƒ=ƒthis.persons[i];
ƒƒifƒ(p.getRole()ƒ==ƒPerson.BIGƒ&&ƒ!p.isPaired())
ƒƒ{ƒSystem.out.println(p.getName());
ƒƒ}
}
In one of our first examples we paired Beth, the person at index 2, with Kathleen, the
person at index 3. But when we’ve decided to pair Beth and Kathleen, how do we find
their positions in the array? We search for them.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 530
530
CHAPTER 10 | ARRAYS
In most cases we don’t know that our search will be successful. It might be that no
object matches the key. Therefore, we need a way to indicate failure. This is usually
done by returning a special value such as null or –1. We can use null when the
search method returns the object that was found and –1 when the search method
returns the array index where the object was found. We use null and –1 for this role
because null is never a legal reference to an object and –1 is never a legal array index.
The easiest way to write a search method is a variation of the Process Matching
Elements pattern—except that the “processing” is to exit the loop and return the
answer. Suppose we are looking for a person using their name as a key. The logic is
shown in the following pseudocode:
We can exit the loop when we find the right person with the return statement. If we
examine all of the people in the array and do not find one matching the key, the code
will exit the loop at the bottom and return null, indicating the search failed.
531
publicƒPersonƒsearch(Stringƒname)
{ƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.persons.length;ƒi++)
ƒƒ{ƒifƒ(this.persons[i].getName().equalsIgnoreCase(name))
ƒƒƒƒ{ƒreturnƒthis.persons[i];ƒƒƒƒƒƒƒƒƒƒ// Search succeeded.
ƒƒƒƒ}
ƒƒ}
ƒƒreturnƒnull;ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// Search failed.
}
LOOKING BACK We can use the search method to pair Kathleen and Beth as follows:
The Prompt class
StringƒbigNameƒ=ƒPrompt.forString("Big's Name: ");
was discussed in
Personƒbigƒ=ƒthis.search(bigName);
Section 9.4.2.
StringƒlittleNameƒ=ƒPrompt.forString("Little's Name: ");
Personƒlittleƒ=ƒthis.search(littleName);
big.pairWith(little);ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// Dangerous code!
KEY IDEA The last line is marked as dangerous code because one or both of the searches may
Always confirm a have failed, in which case big or little will contain the value null. Then a
search was successful NullPointerException will be generated when the last line executes. The outcome
before proceeding. of a search should always be verified and failure handled. The following is better code
because it checks that the searches were successful.
big.pairWith(little);ƒƒ// Safe because both big and little have been found.
Many people think it is a bad idea to exit a loop early. They think that a line such as
the following is like a contract between the programmer and the reader.
forƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.persons.length;ƒi++)
The contract says this code will execute one time for every person in the array. Returning
from the middle of the loop, like the search in Listing 10-1, breaks the contract.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 532
532
CHAPTER 10 | ARRAYS
A search algorithm that respects this view uses a while loop, which does not imply LOOKING BACK
that every element in the array will be visited. The core idea is to repeatedly increment The Four-Step Process
an index variable so that elements of the array are examined in turn. This is Step 1 of for constructing a
loop is discussed in
the Four-Step Process for constructing a while loop. The loop stops (Step 2) when
Section 5.1.2.
either the end of the array is reached or the desired element is found, which ever comes
first. Therefore, the loop continues as long as we have not reached the end of the array
and we have not found the desired element. The loop is assembled (Step 3) with the
results of Steps 1 and 2. Finally, after the loop (Step 4), we need to determine the
answer and return it.
whileƒ(not at the end of the array and matching object not found)
{ƒincrement index to examine the next object
}
Linear Search
ifƒ(at the end of the array)
{ƒthe search failed; return null
}ƒelse
{ƒthe search succeeded; return the object
}
Making this pseudocode concrete to search for a person results in Listing 10-2.
533
An extreme element has the most of something or the least of something. It might be
the person with the most age (oldest person) or the least age (youngest person). In
other contexts, extreme elements might be the employee with the highest salary, the
robot with the most things, the stock with the highest price/earnings ratio, or the name
appearing first in dictionary ordering.
The strategy is to step through the array using the Process All Elements pattern. As we
go, we’ll remember the element that best meets the criteria so far. For each new element
we examine, we’ll ask if it meets the criteria better than the one we’re remembering. If
it does, remember it instead. Expressed in pseudocode, this algorithm is:
Listing 10-3 applies this algorithm to the problem of finding the oldest person in the
array. It begins, in line 3, by remembering the first person in the array (at index 0) as the
oldest we’ve seen so far. This must be true, because we haven’t looked at anyone else.
In line 5, we start looking at the rest of the people in the array. Lines 6–8 check if the
current person matches the criteria better than oldestSoFar. If it does, the old value of
oldestSoFar is replaced with currentPerson. When the loop ends, oldestSoFar
1 /** Find oldest person in the list. (Assumes there is at least one person in the array.) */
2 publicƒPersonƒfindOldestPerson()
3 {ƒPersonƒoldestSoFarƒ=ƒthis.persons[0];
4
5 ƒƒforƒ(PersonƒcurrentPersonƒ:ƒthis.persons)
6 ƒƒ{ƒifƒ(currentPerson.getAge()ƒ>ƒoldestSoFar.getAge())
7 ƒƒƒƒ{ƒoldestSoFarƒ=ƒcurrentPerson;
8 ƒƒƒƒ}
9 ƒƒ}
10 ƒƒreturnƒoldestSoFar;
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 534
534
CHAPTER 10 | ARRAYS
11 } LOOKING AHEAD
Problem 10.4 makes
What happens if two elements in the array meet the criteria equally well? What if two the algorithm more
people have the same age? The algorithm given here will return the first one found and accurate. In
ignore anyone occurring later in the array who happens to be the same age. Changing Section 10.3, we will
the > in line 6 to >= results in finding the oldest person who appears last. learn how to return
an array of people
Listing 10-3 returns the extreme element. Sometimes it is desirable to return the index who all meet the
same criteria.
of that element instead. Implementing such a method requires replacing the foreach
loop with a regular for loop which makes the index explicit.
Java allows an empty array (an array with length zero), as shown in Figure 10-8.
(figure 10-8)
Empty array
persons Person[ ]
length 0
Collections of things are often easier to work with if they are sorted. Card players usu-
ally sort the collection of playing cards in their hands. A collection of words in a dic-
tionary is usually sorted in alphabetical order, as are names in a telephone book. A
collection of banking transactions are sorted by date on the bank statement.
Different algorithms can sort an array. Many of these algorithms have been given
names: Insertion Sort, Selection Sort, QuickSort, HeapSort, ShellSort, MergeSort, and
so on. Selection Sort is one of the easiest sorting algorithms to master. It builds on three
patterns we have already seen: Process All Elements, Find an Extreme, and Swap Two
Elements.
These sorting algorithms vary widely in their efficiency and in their ease of implemen-
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 535
535
Diagrams help us understand how a sort works. For simplicity, our diagrams will use
an array of letters; when the array is sorted, the letters will be in alphabetical order.
The core idea of Selection Sort is to divide the array into two parts, as shown in
Figure 10-9: the part that is already sorted (shown with a dark background) and the
(figure 10-9)part that isn’t (shown with a white background).
At each step in the algorithm, we extend the sorted portion of the array by one ele-
ment. The next element to add to the sorted portion is the smallest element in the
unsorted portion of the array, D. It goes in the position currently occupied by G. These
(figure 10-10)two elements are highlighted in Figure 10-10.
The last part of this step is to swap these two elements, thus extending the sorted por-
(figure 10-11)
tion of the array by one element. See Figure 10-11.
Swapping the two
elements; extending the
sorted part of the array 0 1 2 3 4 5 6
A B C D E G F
These two actions—finding the element that belongs in the next position and swapping
it with the one already there—are performed repeatedly until the entire array is sorted.
The algorithm begins with the sorted portion of the array being empty and the
unsorted portion consuming the entire array. Figure 10-12 shows the entire sorting
operation on a small array.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 536
536
CHAPTER 10 | ARRAYS
0 1 2 3 4 5 6 (figure 10-12)
Two points in this example are worth elaboration. First, notice that when the element
in the next to last position (index 5) is swapped into position, the last element (index 6)
is automatically placed correctly as well. A moment’s thought will explain why: When
all the elements but the last are in their correct places, the last one must also be in its
correct place because there is no where else for it to be.
Second, when it was time to look for the element to place at index 4, the element just hap-
pened to already be there. In this case, we would not need to perform the swapping step.
We will anyway, however, because the “cure” of testing for this condition for every position
in the array is worse than the “disease” of performing the swap every once in a while.
Based on this example, we see that two actions are repeated: Find the element that
belongs in the next position and swap it with the one already there. These actions are
performed for each position in the array, in ascending order, except for the last one.
These observations yield the following pseudocode:
In this case the foreach loop is inappropriate because we will not be examining every
element in the array and because we need the index of the current element.
We can use this algorithm to sort our list of persons, but first we need to decide on the
order we want. Sorted by age? Sorted by name in alphabetical order? Something else?
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 537
537
Listing 10-4 shows the Selection Sort algorithm coded in Java. Let’s look briefly at the
patterns it uses.
First, the sort method uses a very slight variation of the Process All Elements pattern.
The difference is that it processes all the elements except the last one. As noted earlier, by
the time all the other elements are in their place, the last one must be in its place as well.
Second, the helper method uses a variation of the Find an Extreme pattern. It differs
from the pattern in Section 10.1.7 in two ways:
➤ It finds the extreme in only the unsorted part of the array. We pass the index of
the first element it should consider as an argument.
➤ We are concerned with the position of the extreme element, not the element itself.
So our most-wanted holder variable in findExtreme, indexBestSoFar, stores
the index of the best Person object seen so far rather than a reference to
the object.
Listing 10-4: Implementing Selection Sort to sort an array of Person objects by name
ch10/bbbs/
1 publicƒclassƒBBBSƒextendsƒObject
2 {ƒ...ƒpersonsƒ...ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// an array of Person objects
3
4 ƒƒ/** Sort the list of persons in alphabetical order by name. */
5 ƒƒpublicƒvoidƒsort()
Selection Sort 6 ƒƒ{ƒforƒ(intƒfirstUnsortedƒ=ƒ0;
7 ƒƒƒƒƒƒƒƒƒƒƒƒƒfirstUnsortedƒ<ƒthis.persons.length-1;ƒ
8 ƒƒƒƒƒƒƒƒƒƒƒƒƒfirstUnsorted++)
9 ƒƒƒƒ{ƒintƒextremeIndexƒ=ƒthis.findExtreme(firstUnsorted);
10 ƒƒƒƒƒƒthis.swap(firstUnsorted,ƒextremeIndex);
11 ƒƒƒƒ}
12 ƒƒ}
13
14 ƒƒ/** Find the extreme element in the unsorted portion of the array.
15 ƒƒ * @param indexToStart The smallest index in the unsorted portion of the array.
16 ƒƒ * @return The index of the extreme element. */
17 ƒƒprivateƒintƒfindExtreme(intƒindexToStart)
18 ƒƒ{ƒintƒindexBestSoFarƒ=ƒindexToStart;
19 ƒƒƒƒStringƒnameBestSoFarƒ=ƒ
20 ƒƒƒƒƒƒƒƒƒƒƒƒthis.persons[indexBestSoFar].getName();
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 538
538
CHAPTER 10 | ARRAYS
Listing 10-4: Implementing Selection Sort to sort an array of Person objects by name (continued)
21 ƒƒƒƒforƒ(intƒi=indexToStart+1;ƒi<this.persons.length;ƒi++)
22 ƒƒƒƒ{ƒStringƒcurrPersonNameƒ=ƒthis.persons[i].getName();
23 ƒƒƒƒƒƒifƒ(currPersonName.compareTo(nameBestSoFar)ƒ<ƒ0)
24 ƒƒƒƒƒƒ{ƒindexBestSoFarƒ=ƒi;
25 ƒƒƒƒƒƒƒƒnameBestSoFarƒ=ƒthis.persons[i].getName();
26 ƒƒƒƒƒƒ}
27 ƒƒƒƒ}
28 ƒƒƒƒreturnƒindexBestSoFar;
29 ƒƒ}
30
31 ƒƒ/** Swap the elements at indices a and b. */
32 ƒƒprivateƒvoidƒswap(intƒa,ƒintƒb)
33 ƒƒ{ƒPersonƒtempƒ=ƒthis.persons[a];
34 ƒƒƒƒthis.persons[a]ƒ=ƒthis.persons[b];
35 ƒƒƒƒthis.persons[b]ƒ=ƒtemp;
36 ƒƒ}
37 }
Sorting is performed so frequently that a great deal of effort has been spent to make the
operation as fast as possible. The greatest gains in efficiency have been made by
employing different algorithms. QuickSort and HeapSort are among the best, but are
beyond the scope of this book.
LOOKING AHEAD
Selection Sort can be made faster by eliminating the helper methods. Normally, eliminat-
ing helper methods just to speed up an algorithm is not a good idea. In this case, however, This code will be
made more flexible
it may be justified because the algorithm is still relatively understandable. Listing 10-5
and reusable in
implements sortByAge as a single method. The age comparison is somewhat simpler Listing 12.18 in
than comparing names and so some temporary variables have been eliminated as well. Section 12.5.
Listing 10-5: Implementing Selection Sort in a single method to sort an array of Person objects
by age ch10/bbbs/
1 publicƒclassƒBigBroBigSisƒextendsƒObject
2 {ƒ...ƒpersonsƒ...ƒƒƒƒƒƒƒƒƒƒ// An array of Person objects.
3
4 ƒƒ/** Sort the persons array in increasing order by age. */
5 ƒƒpublicƒvoidƒsortByAge()
6 ƒƒ{ƒforƒ(intƒfirstUnsorted=0;ƒ
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 539
539
7 ƒƒƒƒƒƒƒƒfirstUnsorted<this.persons.length-1;
8 ƒƒƒƒƒƒƒƒfirstUnsorted++)
9 ƒƒƒƒ{ƒ// Find the index of the youngest unsorted person.
Selection Sort 10 ƒƒƒƒƒƒintƒextremeIndexƒ=ƒfirstUnsorted;
11 ƒƒƒƒƒƒforƒ(intƒiƒ=ƒfirstUnsortedƒ+ƒ1;ƒ
12 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒiƒ<ƒthis.persons.length;ƒi++)
13 ƒƒƒƒƒƒ{ƒifƒ(this.persons[i].getAge()ƒ<ƒ
14 ƒƒƒƒƒƒƒƒƒƒƒƒthis.persons[extremeIndex].getAge())
15 ƒƒƒƒƒƒƒƒ{ƒextremeIndexƒ=ƒi;
16 ƒƒƒƒƒƒƒƒ}
17 ƒƒƒƒƒƒ}
18
19 ƒƒƒƒƒƒ// Swap the youngest unsorted person with the person at firstUnsorted.
20 ƒƒƒƒƒƒPersonƒtempƒ=ƒthis.persons[extremeIndex];
21 ƒƒƒƒƒƒthis.persons[extremeIndex]ƒ=ƒ
22 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒthis.persons[firstUnsorted];
23 ƒƒƒƒƒƒthis.persons[firstUnsorted]ƒ=ƒtemp;
24 ƒƒƒƒ}
25 ƒƒ}
26 }
Sorting an array is a very common activity and so it’s natural that the Java library pro-
vides support for it via the java.util.Arrays class. It provides methods to sort
arrays of all of the primitive types as well as arrays of objects.
The ordering of the primitive types is defined naturally by their values. Not so with
arrays of objects. When sorting an array of Person objects, for example, how does the
library sort know whether to sort by age or name or some other criteria?
The library sorts use two different approaches, both of which are explained in Chapter 12.
One approach depends on the objects being sorted implementing the Comparable inter-
face. This interface specifies a single method, compareTo, that compares two objects and
returns a number indicating which should come first. Classes that implement this interface
include String, DateTime, File, and enumerated types such as Direction. Sorting a
list of strings, for example, can be accomplished with the code in Listing 10-6.
The vast majority of the code, lines 11–19 and 25–28, is concerned with reading the
strings from the user and printing out the sorted list. The actual sorting is accom-
plished by a single line of code calling a method in the Java library (line 22).
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 540
540
CHAPTER 10 | ARRAYS
The second approach to ordering objects is to pass the sort method the list to sort and
an object implementing the Comparator interface. This is the most flexible approach
and is discussed in Chapter 12.
Some beginning programmers have a hard time distinguishing an array from a file.
After all, both store an ordered collection of objects. Both often use algorithms that
process all of the objects in the collection.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 541
541
One consequence is that accessing an array is much faster than accessing a file. The
disk drive holding your file has moving parts; waiting for them to move makes access-
ing a file slow. Memory, on the other hand, stores the array by arranging electrons in
its chips. Manipulating electrons is much faster.
Files are linear structures. When a file is stored on the disk, all the information is
placed into one long line. It’s processed by reading the first item of information from
the line, then the second, and so on. It’s possible to read an item from the middle of the
line, but you have to know exactly where to start in considerable detail. You need to
know not just that you want the 132nd item, but the exact length of the 131 items that
come before it.
Arrays, on the other hand, support random access naturally. If you want the 132nd
item, use 131 as the index into the array (because arrays are indexed starting at 0).
Random access makes sorting an array easy but sorting a file difficult.
So why do we use files at all? Why not store everything in an array? Because storing
information on a disk drive is much cheaper and because disk drives retain the infor-
mation even when the power is off; memory does not.
Arrays and files are complementary. We often store information in files while we aren’t
working on it. When we begin to use the information, we use a program that loads the
information from the file into an array. After we’re done, usually as one of the last
things a program does, the information is written from the array back to the disk
where it waits until the next time we use it.
KEY IDEA Briefly, creating an array has three steps: declaring the variable, allocating the memory,
Creating an array has and initializing each element in the array to a desired value. In some ways, creating an
three steps: array is like hosting a dinner party. The declaration states your intent to have an
declaration, array—like sending out invitations to your dinner party. When you allocate memory
allocation, and
initialization.
you decide how many elements your array will have—like counting up the responses to
your invitation and setting that many dinner places at the table. Finally, initialization
puts a value in each element of the array—like seating one of your guests at each place
around your table. These three steps are illustrated in Figure 10-13.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 542
542
CHAPTER 10 | ARRAYS
10.2.1 Declaration
Declaring an array is like declaring any other reference variable. A type such as
Person or Robot is required, followed by the name of the variable. If the array is an
instance variable, then an access modifier such as private is appropriate.
The only trick is knowing the type. The type for an array of Person objects is KEY IDEA
Person[] and the type for an array of Robot objects is Robot[]. Simply add a set of The type of an array is
square brackets after the type of elements the array will hold. You might think of the the same as the type
brackets as making the type plural. A variable of type Person holds one person. A of each element, but
with [] appended.
variable of type Person[] holds many persons.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 543
543
publicƒclassƒBBBSƒextendsƒObject
{ƒ...ƒpersonsƒ...ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// An array of Person objects.
publicƒclassƒBBBSƒextendsƒObject
{ƒprivateƒPerson[]ƒpersons;ƒƒƒƒƒƒƒƒ// An array of Person objects.
LOOKING AHEAD The persons array can only hold Person objects.
In Chapter 12, we
will see that the
persons array can
10.2.2 Allocation
also hold subclasses
of Person.
The declaration of an array does not create the array, but only a place to hold a refer-
ence to an array. See Step 1 in Figure 10-13. We also need to allocate the array object
itself, similar to constructing any other kind of object. See Step 2 in Figure 10-13.
KEY IDEA The following code fragment constructs an array object, allocating space for eight ele-
Use the new keyword ments. It uses the new keyword followed by the type of the elements the array will
to set aside space for store. In square brackets is the number of elements the array will be able to hold.
a specific number of
elements. this.personsƒ=ƒnewƒPerson[8];
Of course, including a different number in place of the 8 would allocate space for a dif-
ferent number of elements. The 8 in this example can also be replaced with any expres-
sion that evaluates to an integer, including a simple variable or a complex calculation.
This calculation may, for example, be based on information obtained from a user, as
shown in the following code fragment:
publicƒclassƒBBBSƒextendsƒObject
{ƒprivateƒPerson[]ƒpersons;
ƒƒ...
ƒƒprivateƒvoidƒcreateArray()
ƒƒ{ƒScannerƒinƒ=ƒnewƒScanner(System.in);
ƒƒƒƒSystem.out.print("How many persons: ";
ƒƒƒƒintƒnumPersonsƒ=ƒin.nextInt();
ƒƒƒƒthis.personsƒ=ƒnewƒPerson[numPersons];
ƒƒƒƒ...
ƒƒ}
}
KEY IDEA The programmer often knows how many elements will be in the array when the pro-
An array may be gram is written. In this case, the declaration and the allocation may be combined:
declared and
allocated in one privateƒPerson[]ƒpersonsƒ=ƒnewƒPerson[100];ƒ
statement when you
know how many
elements it will hold.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 544
544
CHAPTER 10 | ARRAYS
10.2.3 Initialization
The final step in creating an array is to initialize each element, as illustrated in Step 3
of Figure 10-13. The simplest approach is to call an appropriate constructor for each
element in the array. For example, a small array of Person objects could be initialized
like this:
this.persons[0]ƒ=ƒnewƒPerson("Steve",ƒ"1968/12/24",
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒGender.MALE,ƒRole.BIG);
this.persons[1]ƒ=ƒnewƒPerson("Ken",ƒ"1997/8/7",
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒGender.MALE,ƒRole.LITTLE);
this.persons[2]ƒ=ƒnewƒPerson("Beth",ƒ"1993/8/27",ƒ
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒGender.FEMALE,ƒRole.LITTLE);
This approach works, but is impractical for a large number of elements. Array initial-
ization is often performed by reading information from a file and constructing an
object for each of the file’s records.
The main problem is knowing how many records are in the file. This information is
needed to allocate the correct number of elements for the array.
One approach is to simply count the records. The file is opened and the records are LOOKING AHEAD
read, counting each one. When the end of the file is reached, it is closed and then Reading objects from
opened again. The array is allocated using the count just obtained. The entire file is a file was discussed in
then read a second time, storing each object in the array. Section 9.2.1.
Listing 10-7 shows the constructor to the BBBS class in lines 14–36. The initialization
of the array takes place in the constructor. The relevant points are:
➤ The array is declared at line 10.
➤ In lines 18–24, the file is opened, every record is read and counted, and then
the file is closed.
➤ In line 27, the array is allocated using the count of the records in the file.
➤ In lines 30–33, the file is again opened and the records read. This time, how-
ever, the objects created with the data are stored in the array at line 32. The
file is closed again in line 34 after all of the records have been read.
545
8 publicƒclassƒBigBroBigSisƒextendsƒObject
9 {
10 ƒƒprivateƒPerson[]ƒpersons; // the list of bigs and littles
11
12 ƒƒ/** Construct a new object by reading all the bigs and littles from a file.
13 ƒƒ* @param fileName the name of the file storing the information for bigs and littles */
14 ƒƒpublicƒBigBroBigSis(StringƒfileName)
15 ƒƒ{ƒsuper();
16
17 ƒƒƒƒ// Count the number of Persons in the file.
18 ƒƒƒƒintƒcountƒ=ƒ0;
19 ƒƒƒƒScannerƒinƒ=ƒthis.openFile(fileName);
20 ƒƒƒƒwhileƒ(in.hasNextLine())
21 ƒƒƒƒ{ƒPersonƒpƒ=ƒnewƒPerson(in);
22 ƒƒƒƒƒƒcount++;
23 ƒƒƒƒ}
24 ƒƒƒƒin.close();
25
26 ƒƒƒƒ// Allocate an array to hold each object we read.
27 ƒƒƒƒthis.personsƒ=ƒnewƒPerson[count];
28
29 ƒƒƒƒ// Read the data, storing a reference to each object in the array.
30 ƒƒƒƒinƒ=ƒthis.openFile(fileName);
31 ƒƒƒƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒcount;ƒi++)
32 ƒƒƒƒ{ƒthis.persons[i]ƒ=ƒnewƒPerson(in);
33 ƒƒƒƒ}
34 ƒƒƒƒin.close();
35
36 ƒƒ}
37 ƒƒ...
99 }
One disadvantage of reading the file twice is inefficiency. Reading from a file is inher-
ently slow and it would be more efficient to avoid reading the entire file twice.
Another approach is to store the number of records as the first item in the file, as shown
in Figure 10-14. The constructor can simply read this data item and allocate the array.
The records can then be read and stored into the array the first time the file is read.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 546
546
CHAPTER 10 | ARRAYS
5 (figure 10-14)
Kenneth A Parsons File with the number of
1997/8/7 M L records stored as the first
Beth A Reyburn
data item
1993/8/27 F L
Kathleen A Waller
1979/5/4 F B
Roydyn A. Clayton
1993/5/25 M L
Christopher Aaron Fairles
1981/2/2 M B
A disadvantage of this approach is that the number of records must be kept accurate. LOOKING AHEAD
This may be hard to guarantee if the file is edited directly by users. However, it is not An array that appears
difficult if the file is always created by a program. to grow can also solve
this problem. See
Section 10.4.
Listing 10-8 shows a constructor using this approach. It could be substituted for the
constructor shown in Listing 10-7, provided the data file were changed to include the
number of records in the file.
Listing 10-8: Initializing an array when the data file contains the number of records
1 ƒƒpublicƒBigBroBigSis(StringƒfileName)
2 ƒƒ{ƒsuper();
3 ƒƒƒƒScannerƒinƒ=ƒthis.openFile(fileName);
4
5 ƒƒƒƒ// Get the number of records in the file.
6 ƒƒƒƒintƒcountƒ=ƒin.nextInt();
7 ƒƒƒƒin.nextLine();
8
9 ƒƒƒƒ// Allocate an array to hold each record we read.
10 ƒƒƒƒthis.personsƒ=ƒnewƒPerson[count];
11
12 ƒƒƒƒ// Read the data, storing a reference to each object in the array.
13 ƒƒƒƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒcount;ƒi++)
14 ƒƒƒƒ{ƒthis.persons[i]ƒ=ƒnewƒPerson(in);
15 ƒƒƒƒ}
16 ƒƒƒƒin.close();
17 ƒƒ}
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 547
547
Java provides a handy shortcut to initialize an array if you know its contents when you
write the program. Essentially, you place the array elements in a comma-separated list
between curly braces, as shown in the following example:
bbbs.personsƒ=ƒnewƒPerson[]
ƒƒ{ƒnewƒPerson("Byron",ƒ"1961/3/21",
ƒƒƒƒƒƒƒƒGender.MALE,ƒRole.BIG),
ƒƒƒƒnewƒPerson("Ann",ƒ"1960/12/3",
ƒƒƒƒƒƒƒƒGender.FEMALE,ƒRole.BIG),
ƒƒƒƒnewƒPerson("Luke",ƒ"1990/10/1",
ƒƒƒƒƒƒƒƒGender.MALE,ƒRole.LITTLE),
ƒƒƒƒnewƒPerson("Joel",ƒ"1994/2/28",
ƒƒƒƒƒƒƒƒGender.MALE,ƒRole.LITTLE)
ƒƒ};
Java will automatically create an array of the right length to hold all the elements
listed. In fact, if you try to specify the size yourself, the compiler will give you an error.
LOOKING AHEAD One common activity that demonstrates both passing and returning arrays is to extract
Problem 12.13 a subset from a larger array. For example, return an array of Person objects that con-
generalizes this tains only “bigs” who are female. To make the method more versatile, we’ll pass the
method with desired gender and role as arguments. The method’s signature is as follows:
interfaces and
polymorphism. publicƒPerson[]ƒextractSubset(Genderƒg,ƒRoleƒr)
The return type of Person[] indicates that the method will return a reference to an
array of Person objects.
548
CHAPTER 10 | ARRAYS
The first step, counting the size of the subset, is an application of the Process Matching
Elements pattern in which the process performed is simply counting. Its signature and
method documentation are as follows; implementing it is Problem 10.7. Process Matching
Elements
/** Count the number of persons matching the given gender and role.
* @param g The gender of persons to be included in the subset.
* @param r The role of the persons to be included in the subset. */
privateƒintƒcountSubset(Genderƒg,ƒRoleƒr)
The second step, allocating a temporary array, illustrates that declaring and allocating
an array within a method is both possible and useful. As always, the access modifier,
such as private, is omitted when declaring a temporary variable.
Person[]ƒsubsetƒ=ƒnewƒPerson[size];
The third step, filling the subset array, is the tricky one. We’ll pass the method the
gender and role of the Person objects desired, as well as a reference to the temporary
array. The method’s signature will be:
privateƒvoidƒfillSubset(Person[]ƒss,ƒGenderƒg,ƒRoleƒr)
Again, notice the type Person[]. The parameter variable ss will refer to an array of LOOKING AHEAD
Person objects. Like other references passed as parameters, ss will contain an alias to Aliases were
subset; both references refer to the same array and both can be used to access and discussed in
Section 8.2.2.
change the contents of the array. The reference itself cannot be changed, but the thing
it refers to can be changed.
Inside the method, we’ll repeatedly find the next person object with the appropriate
gender and role, copying a reference to it into the next available space in the temporary
array. This will require two index variables, one to keep track of where we are in the
persons array and the other to track our position in the subset array.
Figure 10-15 shows the situation immediately after the first Person object has been
inserted into the subset. The index variable ssPos (“subset position”) gives the index
of the next available position in the subset array. The variable arrPos (“array posi-
tion”) gives the index of the next Person object to consider. The colored arrows show
Person objects that have yet to be copied.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 549
549
arrPos 2
The code for the helper method is shown in lines 27–38 of Listing 10-9. Notice that
ssPos is only incremented when a new element is added to the subset (line 34) but that
arrPos is incremented each time a new Person object is considered (line 36).
The final step in the extractSubset method is to return a reference to the subset
array (line 13).
550
CHAPTER 10 | ARRAYS
22
23 ƒƒ/** Fill the subset array with Person objects matching the given gender and role.
24 ƒƒ* @param subset The array to fill with elements belonging to the subset.
25 ƒƒ* @param g The gender of persons to be included in the subset.
26 ƒƒ* @param r The role of the persons to be included in the subset. */
27 ƒƒprivateƒvoidƒfillSubset(Person[]ƒss,ƒGenderƒg,ƒRoleƒr)
28 ƒƒ{ƒintƒssPosƒ=ƒ0;ƒƒƒ// position within the subset
29 ƒƒƒƒintƒarrPosƒ=ƒ0;ƒƒ// position within the array
30 ƒƒƒƒwhileƒ(ssPosƒ<ƒss.length)
31 ƒƒƒƒ{ƒPersonƒpƒ=ƒthis.persons[arrPos];
32 ƒƒƒƒƒƒifƒ(p.getGender()ƒ==ƒgƒ&&ƒp.getRole()ƒ==ƒr)
33 ƒƒƒƒƒƒ{ƒss[ssPos]ƒ=ƒp;
34 ƒƒƒƒƒƒƒƒssPos++;
35 ƒƒƒƒƒƒ}
36 ƒƒƒƒƒƒarrPos++;
37 ƒƒƒƒ}
38 ƒƒ}
39 }
Client code using the BBBS class could use the extractSubset method as follows:
Person[]ƒfemaleBigsƒ=ƒbbbs.extractSubset(Gender.FEMALE,
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒRole.BIG);
System.out.println("FemaleƒBigs:");
forƒ(Personƒpƒ:ƒfemaleBigs)
{ƒSystem.out.println(p.getName());
}
Passing and returning arrays of information are useful techniques. For example, the
Big Brother/Big Sister project might have a reporting subsystem that could use such
techniques extensively. Imagine a suite of subset extraction methods that each return a
subset of a passed array. They could be put together in endless combinations. We could
have, for example, a query like this, in which each extract method takes a criterion
and an array as arguments:
Person[]ƒssƒ=ƒthis.extract(Gender.MALE,ƒ
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒthis.extract(Role.LITTLE,
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒthis.extract(Interests.SPORTS,
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒthis.persons)));
this.print(ss);
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 551
551
To implement add, we must figure out how to “create” additional space in the array.
In this section, we’ll explore two approaches to this problem, and ultimately conclude
that the best solution uses features of both.
KEY IDEA The first approach uses a simple idea: Create an array with room to grow, if necessary.
Allocate extra space This separates the notion of the size of the array (the number of elements it currently
for the array. Use the stores) from the length of the array (the maximum number of elements it can store).
first elements to store
This requires an auxiliary variable that we usually name size. Such an array is usually
data. Keep the
number of elements only partly filled, so we’ll call it a partially filled array.
in use in another
variable.
We will adopt a convention that indices in the range 0..size-1 will hold the valid ele-
ments while indices size..length-1 will be “empty.” This is illustrated in Figure 10-16.
(figure 10-16)
persons
Steve, 1968/12/24, M, B
Partially filled array with
size 4 Person[ ]
four elements Ken, 1997/8/7, M, L
length 8
Beth, 1993/8/27, F, L
[0]
[1] Kathleen, 1979/5/4, F, B
[2]
[3]
[4] null
[5] null
[6] null
[7] null
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 552
552
CHAPTER 10 | ARRAYS
The auxiliary variable, size, can be interpreted two ways. First, it can be interpreted KEY IDEA
as the number of elements in the array that store valid data. This interpretation is use- size says how
ful for the Process All Elements and related patterns. For example, to print all the many elements have
valid data.
names in the partially filled persons array, we write
forƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.size;ƒi++)
{ƒPersonƒpƒ=ƒthis.persons[i];
ƒƒSystem.out.println(p.getName());
}
The other Process All Elements idiom, using the foreach loop, will not work with par- KEY IDEA
tially filled arrays. Writing forƒ(Personƒpƒ:ƒthis.persons) is the same as writ- The foreach loop
ing forƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.persons.length;ƒi++). doesn’t work for
partially filled arrays.
The second interpretation of size is as the first element of the “empty” portion of the
KEY IDEA
array. This interpretation is the natural one for the add method because it tells us
size also says where
where to put the new element.
the next element
publicƒvoidƒadd(Personƒp) should be added.
{ƒthis.persons[this.size]ƒ=ƒp;
ƒƒthis.size++;
}
Of course, if the array is already full (size has the same value as persons.length),
the add method will fail with an ArrayIndexOutOfBoundsException. We will
investigate a solution to this problem shortly.
If the array is already sorted and you want to keep it sorted, simply adding the new ele- LOOKING AHEAD
ment to the end isn’t good enough. One approach would be to add to the end and then Implementing this
sort the entire array, but that is inefficient. A much better approach is to move elements algorithm is
larger than the new element down in the array. The new element can then be inserted Problem 10.8.
553
p p p
The original array containing Move references at the end of Insert the new element and
four Person objects the array down by one to make increment the auxiliary variable,
room for the new element size
Deletion
When deleting an element, we need to fill the “hole” left by the deleted element so that
all the valid array elements are kept at the beginning of the partially filled array and all
the unused space at the end. We’ll use the following algorithm:
The first step may be trivial if we are given the index of the element to delete. In other
situations, we may need to search for the element to find the index.
The second step varies, depending on whether a sorted order must be maintained. If
the array is unsorted, use the last element of the array to replace the element being
deleted. In a sorted array, the elements with indices larger than d all need to be moved
up one position in the array.
The third step recognizes that there is now one less element in the array.
LOOKING AHEAD The last step is not strictly necessary, however it is a good idea to assign null to the
Written Exercise 10.1 element for two reasons. First, it can make debugging easier because accidentally
asks you to explain accessing an element in the unused portion of the partially filled array will generate a
why this step is
NullPointerException, quickly informing us that we made a mistake. Second, it
optional.
may free an object for garbage collection, thereby reducing the memory required by
our program.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 554
554
CHAPTER 10 | ARRAYS
Unfortunately, partially filled arrays pose two significant problems. First, a partially
filled array solves the problem of adding elements to an array, but only up to a point.
There is still a limit. If the array is initially allocated to hold 500 elements, we can’t
insert 501. The last one just won’t fit. Using the algorithms discussed earlier will result
in an ArrayIndexOutOfBoundsException. If this abrupt ending to the program
isn’t desired, a check with a friendlier message can be made:
publicƒvoidƒadd(Personƒp)
{ƒifƒ(this.sizeƒ<ƒthis.persons.length)
ƒƒ{ƒthis.persons[this.size]ƒ=ƒp;
ƒƒƒƒthis.size++;
ƒƒ}ƒelse
ƒƒ{ƒ// error message
ƒƒ}
}
One way of addressing the first problem is to allocate arrays with more space than we
think we’ll ever use. Unfortunately, this leads to the second problem with partially
filled arrays—wasting lots of memory. In addition, history is filled with programmers
who dramatically misjudged how much data would be poured into their programs. For
example, a program written to handle people associated with the local chapter of Big
Brothers/Big Sisters might be deployed nationally and suddenly need to deal with much
more information.
In spite of these two problems, partially filled arrays are a great solution where the
amount of data can be reliably estimated.
A second approach to the problem of adding and deleting elements in an array is to KEY IDEA
“change” the size of the array. Once an array is allocated, its size can’t be changed, but Arrays can’t change
we can allocate a new array with a different size and then copy the elements from the size, but we can make
it appear as if they do.
old array to the new array. After updating the array’s reference to point to the new
array, it appears as though the array has simply grown. The new element can then be
added. These four steps are shown in Figure 10-18.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 555
555
Kathy Kathy
p p
Step 1: Allocate a new, larger array Step 2: Copy the contents to the larger array
persons persons
larger
Person[ ] Ken Ken
Person[ ] Person[ ]
length 4
length 5 Steve length 5 Steve
[0]
[0] [0]
[1] Amy Amy
[1] [1]
[2]
[2] [2]
[3] Beth Beth
[3] [3]
[4] null [4]
Kathy Kathy
p p
Step 3: Reassign the array reference Step 4: Add the new element
1 publicƒclassƒBBBSƒextendsƒObject
2 {ƒprivateƒPerson[]ƒpersons;
3
4 ƒƒ...
5
6 ƒƒ/** Add a new person to the persons array.
7 ƒƒ* @param p The new person to add. */
8 ƒƒpublicƒvoidƒadd(Personƒp)
9 ƒƒ{ƒ// Step 1: Allocate a larger array.
10 ƒƒƒƒPerson[]ƒlargerƒ=ƒnewƒPerson[this.persons.lengthƒ+ƒ1];
11
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 556
556
CHAPTER 10 | ARRAYS
12 ƒƒƒƒ// Step 2: Copy elements from the old array to the new, larger array.
13 ƒƒƒƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.persons.length;ƒi++)
14 ƒƒƒƒ{ƒthis.larger[i]ƒ=ƒthis.persons[i];
15 ƒƒƒƒ}
16
17 ƒƒƒƒ// Step 3: Reassign the array reference.
18 ƒƒƒƒthis.personsƒ=ƒlarger;
19
20 ƒƒƒƒ// Step 4: Add the new element.
21 ƒƒƒƒthis.persons[this.persons.length-1]ƒ=ƒp;
22 ƒƒ}
23 }
There is, however, a big disadvantage to this approach. Inserting many elements is very
time consuming because so much copying is required. For example, one test1 produced
the data shown in Figure 10-19. The first column shows the number of insertions. The
second column shows the time, in seconds, required to make the insertions into an
array that grows by one with each insertion. The last column shows the number of sec-
onds required to insert the same data into a partially filled array.
a) Time to insert into an array b) Graphing the time to insert data into an array (figure 10-19)
that grows
Insertions Grow PFA Inserting elements in
10,000 0.4 0.000 an array
800
20,000 1.8 0.000
700
30,000 6.1 0.000
600
40,000 14.2 0.015
Seconds
30
50
70
90
13
15
557
KEY IDEA Combining the two approaches addresses all three issues. The strategy is to use a par-
Expandable, partially tially filled array. When it gets full, allocate a larger array. However, don’t increase the
filled arrays give the array by only one element. Instead, double the size of the array. That typically wastes
best of both some space, but not more than a factor of two. If that’s too much, the array could be
approaches.
increased by 25% each time it is enlarged.
The same test as shown in Figure 10-19 takes only 0.047 seconds to insert 150,000
items—a little worse than a partially filled array that is initially allocated to hold
150,000 items, but not nearly as bad as growing the array by one each time.
The ArrayList class in the Java library uses exactly this approach. It is simply a par-
tially filled array that can grow when it gets full, wrapped in a class.
Listing 10-11 shows an add method for a partially filled array that is doubled when-
ever it becomes full. Note that this same method can be used in the constructor, elimi-
nating the need to count the number of items in the file (compare Listing 10-11 with
Listing 10-7).
558
CHAPTER 10 | ARRAYS
Listing 10-11: Initializing and adding to an expandable, partially filled array (continued)
Person[]ƒpersonsƒ=ƒnewƒPerson[4];
double[]ƒinterestsƒ=ƒnewƒdouble[4];
The Person class used in the Big Brother/Big Sister program defines four variables to
store potential interests of the participants: the extent to which they like sports, crafts,
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 559
559
10.5 ARRAYS
games, and the outdoors. A value of 0.0 indicates they don’t have an interest in it at all
whereas a value of 1.0 indicates a very high interest. Before two people are paired,
their compatibility is determined with the getCompatibility query:
OF
PRIMITIVE TYPES
publicƒdoubleƒgetCompatibility(Personƒp)
{ƒreturnƒ(this.likesCraftsƒ*ƒp.likesCraftsƒ
ƒƒƒƒƒƒ+ƒthis.likesGamesƒ*ƒp.likesGames
ƒƒƒƒƒƒ+ƒthis.likesOutdoorsƒ*ƒp.likesOutdoors
ƒƒƒƒƒƒ+ƒthis.likesSportsƒ*ƒp.likesSports)
ƒƒƒƒƒƒ/4.0;
}ƒ
Suppose it was determined that these four interests need to be supplemented with an
additional 16, for a total of 20 different interests. Using separate variables for each one
would be tedious; an array is a much better choice. Using an array, the Person class is
written as shown in Listing 10-12.
1 publicƒclassƒPersonƒextendsƒObject
2 {ƒ...
3 ƒƒprivateƒstaticƒfinalƒintƒNUM_INTERESTSƒ=ƒ20;
4 ƒƒprivateƒdouble[]ƒinterestsƒ=ƒnewƒdouble[NUM_INTERESTS];
5 ƒƒ...
6
7 ƒƒpublicƒPerson(Scannerƒin)
8 ƒƒ{ƒ...
9 ƒƒƒƒ// Read this person's interests from the file.
10 ƒƒƒƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒPerson.NUM_INTERESTS;ƒi++)
11 ƒƒƒƒ{ƒthis.interests[i]ƒ=ƒin.nextDouble();
12 ƒƒƒƒ}
13 ƒƒƒƒ...
14 ƒƒ}
15
16 ƒƒ/** How compatible is this person with person p? A score of 0.0 means not at all
17 ƒƒ* compatible; 1.0 means extremely compatible. */
18 ƒƒpublicƒdoubleƒgetCompatibility(Personƒp)
19 ƒƒ{ƒdoubleƒcompatƒ=ƒ0.0;
20 ƒƒƒƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒPerson.NUM_INTERESTS;ƒi++)
21 ƒƒƒƒ{ƒcompatƒ=ƒcompatƒ+ƒthis.interests[i]ƒ*ƒp.interests[i];
22 ƒƒƒƒ}
23 ƒƒƒƒreturnƒcompatƒ/ƒPerson.NUM_INTERESTS;
24 ƒƒ}
25 }
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 560
560
CHAPTER 10 | ARRAYS
So far the indices of our arrays have been just positions. They haven’t had any meaning
attached to them, though it is sometimes useful to do just that. Suppose, for example,
that we wanted to know the distribution of ages of the people participating in the Big
Brother/Big Sister program. That is, we want to know how many people are 10 years
old, how many are 11, and so on. We’ll assume no one is over 200 years old.
To solve this problem we can allocate an array named ageCounters with 200 ele-
ments. Each element will be a counter for a particular year. Which year? The year cor-
responding to the index. Thus, ageCounters[10] will be the number of 10 year-olds
and ageCounters[25] will be the number of 25 year-olds. We’ll have a counter for
everyone between 0 and 199 years old, inclusive.
The method shown in Listing 10-13, when added to the BigBroBigSis class, will
return a filled array giving the number of participants for each age. It could be used
like this:
int[]ƒagesƒ=ƒbbbs.getAgeCounts();
forƒ(intƒiƒ=ƒ0;ƒiƒ<ƒages.length;ƒi++)
{ƒifƒ(ages[i]ƒ>ƒ0)
ƒƒ{ƒSystem.out.printlnƒ("There are "ƒ+ƒages[i]ƒ+ƒ
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ" participants that are "ƒ+ƒiƒ+ƒ" years old.");
ƒƒ}
}
561
10.5 ARRAYS
In the last example, the indices naturally matched ages because both ranges start at 0.
Sometimes that isn’t the case. Consider a slight modification of this problem: Count
the number of coins in a collection by the year they were minted. Assume the oldest
OF
coin was minted in 1850.
PRIMITIVE TYPES
This problem could be solved by allocating an array with 1850 unused elements. A bet-
ter approach is to offset the indices by 1850, as shown in Listing 10-14. The crucial
lines are 5, 16, and 22. In line 5, the constants EARLIEST and LATEST are used to cal-
culate the actual number of elements or counters that are needed. This avoids the
unused elements at the beginning of the array. In line 16, the year entered by the user is
reduced by the appropriate amount so that it can be used as an index into the array. In
line 22, the reverse is done to map the index to the appropriate year.
562
CHAPTER 10 | ARRAYS
(figure 10-20)
Two-dimensional array
recording income by
source and month
Java uses one pair of brackets for each dimension of an array. The one-dimensional
arrays we used earlier in the chapter use one pair of brackets; the two-dimensional
array shown in Figure 10-20 uses two. Of course, a three-dimensional array uses three
pairs. The pattern continues for as many dimensions as you need.
int[][]ƒincomeƒ=ƒnewƒint[12][5]
The declaration on the left side of the equal sign specifies a 2D array where each cell KEY IDEA
stores an integer. The allocation on the right side specifies that the array has 12 rows The first pair of
and five columns. brackets is for the
rows; the second pair
Figure 10-20 is actually a bit misleading, for the following reasons: of brackets is for the
columns.
➤ Column names like “Corporate Donations” and row names like “May” are
not directly associated with an array. The array itself is declared to store only
integers. It cannot store strings as column or row labels.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 563
563
A more accurate picture of the array is shown in Figure 10-21 which takes all this into
account.
For example, to print the income array we could use a method like the one shown in
Listing 10-15.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 564
564
CHAPTER 10 | ARRAYS
The inside loop, lines 10–12, prints one entire row each time it executes. The row it
prints is specified by the outer loop, row r. After the row is printed, line 13 ends the
current line of text and begins a new line. This process of printing a row is repeated for
each row specified by the outer loop.
Notice that the number of rows is found in line 9 with this.income.length while KEY IDEA
the number of columns in a particular row is found in line 10 with this. It’s possible to find
income[r].length. They differ because in Java a 2D array can be ragged—each row the number of rows in
may have its own length. We will see an example of this in Section 10.6.3. a 2D array, as well
as the number of
columns in each row.
Sum Every Element
The same nested looping pattern can be used to find the total income, from all sources,
for the entire year:
565
Summing a Column
To find the total of the individual donations in one year, we need to sum column 2 in
the income array. This task requires a single loop because it is working in a single
dimension—moving down the column. Passing the column index as a parameter makes
the method more flexible:
/** Calculate the total income for a given category for the year.
* @param columnNum The index of the column containing the desired category. */
publicƒintƒgetTotalByCategory(intƒcolumnNum)
{ƒintƒtotalƒ=ƒ0;
ƒƒforƒ(intƒrƒ=ƒ0;ƒrƒ<ƒthis.income.length;ƒr++)
ƒƒ{ƒtotalƒ=ƒtotalƒ+ƒthis.income[r][columnNum];
ƒƒ}
ƒƒreturnƒtotal;
}
As with a one-dimensional array, the declaration and allocation of the array can be
split. This means that determining the size of an array can be delayed until the program
is actually executing. For example, the array could be initialized from a file where the
first two numbers indicate the number of rows and columns, respectively.
The first five rows of such a data file are shown in Figure 10-22. The constructor
shown in Listing 10-16 shows how the array is allocated and then initialized using this
data. The size of the array is determined in lines 11 and 12. The array itself is allocated
using those sizes in line 16. Finally, the data is read and stored in the array using the by
now familiar double loop in lines 19-24. The calls to nextLine in lines 13 and 23 are
not strictly necessary because nextInt will read across line boundaries; however,
using nextLine shows where line endings are expected in the file and adds to the clar-
ity of the code.
(figure 10-22) 12 5
Sample data file 0 3000 6915 0 15500
0 2125 4606 0 5500
0 2000 5448 0 5500
0 3000 4833 13983 15500
...
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 566
566
CHAPTER 10 | ARRAYS
The picture we’ve used so far of a 2D array having rows and columns is adequate in
most circumstances (see Figure 10-21). However, it doesn’t match reality and some-
times knowing all the details is useful.
We can now understand accessing the number of rows and columns in an array. When
we write this.income.length, it returns the length of the array holding the rows—
the number of rows in the 2D array. When we write this.income[r].length, it
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 567
567
(figure 10-23)
15,500
0
4,833[1][3]2,125 0
0 [1] 5 3,000
] [1] 53,000[0][2] 6,915
income
5
Viewing a 2D array as an
0
13,983[2][4]4,606
5,500
int[ ]
array of arrays int[ ][ ]
0
[1] 2,000
[2] 5,448
[4] 5,500
[0]]
length
int[
length 12
5
[0] ] length
[3]
[4]
int[ ]
[0]
15,500
[0]
length
[3]
0
[0][2]20,569
[1]0[3] 2,000
6,091
00 5,500
5
[1]
int[ ]
[2]
int[
length
[4]
[3]
8,000
4,867
5,500
int[length
[2]
[3]
[4]
[4] 15,500
0
[1][4] 3,000
4,196
5
[4]
5
5 int[ ]
[5]
0
2,550
[2] 0 4.736
] 5,500[0]
length
[1]
[2]
[3]
5
[0]
length
[2]
[3]
[6]
int[ ]
0
0 [1] 2,000
[4] 5,500 [2] 4,305
[4] 5,500
[3] 32,254
[4] [0]15,500
5[1] 3,000
5,286
[7]
5 int[ ][0]
length
[1]
[3]
[4]
5
00 int[
[8]
length
[3]
[9]
] [0]
2,000
[2] 6,834
length
[2]
[0] 9,351
[1]int[2,000
[2] 7,459
[1] 5,500
[10]
int[ ]
[11]
[0]
length
[3]
length
[3]
[4]
Sometimes, viewing a 2D array this way can work to our advantage in writing a pro-
gram, too. For example, suppose you want to swap row r and row s in the array
income. Rather than swap each element in row r with the corresponding element in
row s, we can write:
int[]ƒtempƒ=ƒincome[r];
income[r]ƒ=ƒincome[s];
income[s]ƒ=ƒtemp;
The first line declares a temporary variable to store a 1D array. Then the rows are
swapped by swapping their references. There is no equivalent way to swap columns.
Another way in which the array of arrays viewpoint can make a difference in our code
is a method that takes an entire row as a parameter. For example, we might already
have a simple utility method to sum a 1D array:
privateƒintƒsum(int[]ƒa)
{ƒintƒsumƒ=ƒ0;
ƒƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒa.length;ƒi++)
ƒƒ{ƒsumƒ=ƒsumƒ+ƒa[i];
ƒƒ}
ƒƒreturnƒsum;
}
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 568
568
CHAPTER 10 | ARRAYS
We can find the sum of the entire income array by passing sum a row at a time:
publicƒintƒgetTotalIncome()
{ƒintƒtotalƒ=ƒ0;
ƒƒforƒ(intƒrƒ=ƒ0;ƒrƒ<ƒthis.income.length;ƒr++)
ƒƒ{ƒtotalƒ=ƒtotalƒ+ƒthis.sum(this.income[r]);
ƒƒ}
ƒƒreturnƒtotal;
}
A final use of the array-of-arrays view is when rows of the array have different lengths.
For example, Blaise Pascal explored the many properties of a pattern of numbers that
has come to be known as “Pascal’s Triangle.” The first five rows of the triangle are
shown in Figure 10-24. The first and last element of each row is 1. The elements in
between are the sum of two elements from the row before it.
(figure 10-24)
1
Pascal’s Triangle
1 1
1 2 1
1 3 3 1
1 4 6 4 1
A 2D array to store the first 10 rows of Pascal’s Triangle can be declared and allocated
with the following statement:
int[][]ƒpascalƒ=ƒnewƒint[10][];
Notice that the last pair of brackets is empty. This causes Java to allocate only one
dimension of the array. We can now allocate the rest of the array—with each row hav-
ing the appropriate length—with the following loop. It first allocates a 1D array the
correct length and then inserts it into the pascal array.
ƒƒ// the array must still be initialized with the correct values!
}
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 569
569
forƒ(intƒrƒ=ƒ0;ƒrƒ<ƒpascal.length;ƒr++)
{ƒforƒ(intƒcƒ=ƒ0;ƒcƒ<ƒpascal[r].length;ƒc++)
ƒƒ{ƒSystem.out.print(pascal[r][c]ƒ+ƒ"\t");
ƒƒ}
ƒƒSystem.out.println();
}
In Chapter 9, we saw how to display a single image from a file. In this section, we’ll com-
bine that capability with arrays to display a simple animation. The principle of this ani-
mation approach is to store a sequence of images in an array. The image displayed is
switched from one image to the next quickly enough that it fools the eye into thinking
there is smooth motion. Our example will use the six images shown in Figure 10-25.
When shown repeatedly in quick succession, the eyes appear to roll. The images them-
selves were created with a graphics program that can create .gif files.
(figure 10-25)
Listing 10-17 and Listing 10-18 work together to show two happy face images, one
with the eyes rolling clockwise and the other with the eyes rolling counterclockwise.
One goes through the array forward as it displays the images; the other goes through
the array backward as it displays the images.
The main method for the program is shown in Listing 10-17 and follows our standard
pattern: Create the components we need (two instances of a custom component named
AnimateImage), put them in an instance of JPanel, and then put the panel in an
instance of JFrame.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 570
570
CHAPTER 10 | ARRAYS
Lines 25-28 start two threads, one for each animation. Just like threads allowed robots
in Section 3.5.2 to move independently and simultaneously, these threads allow each
animation to run independently of the other.
The component that actually does the animation is shown in Listing 10-18. Its key fea-
tures are the following:
➤ An array to store the images comprising the animation is declared (line 10)
and initialized with the images (lines 28–31).
➤ An instance variable, currentImage, holds the array index of the image cur-
rently being displayed.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 571
571
572
CHAPTER 10 | ARRAYS
Listing 10-18: A component that shows images in sequence to produce an animation (continued)
33 ƒƒƒƒthis.setPreferredSize(newƒDimension(
34 ƒƒƒƒƒƒƒƒƒƒƒƒthis.images[0].getIconWidth(),
35 ƒƒƒƒƒƒƒƒƒƒƒƒthis.images[0].getIconHeight()));
36 ƒƒ}
37
38 ƒƒ/** Paint the current image on the screen. */
39 ƒƒpublicƒvoidƒpaintComponent(Graphicsƒg)
40 ƒƒ{ƒsuper.paintComponent(g);
41 ƒƒƒƒImageƒimgƒ=ƒthis.images[this.currentImage].getImage();
42 ƒƒƒƒg.drawImage(img,ƒ0,ƒ0,ƒnull);
43 ƒƒ}
44
45 ƒƒ/** Run the animation. */
46 ƒƒpublicƒvoidƒrun()
47 ƒƒ{ƒwhileƒ(true)
48 ƒƒƒƒ{ƒ// Select the next image and call for the system to repaint the component.
49 ƒƒƒƒƒƒ// If this.dir is negative, the remainder operator doesn't work as desired. Add
50 ƒƒƒƒƒƒ// this.images.length to compensate.
51 ƒƒƒƒƒƒthis.currentImageƒ=ƒ(this.currentImageƒ+ƒthis.direction
52 ƒƒƒƒƒƒƒƒƒƒƒƒ+ƒthis.images.length)ƒ%ƒthis.images.length;
53 ƒƒƒƒƒƒthis.repaint();
54 ƒƒƒƒƒƒtryƒ
55 ƒƒƒƒƒƒ{ƒThread.sleep(100); // Use the sleep method in the Java library.
56 ƒƒƒƒƒƒ}ƒcatchƒ(InterruptedExceptionƒex)ƒ
57 ƒƒƒƒƒƒ{// ignore
58 ƒƒƒƒƒƒ}
59 ƒƒƒƒ}
60 ƒƒ}
61 }
10.8 Patterns
Many patterns involve arrays. They include initialization and changing the size of an
array, as well as many algorithms. This section contains only a sampling of what could
be considered patterns in this chapter.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 573
573
10.8 PATTERNS
10.8.1 The Process All Elements Pattern
Context: You have a collection of values stored in an array and need to perform the
same operation on all of them.
Solution: Use a for loop to process each element of the array, one element with each
iteration of the loop. The following code template applies:
forƒ(«elementType»ƒ«elementName»ƒ:ƒ«arrayName»)
{ƒ«statements to process element»
}
For example, to print the names of all the elements in the persons array:
forƒ(Personƒpƒ:ƒthis.persons)
{ƒSystem.out.println(p.getName());
}
Consequences: Each element in the array is processed by the statements inside the loop.
If the array happens to be partially filled, the preceding form will cause a null pointer
exception. Then the alternate form, which uses an explicit index and an auxiliary vari-
able, should be used.
Related Patterns: The Process Matching Elements, Find an Extreme, Selection Sort,
and many other patterns are specializations of the Process All Elements pattern.
Context: You have an indexed collection and are interested in objects in the collection
that satisfy a particular property. You want to do one of the following tasks:
➤ determine whether an element satisfying the property exists in the collection
➤ determine the position of the first or last element in the collection that satisfies
the property
➤ retrieve the first or last element in the collection that satisfies the property
Solution: Write a method that takes the criteria that identify the desired element as one
or more parameters. Use the Process All Elements pattern to test each element of the
array against the criteria. An element satisfying them can be saved and returned after
the loop, or more efficiently, returned as soon as it is found. The following code tem-
plate uses the early return approach and assumes a partially filled array.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 574
574
CHAPTER 10 | ARRAYS
publicƒ«typeOfElement»ƒ«methodName»(«type»ƒ«criteria»)
{ƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒ«auxVar»;ƒi++)
ƒƒ{ƒ«typeOfElement»ƒ«elem»ƒ=ƒ«arrayName»[i];
ƒƒƒƒifƒ(«elem»ƒsatisfiesƒ«criteria»)
ƒƒƒƒ{ƒreturnƒ«elem»;
ƒƒƒƒ}
ƒƒ}
ƒƒreturnƒ«failureValue»;
}
This basic pattern has many variations. Some of the differences are whether the array
is partially filled, whether the element is guaranteed to be found, and whether you
want to know whether such an element exists, its position, or the element itself.
Many people prefer to use a while loop instead of a for loop. In that case, use the fol-
lowing variant of the pattern. The while loop depends on short circuit evaluation to
stop the loop when the element is not found. For this to work, the test for the index
being in bounds must be first.
publicƒ«typeOfElement»ƒ«methodName»(«type»ƒ«criteria»)
{ƒintƒiƒ=ƒ0;
ƒƒwhileƒ(iƒ<ƒ«auxVar»ƒ&&ƒ
ƒƒƒƒƒƒƒƒƒ!(«arrayName»[i]ƒsatisfiesƒ«criteria»))
ƒƒ{ƒi++;
ƒƒ}
ƒƒifƒ(iƒ==ƒ«auxVar»)ƒƒ{ƒreturnƒ«failureValue»;ƒƒƒƒ}
ƒƒelseƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ{ƒreturnƒ«arrayName»[i];ƒƒƒƒ}
}
Related Patterns: Some variations of this pattern are similar to the Process All Elements
pattern.
575
meaningful
allocating
space
type
es
tim
declaring
me
variables initializing
so
by
e
elements
am
ar e
ond
first by
es
by
sec
th
indices
ird
ve
th th are
ha
i
dw us
ch
e ua
prepared nc
ea
er e lly
for use ref
multiple are algorithms sequence
values numbers
suc
be
with ha
s
must
ed
old ro cess
p
su
h
such as
are process all
ch
elements
as
arrays
may
have
multiple
ma
may be re
dimensions find an
yb
sort extreme
e
allocated
partially
filled arrays have additi
onal algorithms such as insert
trac
k fi suc
lled ha
to
elem s
ent delete
sw
ith
an
change size
auxiliary
variable
Written Exercises
10.1 In Section 10.4.1, it was noted that assigning null to an unused element after a
deletion from a partially filled array is not strictly necessary. Explain why a pro-
gram should work as implemented without that step. Drawing pictures may help.
10.2 Consider the code shown in Section 10.6.3 that swaps two rows of a 2D array.
a. Draw four diagrams, each one similar to Figure 10-23, that trace the three
lines of code. Assume the array has five rows with three columns each and
that r is one and s is three.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 576
576
CHAPTER 10 | ARRAYS
b. Write pseudocode for a method that swaps two rows by swapping individ-
ual elements rather than entire rows.
10.3 Write patterns, in the same style as Section 10.8, for the following:
a. Declaring, allocating, and initializing a filled array where the initial values
are read from a file
b. Finding an extreme element
c. Deleting an element from a specified index in an unsorted, partially filled array
d. Inserting an element into a sorted, partially filled array
e. Enlarging a partially filled array
Programming Exercises
10.4 In Section 10.1.7, we found the oldest person by comparing the ages of everyone
in the array. This, however, is accurate only to the nearest year. On 364 days of
the year, a person born April 1, 1987 and another born April 2, 1987 will be the
same number of years old—yet one is clearly older than the other. Rewrite the
findOldestPerson method to compare their birth dates rather than their ages.
With this modification, two people must be born on exactly the same day and year
to be considered equally old. You will need to add a method to the Person class.
10.5 Write a method named split. This method is passed a Scanner object. It
reads all of the tokens up to the end of the file, returning them as a filled array
of strings (no blanks or nulls). Do not use the split method in the String
class nor any of the collection classes.
10.6 The package becker.xtras.hangman includes classes implementing the
game of Hangman. Figure 7-13 has an image of the user interface. Extend
SampleHangman. Your new constructor should read a file of phrases that you
create and store them in an array. Override the newGame() method to choose a
random phrase from the array and then call the other newGame method with
the chosen phrase. Create a main method, as shown in the package overview,
to run your program.
10.7 Complete the countSubset helper method discussed in Section 10.3.
10.8 Write a method named add that adds a new Person object into a sorted, par-
tially filled array. You may find Figure 10-17 helpful for this.
10.9 Implement a method with the signature voidƒdelete(intƒd) that deletes the
element at index d from a partially filled array.
a. Assume the partially filled array is unsorted.
b. Assume the array is sorted.
10.10 Write a program that reads a series of daily high temperatures from a file. Print
out the number of days that each high was achieved. If you normally think of
temperatures in degrees Celsius, assume the temperatures fall between -40° and
50°. If you normally think in Fahrenheit, assume they fall between -40° and 110°.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 577
577
Programming Projects
10.13 Write a program implementing a robot bucket brigade. The bucket brigade con-
sists of some number of RobotSEs positioned on consecutive intersections. There
are a number of Thing objects (buckets) on the same intersection as the first
robot in the brigade. When the program executes, the first robot will pick up one
Thing and move it to the next robot’s intersection, put it down, and return to its
original position. The next robot will then move the Thing one more position
down the line, and so on. When the brigade is finished, all the Things will be at
the other end of the line of robots, one intersection beyond the last robot.
10.14 Implement a class named SortTest. It asks the user for an array size, a file-
name, and a sorting algorithm. It then allocates an array of strings the given
length and fills it by reading tokens from the file. If the file doesn’t have
enough tokens, close it and begin reading again from the beginning. When the
array is filled, sort it using either Selection Sort or the sort method imple-
mented in java.util.Arrays (an implementation of MergeSort). Use the
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 578
578
CHAPTER 10 | ARRAYS
(figure 10-26)
Image transformation
graphical user interface
579
(figure 10-27)
a. Write a main method, as described in the package overview, so that you can
play a game of Jotto using the supplied SampleWordList and
SampleGuessEvaluator classes together with the supplied user interface.
b. Write a class named WordList that implements the interface IWordList.
Modify your main method to run the program using your new class.
Implement it using a completely filled array.
c. Write a class named WordList that implements the interface IWordList.
Modify your main method to run the program using your new class.
Implement it using a partially filled array that includes an addWord method
which enlarges the array as required.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 580
580
CHAPTER 10 | ARRAYS
(figure 10-28)
10.19 Consider Table 10-1. It gives distances between pairs of cities, similar to the
charts found in some road atlases. Write a class, Distances, that has an
instance variable referring to a 2D array storing the distances. Initialize the
array from a file.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 581
581
Stratford 45 61 0 149
4
110
45ƒƒƒƒ61
107ƒƒ194ƒƒƒƒ149
where the first line gives the number of cities and the remaining lines give the
distances between cities X and Y where the index of city X is less than the
index of city Y. Note that this data corresponds to the lower left corner of
Table 10-1.
a. Write a constructor that reads this data but constructs a full 2D array, the
same as Problem 10.19.
b. Write a constructor that reads this data into a 2D array where each row is
only long enough to store the required data.
c. Add methods that perform the same calculations as a, b, d, and e in
Problem 10.19.
10 Chapter C5743 40143.ps 11/30/06 1:15 PM Page 582
Chapter 11 Building Quality Software
Chapter Objectives
After studying this chapter, you should be able to:
➤ Identify characteristics of quality software, both from the users’ and programmers’
perspectives
➤ Follow a development process that promotes quality as you develop your programs
➤ Avoid common pitfalls in designing object-oriented programs
➤ Include defensive programming measures to make errors more likely to expose
themselves so they can be fixed
➤ Explain characteristics of quality user interfaces and describe an iterative method-
ology for developing them
Suppose your rich uncle offered you a choice of two automobiles. One is known for its
high performance, luxurious leather interior, and precision workmanship. The other is
underpowered, needs frequent repair, and will probably have visible body rust before
it’s five years old. Both are free, no strings attached. Which would you take?
Most of us have a highly developed sense of quality. It’s sometimes hard to define
exactly what quality is, but given a choice, we prefer the higher quality option.
This chapter begins by describing what to look for in high-quality software. The rest of
the chapter describes how to improve the quality of the software we write.
583
CHAPTER 11 | BUILDING QUALITY SOFTWARE 584
Sometimes we can take measurements that correspond very well to quality. For exam-
ple, an automobile’s top speed or time to accelerate from 0 to 60 miles per hour are
easily measured and indicate the car’s performance pretty well. Similarly, the number
of threads per inch is easily measured and often corresponds to the quality of a gar-
ment’s fabric. The quality of other characteristics such as an automobile’s reliability or
the style of a garment is more subjective and must be measured less directly. For exam-
ple, one could survey car owners for the number of repairs they have required over the
last 5 years or survey shoppers on their reactions to a garment.
In the remainder of this section we’ll examine characteristics that are important to “the
degree of excellence” or quality of software.
The user of a program judges its quality based on many characteristics. Three of the
most important, however, are correctness, usability, and reliability.
A program is correct if it meets its specifications. A payroll program that ignores over-
time or withholds too much tax would not be correct. A Web browser that only dis-
plays the first five paragraphs of a Web page cannot be considered correct. However, a
program can be missing your favorite feature and still be correct. The key is whether it
meets its specification—whether it does what it is supposed to do—correctly. It’s a fact
of life, however, that all but the simplest programs will be less than 100% correct.
The usability of a program is determined by the effort required to learn, operate, pre-
pare input, and interpret output when compared to the alternatives. A more usable
program is a higher quality program. But usability is subjective and must take the user
into account. A beginning digital photographer may want a very simple program to
crop photos and touch up “red-eye.” The “ease-of-use” of this program would be
nothing but frustration to a professional photographer that also wants to manipulate
the color balance or merge parts of one image into another.
It may seem strange, at first, to consider quality from any other perspective than the
user. However, when buying a new car, your mechanic may tell you to avoid certain
models because they are difficult to repair. From the mechanic’s perspective, the mod-
els differ in quality. New home buyers often have the home inspected by someone
trained to look beneath the paint. The inspector determines whether the foundation is
sound or whether shortcuts were taken in insulating the windows. From the inspector’s
perspective, two homes that look the same may have different levels of quality.
Similarly, two programs may look exactly the same to the user but have radically dif-
ferent quality levels to programmers. From their perspective, a quality program makes
their life easier because it is understandable, testable, and maintainable.
Understandable Programs
Understandability is important because most programs are read by many people over
many years. A program in an insurance company may be written and debugged by a
team of programmers over several months. Understandability helps the team work
together. Several months later, another programmer may read the code to correct bugs.
Five years later, the program may need extensive modifications to accommodate a new
product offered by the company and two years after that it may again need modifica-
tion to accommodate a change to the business practice of the company. In each of these
situations, the programmer’s life is improved by working with understandable code.
Most professional programs last much longer than those written by students learning
to program. A typical student program is written in a few hours or at most a few
weeks. After it is handed in, someone reads it, assigns a grade, and the program’s use-
ful life is over. With such short lifespans and so few people involved, program under-
standability has less importance—although it never hurts to ensure that the person
assigning the grade can understand what you’ve written!
Understandability becomes more important as the size of the program grows. A typical
student program may contain between twenty and several hundred lines of code. At this
scale, programmers can keep most of the important details for the entire program in
mind or can easily remind themselves if they forget. Not so for commercial programs
that may involve between several thousand and several million lines of code. In such pro-
grams, understandability is vital to successfully writing or fixing the program.
CHAPTER 11 | BUILDING QUALITY SOFTWARE 586
Testable Programs
Programmers need to test their code. It’s a simple fact of a programmer’s life. Designing
software that is easy to test makes this part of programming easier and more enjoyable,
and improves the program’s quality from the programmer’s perspective.
It is also likely to improve the program’s quality from the user’s perspective. Recall that
correctness is a major factor. A testable program is more likely to be tested, and is
therefore more likely to be correct.
A testable program has classes with a single, well-defined purpose. Methods have a sin-
gle purpose and few dependencies on other parts of the code. A testable program has
an infrastructure for testing, allowing it to be easily tested whenever it is modified
using the techniques discussed in Section 7.1.
Maintainable Programs
As noted earlier, a typical program is often changed over its lifetime to accommodate
new requirements. A high-quality program is maintainable if it is easy to find and cor-
rect bugs, easy to adapt to new requirements, and easy to improve its overall quality in
a process called refactoring. Refactoring modifies the code to improve its quality but
doesn’t actually change what the program does.
Many development processes have been proposed over the years. The one we will use
is illustrated in Figure 11-1. It combines the best of the older, plan-first development
processes with the newer, object-oriented and agile development processes.
Program development begins with defining the requirements. Upon completing each KEY IDEA
stage, development proceeds to the next stage as shown by the heavier arrows. At each User involvement is
stage, perhaps with the exception of implementing scenarios, lots of interaction with vital to a successful
users should be expected. project.
In the following sections we’ll examine each of the major stages shown in Figure 11-1
and illustrate the process with a case study.
587
The first stage of the development process is defining the requirements, also known
as the specification. The requirements are a written statement of what the program is
supposed to do. Depending on the complexity of the problem, it could be several
paragraphs or many pages.
Requirements often start with a single person’s idea, but are usually developed by talk-
ing with people who are expected to use the program. Questions might include:
➤ How do you currently do the job (without the program)?
➤ What are the good parts and the bad parts of your current approach?
➤ What capabilities would you like to have that you don’t have now?
Eventually, the answers to these questions are written down. They might result in a
document that contains information similar to the paragraphs shown in Figure 11-2.
We will use it as a running example for the remainder of this section.
CHAPTER 11 | BUILDING QUALITY SOFTWARE 588
(figure 11-2)
The video store system is used to rent videos to the store’s customers.
Requirements for a video
The system has a list of videos and a list of customers. Each video has a unique
store system
identifying number, title, and genre. The system must be able to add new customers and
videos to these lists, as well as remove inactive customers and videos no longer in
circulation.
Customers are charged a rental fee of $.99 for videos released more than one year ago
and $2.99 for new releases, plus any accumulated late fees, when they rent a video.
Customers are assessed late fees if a video is returned past its due date. Customers
have a rental history to help resolve late fee disputes.
Requirements are seldom complete. This is a fact of life that programmers must deal
with, rather than the way it should be. Incomplete requirements mean that program-
mers typically need to go back to the users with questions about what the system
should do. Some of the issues these requirements should address, but don’t, include the
following:
➤ Can the store have multiple copies of the same video?
➤ Can a customer rent more than one video at one time?
➤ Are there additional fees such as taxes?
➤ Will other pricing strategies be offered? For example, three videos for three
nights at a reduced rate?
➤ What kind of reports are required?
➤ What are the possible genres?
➤ Does this program run on a single computer or many? (Running on many
computers with coordinated data is much beyond the scope of this book.)
For this example, we will assume the store can have multiple copies of a video and that cus-
tomers can rent more than one video at a time. We will ignore the other issues for now.
The second stage of the software development process is designing the program’s archi- KEY IDEA
tecture. The architecture refers to how the most important classes interact with each Defining the
other. Crucial decisions here have consequences throughout the life of the program, so architecture includes
it’s important to get it right. Designing the architecture consists of five tasks, as shown some of the most
important and far-
in Figure 11-1. The five tasks are as follows:
reaching decisions of
➤ Identify the most important classes and methods for the program using an the project.
analysis of the nouns and verbs in the requirements.
➤ Summarize the responsibilities and collaborators for each class on index cards.
➤ List scenarios in which the software will be used.
➤ Walk through the scenarios using the index cards to further develop the
responsibilities and collaborators.
589
(figure 11-3)
1. Read the description of what the program is supposed to do, highlighting the nouns and noun
Object-based design phrases. These are the objects your program must declare. If there are any objects that cannot
be directly represented using existing types, define classes to represent such objects.
methodology (reproduced
from Figure 8-13) 2. Highlight the verbs and verb phrases in the description. These are the services. If a service is
not predefined:
a. Define a method to perform the service.
b. Store it in the class responsible for providing the service.
3. Apply the services from Step 2 to the objects from Step 1 in a way that solves the problem.
Applying this methodology is easier if the specification is rewritten using simpler sen-
tences. All sentences have a subject and a predicate. The subject is a noun or noun
phrase and provides the answer of who or what did the action. The predicate contains
a verb and explains the action or condition of the subject.
The rewritten specification should use a verb in the active voice. Such a sentence has a
subject that does something or is in a state of being. The alternative is a passive voice
where the subject receives the action. “Customers are charged a rental fee of $.99 …”
is passive. “Customers pay a rental fee of $.99 …” is active.
The rewritten specification should also remove connecting words like “and” in the
predicates, wherever possible. This will introduce some verbal redundancy. For exam-
ple, “The system has a list of videos and a list of customers” will turn into two sen-
tences: “The system has a list of videos” and “The system has a list of customers.”
Such rewriting must be done carefully to ensure that the meaning is unchanged.
The result of rewriting the requirements in Figure 11-2 is shown in Figure 11-4. The
verb is underlined in each case.
CHAPTER 11 | BUILDING QUALITY SOFTWARE 590
With the requirements in this form, we can more easily use the nouns and verbs to
identify classes and methods, as suggested by the methodology in Figure 11-3. The fol-
lowing guidelines are relevant:
➤ Nouns in the sentence’s subject are almost always relevant classes.
➤ Some nouns will represent instance variables in a class. Examples from
Figure 11-4 include “unique identifying number” and “title.” Nouns that
can be represented using existing types such as int and String or occur
with a verb such as has are particularly likely to be instance variables rather
than new classes.
➤ Look at nouns in the predicate as well. For example, “rental history” looks
like it might be a class we need to write.
➤ Name classes with singular nouns. If we need many videos or customers, we
will construct many Video or Customer objects.
➤ Don’t let adjectives fool you. They describe or modify a noun, but rarely rep-
resent a new class. For example, we do not need one class for customers and
another class for inactive customers (“inactive” is the adjective).
➤ Sometimes synonyms or abbreviations are given for the same thing—for
example, “video store system” and “system.” Choose just one name to repre-
sent all of these different ways of saying the same thing.
➤ Class names are important. Take enough time to find just the right words to
describe the objects they represent. In this case, Video, Customer, and
RentalHistory are fairly obvious choices. “The system” needs more work.
VideoStore is one reasonable choice to encompass the whole “system.”
The predicates in the rewritten requirements represent the responsibilities of each class.
Responsibilities come in two flavors: information the class must know or derive, and
actions the class must be able to carry out. The first kind of responsibility is often rep-
resented by possessive verbs such as have, has, or keeps. The second kind is often rep-
resented by active verbs such as add, remove, rent, or charge.
591
KEY IDEA CRC cards are a handy way to record the classes and responsibilities identified in the
CRC cards list a previous step. CRC stands for Classes, Responsibilities, and Collaborators. The cards
class’s are usually made from 4 x 6 inch index cards and are divided into three areas, as
responsibilities and shown in Figure 11-5.
collaborators.
(figure 11-5)
The top area of the CRC card contains the name of the class. Below the class name, the
responsibilities are listed on the left side. The right side contains a list of the collaborators.
The collaborators section is a recognition that classes usually do not act alone. They col-
laborate with other classes to do their work. For example, to rent a video, the
VideoStore class will likely need to work with instances of the Video and Customer
classes. Hence, both classes are listed to the right. A collaborator is only listed once even
though it may be involved with several responsibilities.
It’s important to use real index cards rather than making these lists on a computer
because in one of the following steps we will distribute the CRC cards to people in a
group. The size is also important. These cards are meant to represent an overview of
the class. If we can’t express it in the space on one card, we are using too much detail.
That’s why the closely related actions of adding and removing customers are com-
pressed into a single responsibility in Figure 11-5.
These CRC cards represent the classes likely to play the most important roles within
the program. As such, they form the foundation of the program’s architecture.
CHAPTER 11 | BUILDING QUALITY SOFTWARE 592
However, we now set the CRC cards aside while we develop scenarios. The scenarios
will eventually be used with the cards to further develop their responsibilities and
collaborations.
Develop Scenarios
A scenario is a specific task that a user might want to do with the program. Scenarios
are also known as use cases. We will use scenarios to simulate the program to gain a
better understanding of what classes are needed, the services they need to support, and
how the classes interact with each other.
A complex system could have hundreds or even thousands of scenarios. List as many as
you can think of.
CRC cards and scenarios are brought together in a walk-through. A walk-through pro-
ceeds through one scenario in an orderly fashion to develop the tasks that each class
must perform. A walk-through works best with a group of people in which each per-
son is assigned one of the CRC cards. That person gives voice to the class’s responsi-
bilities as the group walks through the scenario. If a group of people is not available,
the process can be simulated by just one person.
KEY IDEA Customer: “I don’t have any responsibilities related to that. I guess I could have
Defining a name or an ID number. I’ll write that down on my card.”
responsibilities often (Customer adds has info like name and ID to his card.) “I could also
identifies required
have a responsibility to determine if a given name or ID matches
instance variables.
me.” (Customer adds determine if given name or ID matches me.)
VideoStore: “So I need to have a way to get a customer name or an ID from the
user. I can also see that eventually I’m going to need information to
identify a video, too. But I’ve already got a long list of responsibilities
from the noun/verb analysis!”
Video: “What if we had a user interface (UI) class to collect that kind of infor-
mation? That would off-load some of that responsibility from you.”
KEY IDEA VideoStore adds UI as a collaborator. Someone makes a new CRC card for UI and
New CRC cards are adds the responsibilities accept customer ID or name and accept video ID as respon-
often added during a sibilities. VideoStore is added as a collaborator.
walk-through.
VideoStore: “OK. So now I can get a customer number from the UI and collabo-
rate with Customer to find the specific customer. I can do the same
with Video to find the video. So Video, you already have the respon-
sibility to have info like ID, title, and genre from our noun/verb analy-
sis. You’d better do like Customer did and add determine if given ID
matches me. I’ll also add the responsibility find a video.
“Now I’ve got a Video and a Customer and I’m supposed to rent
one to the other. I’d really like to give that responsibility to someone
else. Video, can you rent yourself to the Customer?”
CHAPTER 11 | BUILDING QUALITY SOFTWARE 594
Video: “I suppose so. But I recall that the Customer class has
responsibilities for having a rental history and paying rental
fees. I think it would make more sense for him to have that
responsibility.”
Video: “You really want that information so you know how much to
charge, right? I think it would be better if you just asked me
that question directly rather than whether I’m a new release.”
Customer: I also need to add the video to the rental history. So I’ll add
the responsibility add video to rental history and add
RentalHistory as a collaborator.
RentalHistory: “That makes me think that I’m just a list of some other kind
of object. I think “rental history” is really just an
ArrayList in the Customer class. I propose renaming
myself to just Rental. Then I would have the responsibility
of having information about one rental—mainly the video,
the date rented, and the due date.”
The RentalHistory card is thrown away and a new one named Rental is created.
The responsibility to have info like video, date rented, date due is added.
RentalHistory collaborates with Video. The collaborator on the Customer card is
changed from RentalHistory to just Rental.
We’ll stop our transcript here. To finish this scenario the participants need to decide
how to charge the customer for the rental and perhaps for accumulated late charges.
After finishing this scenario, the group should walk through additional scenarios. It’s KEY IDEA
good to do several radically different scenarios early to verify that the evolving archi- Walk through several
tecture can handle them. The architecture often changes quite a bit while walking scenarios.
through the first several scenarios, but then settles down to a stable design. Eventually
scenarios will not result in new CRC cards or collaborations and will produce only a
few new responsibilities. At that point, the walk-through process can stop.
A walk-through blurs the distinction between objects and classes. In practice, it doesn’t
matter. The same person can represent all the instances of one class and generally does-
n’t need to keep track of them as distinct objects.
595
The final step in designing the architecture is to develop a class diagram from the CRC
cards. “Knowing” responsibilities often turn into instance variables. The other responsibil-
ities turn into methods. Collaborations turn into associations between classes. The CRC
cards developed so far for the video store yield the class diagram shown in Figure 11-6.
UserInterface
-VideoStore model
+UI(...)
The main body of the development methodology, shown in Figure 11-1, consists of
four repeated steps called the development cycle:
➤ Choose a set of scenarios to implement.
➤ Implement those scenarios.
➤ Evaluate the resulting system with users.
➤ Refine the design based on user evaluation, perhaps by adding new require-
ments or scenarios or revising the existing ones.
CHAPTER 11 | BUILDING QUALITY SOFTWARE 596
In a large project, the development team will repeat this development cycle many times.
With each iteration, they have a program that is closer to the finished product, and with
adequate feedback from users, refined into a product that actually meets their needs.
The development cycle begins with choosing a subset of the scenarios to implement.
The choice should be made with the users. Which scenarios will they find most useful?
Which scenarios are required for the most basic functionality? For example, we need to
add videos and customers before we can rent videos. So we might choose adding videos
and customers as the first scenarios to implement.
These scenarios form the basis for the remainder of the cycle.
The implementation phase is when the code is actually written. As illustrated by the
development methodology diagram in Figure 11-1, implementation itself is iterative
within the larger iterative process.
Each implementation iteration begins with choosing one scenario—for example, adding
a video. This scenario is represented in the class diagram by the addVideo method.
Next, write tests to determine if the scenario is implemented correctly. Recall that a test LOOKING BACK
involves five steps: Testing was first
➤ Decide which method you want to test. In this case, addVideo will be our pri- discussed in
Section 7.1.
mary concern.
➤ Set up a known situation. For example, a brand new VideoStore object that
has zero videos.
➤ Determine the expected results of executing the chosen method.
➤ Execute the code you want to test.
➤ Verify the results. For example, verify that calling addVideo causes the
VideoStore object to have one more video than before. Verifying that the
video can also be retrieved is another good test.
Testing a command such as addVideo also requires queries to determine the current
state of the VideoStore class.
1 publicƒclassƒVideoStoreƒextendsƒObject
2 {ƒ// Methods omitted.
3
4 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
5 ƒƒ{
6 ƒƒƒSystem.out.println("Testing adding a video...");
7 ƒƒƒVideoStoreƒvsƒ=ƒnewƒVideoStore();
8 ƒƒƒTest.ckEquals("no videos",ƒ0,ƒvs.getNumVideos());
9 ƒƒƒvs.addVideo("Star Wars: A New Hope",ƒGenre.SCI_FI);
10 ƒƒƒTest.ckEquals("one video",ƒ1,ƒvs.getNumVideos());
11
12 ƒƒƒ// test finding by name
13 ƒƒƒVideoƒv1ƒ=ƒvs.findVideo("Star Wars: A New Hope");
14 ƒƒƒTest.ckEquals("found video",ƒtrue,ƒ
15 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒv1.isMatch("Star Wars: A New Hope"));
16
17 ƒƒƒ// test finding by id
18 ƒƒƒVideoƒv2ƒ=ƒvs.findVideo(v1.getID());
19 ƒƒƒTest.ckEquals("found video",ƒtrue,ƒ
20 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒv2.isMatch("Star Wars: A New Hope"));
21
22 ƒƒƒ// test not found condition
23 ƒƒƒVideoƒv3ƒ=ƒvs.findVideo("Gone with the Wind");
24 ƒƒƒTest.ckIsNull("not found",ƒv3);
25 ƒƒ}
26 }
Another set of tests should be written in the Video class. Among them, tests for
isMatch and tests to verify that each instance of Video is assigned a unique identifi-
cation number.
KEY IDEA After writing the tests, implement the methods required so that the tests pass. This
Some programmers means actually compiling the program (and fixing the compile-time errors) and run-
use the mantra “Test a ning it (and fixing any bugs the tests expose).
little, code a little; test
a little, code a little….” When the scenario passes its tests, choose another scenario and repeat the process.
Keep choosing new scenarios, writing tests, and writing code until all the scenarios for
this development cycle are implemented.
Finally, reexamine the program in light of the new code that has been added. There
may be areas that are overly complex or where code is duplicated. Take the time to
simplify it (refactor) before moving on.
CHAPTER 11 | BUILDING QUALITY SOFTWARE 598
After the scenarios for this development cycle have been implemented, evaluate the
resulting program with users. Does it do what they expect? Are there ways to improve
how they interact with the program? Does it spark new ideas for how the program can
be used to do their jobs more efficiently?
Users’ needs change over time. In fact, introducing the program itself changes users
and their needs. It could be that what they thought was needed when the project began
is very different now—and the project must adapt to that new reality.
The evaluation step may require refining the design. Perhaps the requirements them-
selves need to be refined. Perhaps a key scenario needs to be changed with a new walk-
through, or it is realized that some scenarios won’t actually be needed, or (more likely)
that some scenarios need to be added.
After refining the design, repeat the development cycle again, beginning with choosing
a new set of scenarios to develop.
Waterfall model
Design
Implementation
Testing
Deployment
Maintenance
The iterative approach offers at least six specific advantages (in no particular order):
➤ In the iterative approach, bugs are produced, identified, and fixed in small
groups. In the waterfall model, many bugs are produced all at once during the
implementation phase, and then must be found and fixed all at once during
599
Our examination will concentrate on just two classes: Mailbox and Message. The
Message class models a single message that has been either sent or received. It has
instance variables to store the sender, recipient, date, subject, and the body of the mes-
sage. The Mailbox class stores and manipulates a number of Message objects. It has
an instance variable, msgs, that is a partially filled array of Message objects. It also
has methods such as sendMessage, replyToMessage, and deleteMessage.
The program uses two instances of Mailbox, one for the “in box” (received messages)
and another for the “out box” (sent messages).
CHAPTER 11 | BUILDING QUALITY SOFTWARE 600
The code for these two classes is shown in Listings 11-2 and 11-3. A class diagram for
the program is shown in Figure 11-8. You should take some time to examine these
classes and try to understand what they do and how they work. Remember, the code
has many poor design decisions. This is not code to emulate! Many of these poor
choices are shown in annotations within the listing.
Listing 11-2: A very poor implementation of the Mailbox class for an e-mail program
ch11/email/
1 importƒjava.util.Scanner;
2 importƒjava.io.*;
3 importƒbecker.util.*;
4
5 /** A mailbox holds messages for an e-mail program.
6 *
7 * @author Jack Rehder, Byron Weber Becker */
8 publicƒclassƒMailboxƒextendsƒObject
601
Listing 11-2: A very poor implementation of the Mailbox class for an e-mail program (continued)
52 ƒƒƒƒin.close();
53 ƒƒ}
54
55 ƒƒpublicƒvoidƒsendMessage(Stringƒrecipient,ƒ
56 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒStringƒsubject,ƒStringƒbody)
57 ƒƒ{ƒMessageƒmƒ=ƒnewƒMessage();
58 ƒƒƒƒm.recipientƒ=ƒrecipient; Constructor apparently does nothing.
59 ƒƒƒƒm.setSubject(subject);
60 ƒƒƒƒm.dateƒ=ƒnewƒDate();
61 ƒƒƒƒm.setBody(body); Public instance variables
leave data unprotected.
62 ƒƒƒƒm.senderƒ=ƒthis.owner;
63
64 ƒƒƒƒthis.addMessage(m);
65
66 ƒƒƒƒPrintWriterƒoutƒ=ƒthis.openOutputFile("outbox.txt");
67 ƒƒƒƒout.println("From: "ƒ+ƒm.senderƒ+ƒ"\nTo: "ƒ+ƒm.recipient
68 ƒƒƒƒƒƒƒƒ+ƒ"\nDate: "ƒ+ƒm.getDate().numeric()ƒ+ƒ"\nSubject: "
69 ƒƒƒƒƒƒƒƒ+ƒm.getSubject()ƒ+ƒ"\n"ƒ+ƒm.getBody());
70 ƒƒƒƒout.close();
This code is repeated five times
71 ƒƒ}
(look for the other four times).
72
73 ƒƒpublicƒvoidƒsaveMessage(intƒmsgNum,ƒStringƒfilename)
74 ƒƒ{ƒPrintWriterƒoutƒ=ƒthis.openOutputFile(filename);
75 ƒƒƒƒout.println("From: "ƒ+ƒthis.msgs[msgNum].senderƒ+ƒ"\nTo: "
76 ƒƒƒƒƒƒƒƒ+ƒthis.msgs[msgNum].recipientƒ+ƒ"\nDate: "
77 ƒƒƒƒƒƒƒƒ+ƒthis.msgs[msgNum].getDate().numeric()ƒ+ƒ"\nSubject: "
78 ƒƒƒƒƒƒƒƒ+ƒthis.msgs[msgNum].getSubject()ƒ+ƒ"\n"
79 ƒƒƒƒƒƒƒƒ+ƒthis.msgs[msgNum].getBody());
80 ƒƒƒƒout.close();
81 ƒƒ}
82
83 ƒƒpublicƒvoidƒsave()
84 ƒƒ{ƒPrintWriterƒoutƒ=ƒ
85 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒthis.openOutputFile(this.currentMboxFile);
86
87 ƒƒƒƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.size;ƒi++)
88 ƒƒƒƒ{ƒMessageƒmƒ=ƒthis.msgs[i];
89 ƒƒƒƒƒƒout.print("From: "ƒ+ƒm.senderƒ+ƒ"\nTo: "ƒ+ƒm.recipientƒ
90 ƒƒƒƒƒƒƒƒƒƒ+ƒ"\nDate: "ƒ+ƒm.getDate().numeric()ƒ+ƒ"\nSubject: "ƒ
91 ƒƒƒƒƒƒƒƒƒƒ+ƒm.getSubject()ƒ+ƒ"\n" +ƒm.getBody()ƒ+ƒ"EOM\n");
92 ƒƒƒƒ}
93 ƒƒƒƒout.close();
94 ƒƒ}
603
95
96 ƒƒpublicƒvoidƒreplyToMessage(intƒi,ƒStringƒbody,ƒ
97 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒMailboxƒoutBox)
98 ƒƒ{ƒMessageƒreplyƒ=ƒnewƒMessage();
99 This method is too long and complex.
100 ƒƒƒƒreply.setDate(newƒDate()); Much of the code belongs elsewhere.
101 ƒƒƒƒStringƒsenderƒ=ƒthis.msgs[i].recipient;
102 ƒƒƒƒreply.senderƒ=ƒsender;
103 ƒƒƒƒStringƒrecipientƒ=ƒthis.msgs[i].sender;
104 ƒƒƒƒreply.recipientƒ=ƒrecipient;
105 ƒƒƒƒStringƒsubjectƒ=ƒthis.msgs[i].getSubject();
106 ƒƒƒƒreply.setSubject("Re: "ƒ+ƒsubject);
107 ƒƒƒƒDateƒdƒ=ƒthis.msgs[i].getDate();
108
109 ƒƒƒƒStringƒwhoƒ=ƒthis.msgs[i].sender;
110 ƒƒƒƒStringƒBEGINƒ=ƒ"**On "ƒ+ƒd.numeric()ƒ+ƒ", "
111 ƒƒƒƒƒƒƒƒ+ƒwho.substring(0,ƒwho.indexOf('<')).trim()ƒ
112 ƒƒƒƒƒƒƒƒ+ƒ" wrote:\n";
113 ƒƒƒƒStringƒENDƒ=ƒ"**end original message**\n\n";
114
115 ƒƒƒƒStringƒorigMsgƒ=ƒthis.msgs[i].getBody();
116 ƒƒƒƒStringƒreplyBodyƒ=ƒBEGINƒ+ƒorigMsgƒ+ƒENDƒ+ƒbody;
117 ƒƒƒƒreply.setBody(replyBody);
118
119 ƒƒƒƒPrintWriterƒoutƒ=ƒthis.openOutputFile("outbox.txt");
120 ƒƒƒƒout.println("From: "ƒ+ƒreply.senderƒ+ƒ"\nTo: "ƒ
121 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ+ƒreply.recipient
122 ƒƒƒƒƒƒƒƒ+ƒ"\nDate: "ƒ+ƒreply.getDate().numeric()ƒ+ƒ"\nSubject: "
123 ƒƒƒƒƒƒƒƒ+ƒreply.getSubject()ƒ+ƒ"\n"ƒ+ƒreply.getBody());
124 ƒƒƒƒout.close();
125 ƒƒƒƒoutBox.addMessage(reply); More repeated code.
126 ƒƒ}
127
128 ƒƒpublicƒvoidƒdeleteMessage(intƒn)
129 ƒƒ{ƒforƒ(intƒiƒ=ƒn;ƒiƒ<ƒthis.sizeƒ-ƒ1;ƒi++)
130 ƒƒƒƒ{ƒthis.msgs[i]ƒ=ƒthis.msgs[iƒ+ƒ1];
131 ƒƒƒƒ}
132 ƒƒƒƒthis.size--; No documentation.
133 ƒƒ}
134
135 ƒƒpublicƒvoidƒaddMessage(Messageƒm)
136 ƒƒ{ƒifƒ(this.sizeƒ==ƒthis.msgs.length)
CHAPTER 11 | BUILDING QUALITY SOFTWARE 604
Listing 11-2: A very poor implementation of the Mailbox class for an e-mail program (continued)
137 ƒƒƒƒ{ƒthis.growArray();
138 ƒƒƒƒ}
139 ƒƒƒƒthis.msgs[this.size]ƒ=ƒm;
140 ƒƒƒƒthis.size++;
141 ƒƒ} Many methods that should
142 be private are public.
143 ƒƒpublicƒvoidƒgrowArray()
144 ƒƒ{ƒMessage[]ƒtempƒ=ƒnewƒMessage[this.msgs.lengthƒ*ƒ2];
145 ƒƒƒƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.msgs.length;ƒi++)
146 ƒƒƒƒ{ƒtemp[i]ƒ=ƒthis.msgs[i];
147 ƒƒƒƒ}
148 ƒƒƒƒthis.msgsƒ=ƒtemp;
149 ƒƒ}
150
151 ƒƒpublicƒintƒgetSize()
152 ƒƒ{ƒreturnƒthis.size;
153 ƒƒ} This kind of processing
belongs in the Message class.
154
155 ƒƒpublicƒStringƒgetMessage(intƒi)
156 ƒƒ{ƒreturnƒ"From: "ƒ+ƒthis.msgs[i].senderƒ
157 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ+ƒ"\nTo: "ƒ+ƒthis.msgs[i].recipient
158 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ+ƒ"\nDate: "ƒ+ƒthis.msgs[i].getDate().numeric()
159 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ+ƒ"\nSubject: "ƒ+ƒthis.msgs[i].getSubject()ƒ+ƒ"\n"
160 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ+ƒthis.msgs[i].getBody();
161 ƒƒ}
162
163 ƒƒpublicƒStringƒmakeDateString(Dateƒd)
164 ƒƒ{ƒifƒ(d.isEquivalent(newƒDate()))
165 ƒƒƒƒ{ƒreturnƒ"Today";
166 ƒƒƒƒ} These methods have little to do
167 with the core purpose of the
168 ƒƒƒƒintƒmonthƒ=ƒd.getMonth(); class and should be elsewhere.
169 ƒƒƒƒintƒyearƒ=ƒd.getYear();
170 ƒƒƒƒintƒdayƒ=ƒd.getDay();
171
172 ƒƒƒƒStringƒwkDayƒ=ƒthis.dayOfWeek(year,ƒmonth,ƒday);
173 ƒƒƒƒreturnƒwkDayƒ+ƒ", "ƒ+ƒmonths[month-1]ƒ+ƒ" "ƒ+ƒdayƒ+ƒ" "ƒ+ƒyear;
174 ƒƒ}
175
176 ƒƒpublicƒStringƒdayOfWeek(intƒyear,ƒintƒmonth,ƒintƒday)ƒ
177 ƒƒ{ƒintƒaƒ=ƒ(int)ƒMath.floor((14ƒ-ƒmonth)ƒ/ƒ12);
178 ƒƒƒƒintƒyƒ=ƒyearƒ-ƒa;
179 ƒƒƒƒintƒmƒ=ƒmonthƒ+ƒ12ƒ*ƒaƒ-ƒ2;
605
180 ƒƒƒƒintƒdƒ=ƒ(dayƒ+ƒyƒ+ƒ(int)Math.floor(yƒ/ƒ4)ƒ
181 ƒƒƒƒƒƒƒƒ-ƒ(int)Math.floor(y/100)ƒ+ƒ(int)Math.floor(y/400)ƒ
182 ƒƒƒƒƒƒƒƒ+ƒ(int)ƒMath.floor((31ƒ*ƒm)/12))ƒ%ƒ7;
183
184 ƒƒƒƒreturnƒthis.days[d];
185 ƒƒ}
186 ƒƒƒ
187 ƒƒpublicƒPrintWriterƒopenOutputFile(Stringƒfilename)
188 ƒƒ{ƒtryƒ
189 ƒƒƒƒ{ƒreturnƒnewƒPrintWriter(filename);
190 ƒƒƒƒ}
No attempt to handle
191 ƒƒƒƒcatchƒ(Exceptionƒex)ƒ the error.
192 ƒƒƒƒ{ƒex.printStackTrace();
193 ƒƒƒƒƒƒSystem.exit(1);
194 ƒƒƒƒ}
195 ƒƒƒƒreturnƒnull;
196 ƒƒ}
197 }
Listing 11-3: A very poor implementation of the Message class for an e-mail program
ch11/email/
1 /** Store one message in the mail program.
2 *
3 * @author Jack Rehder; Byron Weber Becker */
4 publicƒclassƒMessageƒextendsƒObject
5 { public instance variables
6 ƒƒpublicƒStringƒsender; are open to abuse.
7 ƒƒpublicƒStringƒrecipient;
8 ƒƒpublicƒDateƒdate;
9 ƒƒpublicƒStringƒsubject; Constructor doesn’t initialize
10 ƒƒpublicƒStringƒbodyƒ=ƒ""; instance variables.
11
12 ƒƒpublicƒMessage() Set methods required to
overcome inadequacies in
13 ƒƒ{}
the constructor.
14
15 ƒƒpublicƒvoidƒsetDate(intƒy,ƒintƒm,ƒintƒd)
16 ƒƒ{ƒthis.dateƒ=ƒnewƒDate(y,ƒm,ƒd); This class should provide
17 ƒƒ} services for its clients
18 other than simply storing
information.
19 ƒƒpublicƒvoidƒsetDate(Dateƒd)
CHAPTER 11 | BUILDING QUALITY SOFTWARE 606
Listing 11-3: A very poor implementation of the Message class for an e-mail program (continued)
20 ƒƒ{ƒthis.dateƒ=ƒd;
21 ƒƒ}
22
23 ƒƒpublicƒDateƒgetDate()
24 ƒƒ{ƒreturnƒthis.date;
25 ƒƒ}
26
27 ƒƒpublicƒvoidƒsetSubject(StringƒaSubject)
28 ƒƒ{ƒthis.subjectƒ=ƒaSubject;
29 ƒƒ}
30
31 ƒƒpublicƒStringƒgetSubject()
32 ƒƒ{ƒreturnƒthis.subject;
33 ƒƒ}
34
35 ƒƒpublicƒvoidƒsetBody(StringƒaBody)
36 ƒƒ{ƒthis.bodyƒ=ƒaBody;
37 ƒƒ}
38
39 ƒƒpublicƒStringƒgetBody()
40 ƒƒ{ƒreturnƒthis.body;
41 ƒƒ}
42 }
In this section we will discuss a number of rules of thumb for writing quality code. In
Section 11.3.2 we will put them into a larger framework.
You probably noticed that the Mailbox and Message classes have almost no docu-
mentation. If you read the code for comprehension, you probably wished that it had
more documentation to help you understand it.
Documentation, or the lack of it, obviously affects how easily a class or method can be
understood. That, in turn, affects how easily the code can be maintained and, to some
extent, tested.
607
The Mailbox constructor contains a loop within a loop. Such structures are difficult
for programmers to understand. Simplify them by putting the inner loop into an
appropriately named helper method. Naming the task helps you clarify the responsi-
bilities of both the outer loop and the inner loop.
Research published by psychologist George A. Miller in 1956 shows that people can
only store and process a limited amount of information in short term, or working,
memory. The limit is often given as seven chunks of information, plus or minus two.
This research supports writing short methods. Methods are more likely to be written and
understood correctly if they have fewer chunks of information. The most appropriate
way to write such methods is using stepwise refinement, as discussed in Chapter 3.
The Mailbox class has at least two methods that are too long: the constructor and
replyToMessage. Instead of a constructor that is 32 lines long, consider one that uses
a helper method to read one message:
publicƒMailbox(Stringƒfilename,ƒStringƒownerInfo)
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒthrowsƒFileNotFoundException
{ƒsuper();
ƒƒthis.ownerƒ=ƒownerInfo;
ƒƒthis.currentMboxFileƒ=ƒfilename;
ƒƒScannerƒinƒ=ƒnewƒScanner(newƒFile(filename));
ƒƒwhileƒ(in.hasNextLine())
ƒƒ{ƒMessageƒmsgƒ=ƒthis.readOneMessage(in);
ƒƒƒƒthis.addMessage(msg);
ƒƒ}
ƒƒin.close();
}
CHAPTER 11 | BUILDING QUALITY SOFTWARE 608
In this version, all the details of reading a message, lines 30–49 in Listing 11-2, are col-
lapsed into readOneMessage. That method might be further refined using two more
helper methods, readHeader and readBody.
Using helper methods provides at least three benefits. First, each helper method gives
the code it contains a name. A well-chosen name helps identify what the code does,
making the helper method easier to understand. Second, the name summarizes the
code, making the client that calls it easier to read and understand. Third, helper meth-
ods make testing easier. The code in each helper method can often be tested separately,
which is easier than testing the same code written as a single method.
Methods that do not need to be called from outside their containing class should be
declared private. For example, there is no reason to call readOneMessage from out-
side the Mailbox class. The same is true for growArray and probably for
makeDateString and dayOfWeek.
Making these methods private offers two advantages. First, it prevents programmers
from using them inappropriately, either maliciously or by mistake. For example, a pro-
grammer may think it is his responsibility to call growArray before adding a message
to the mailbox. Doing so could slow the program dramatically and could waste a huge
amount of memory. Taking active steps to prevent such problems can make the pro-
gram as a whole more bug free.
Second, when a bug does occur, appropriate use of accessibility helps track it down by
elimination. If a problem occurs within a private method, we know with absolute cer-
tainty that it was called from within the same class. With non-private methods, we
might be surprised to find that it was called from some place completely unexpected.
The code in Listing 11-2 has five places where essentially the same code is repeated.
Take a moment to find them. The code itself isn’t exactly the same, but the results are.
The repeated code assembles an e-mail message into a string to be saved or displayed in
the user interface and occurs in lines 67–69, 75–79, 89–91, 120–123, and 156–160.
This is undesirable for a number of reasons:
➤ It makes the class longer, with more code for programmers to read and
understand.
➤ If a change to the way messages are represented is required, there are five
copies of the code that need to change.
609
The solution is to make a helper method that can be called from many places in the pro-
gram rather than duplicating the code itself. In fact, it may appear that getMessage is
precisely the helper method we need:
155 ƒƒpublicƒStringƒgetMessage(intƒi)
156 ƒƒ{ƒreturnƒ"From: "ƒ+ƒthis.msgs[i].senderƒ
157 ƒ +ƒ"\nTo: "ƒ+ƒthis.msgs[i].recipient
158 ƒ +ƒ"\nDate: "ƒ+ƒthis.msgs[i].getDate().numeric()ƒ
159 ƒ +ƒ"\nSubject: "ƒ+ƒthis.msgs[i].getSubject()ƒ
160 ƒ +ƒ"\n"ƒ+ƒthis.msgs[i].getBody();
161 ƒƒ}
This method can be used to replace code in saveMessage (lines 75–79) and save
(lines 89–91), but is much more difficult for sendMessage (lines 67–69) and
replyToMessage (lines 120–123). Passing an index as the argument works for the
first two methods, but the last two work with Message objects that are not yet in an
array and thus can’t be accessed with an index.
LOOKING AHEAD One solution is writing a helper method, formatMessage, with a Message object as
Later, we consider the a parameter. Then getMessage, above, can be written as follows:
question “Which class
155 ƒƒpublicƒStringƒgetMessage(intƒi)
should contain the
helper method?” It 156 ƒƒ{ƒreturnƒthis.formatMessage(this.msgs[i]);
will lead to an even 161 ƒƒ}ƒ
better solution.
and lines 89–91 can be rewritten as follows:
89 ƒƒƒƒout.print(this.formatMessage(m)ƒ+ƒ"EOM\n");
In addition to reducing wasted time and energy, putting duplicate code into a method
gives you an added abstraction to work with. If you’ve already used the same code sev-
eral times, chances are good that it represents a higher-level idea, or abstraction, within
your code. Putting it in a method increases the chances that you’ll recognize when it is
appropriate to use it, and makes using it trivial—just call the method.
Mailbox and Message both have many public instance variables. This is unfortunate
for several reasons. First, it makes the classes more difficult to change. For example,
the Message class uses Strings to store the sender and receiver. They both have two
CHAPTER 11 | BUILDING QUALITY SOFTWARE 610
distinct parts, the real name and the e-mail address in angle brackets, as in the follow-
ing example:
ByronƒWeberƒBeckerƒ<[email protected]>
It may be decided later that it would be better to define a class named Contact for this
information. Instances could store the real name in one field and the e-mail address in
another. Other information might be added such as nicknames, telephone numbers, or
mailing addresses.
If the instance variables were private, making this change would be straightforward.
We would have to find all the places inside the Message class that accessed either
sender or recipient and change them to use the new Contact class. However, if
the instance variables are public our search must expand beyond the containing class
to include the entire program. In fact, it is possible for many programs to use a given
class—and any of them might need changing if they used the instance variable instead
of an appropriate method.
A second reason to keep instance variables private is to prevent misuse, either acciden-
tally or maliciously. For example, a programmer writing the user interface may need to
know the number of messages in an instance of Mailbox. He might write
this.mBox.msgs.length, failing to realize that msgs is a partially filled array and
that the number of messages does not correspond to the space in the array. Or perhaps
you wrote the Mailbox class and the programmer working on the user interface has
either a grudge against you or a really wacky sense of humor and adds the following in
an obscure part of the user interface code:
ifƒ(Math.random()ƒ<ƒ0.01)
{ƒthis.mBox.size--;
}
The effect of this insertion is that one message is lost from the mailbox 1% of the times
the above code executes. It seems like a bug in the Mailbox class, but who would think
to look in a completely unrelated part of the user interface? You could spend a lot of
time tracking down this “bug” and face a lot of pressure from management while
you’re doing it! The best policy is to simply avoid the issue by making instance vari-
ables private.
In the case of the Message class, two constructors might be appropriate. The first
would be used in the Mailbox constructor to read one message from a file. It would
take a single argument, an open Scanner object. The second could be used by the
replyToMessage and sendMessage methods to construct an object from the con-
stituent pieces.
Powerful constructors make the Message class more understandable because it elimi-
nates the need for many set methods. Many bugs arise from variables being given
incorrect values. By minimizing the places where new values are given, bugs are made
less likely and those that do exist are easier to find.
The largest change, and the biggest benefit, to the Mailbox and Message classes
comes from keeping data and processing together. The major clues that these classes
don’t keep them together are:
➤ Message is a “container” class. It contains information, but doesn’t have any
methods that actually process that information.
➤ Mailbox gets many individual pieces of data from instances of Message and
then processes them in some way.
KEY IDEA A better approach is to have the class with the data do the processing. For example, the
The class with the formatMessage helper method we described earlier really belongs in the Message
data should do the class, not the Mailbox class. By moving it, you can avoid passing an argument and use
processing. the instance variables directly instead of using the get methods.
This code also strongly hints that a message should save itself to a file rather than
expecting the Mailbox class to do it. After all, the message is where the data that
requires saving is located.
A class is mutable if its instances can be changed after they are constructed. A class is
immutable if instances can not be changed after construction. An example of an
immutable class is String. There are no methods to change a string once it has been
created, only methods that create a new string that is similar to the old one in some
way. For example, the name.toUppercase() method does not change the string
name; it creates a new string like name except that lowercase characters are converted
to uppercase.
Immutable classes are simple. Their objects have only a single state—the state in which
they were created. If the state is correct when it’s created, it will be correct for all time
without any further work by you or the programmers using the class. References to
immutable classes can be shared freely because there is no way to change the object’s
state. Mutable classes, on the other hand, can have objects in a wide variety of states
613
Classes that represent a value, like Date or String, should almost always be made
immutable. All other classes should be made as immutable as possible. It obviously
isn’t possible to make Mailbox immutable because messages need to be added and
deleted from it. But Message can be made immutable. Once created, there is no reason
to change a message.
If you want to really go the extra mile for your immutable classes, you need to follow
two more rules:
➤ Use the final keyword for instance variables. For example, private final
String subject in the Message class emphasizes that subject’s value
should not change after the first assignment. This is enforced by the compiler.
➤ Use the final keyword for the class as a whole. For example, public final
class Message prevents someone from overriding the methods in the
Message class and changing their behaviors.
The DateTime class is mutable, even though it represents a value and should be
immutable according to the criteria given earlier. Suppose you wanted an immutable
Date class. Extending DateTime doesn’t work because methods like addDays would
still be available via inheritance. But DateTime has a lot of functionality that would be
good to reuse.
The solution is for the Date class to have an instance of DateTime as a private
instance variable. It can then provide exactly the methods it requires and omit the
problematic ones. For example, see Listing 11-5. Of course, nothing prevents you from
adding new methods to the class as well.
CHAPTER 11 | BUILDING QUALITY SOFTWARE 614
1 publicƒfinalƒclassƒDateƒextendsƒObject
2 {
3 ƒƒprivateƒfinalƒDateTimeƒmyDate;
4
5 ƒƒpublicƒDate(intƒyear,ƒintƒmonth,ƒintƒday)
6 ƒƒ{ƒsuper();
7 ƒƒƒƒthis.myDateƒ=ƒnewƒDateTime(year,ƒmonth,ƒday);
8 ƒƒ}
9
10 ƒƒ// For internal use only. Assumes that the caller does NOT keep a reference to d.
11 ƒƒprivateƒDate(DateTimeƒd)
12 ƒƒ{ƒsuper();
13 ƒƒƒƒthis.myDateƒ=ƒd;
14 ƒƒ}
15
16 ƒƒpublicƒintƒgetYear()ƒƒ{ƒƒreturnƒthis.myDate.getYear();ƒƒ}
17 ƒƒpublicƒintƒgetMonth()ƒ{ƒƒreturnƒthis.myDate.getMonth();ƒ}
18
19 ƒƒ// Return a new date, adding the given number of days.
20 ƒƒpublicƒDateƒaddDays(intƒhowMany)
21 ƒƒ{ƒDateTimeƒcopyƒ=ƒnewƒDateTime(this.myDate);
22 ƒƒƒƒcopy.addDays(howMany);
23 ƒƒƒƒreturnƒnewƒDate(copy);ƒ
24 ƒƒ}
25 ƒƒ// etc.
26 }
A class should represent a single abstraction. The Message class should model an
e-mail message and the Mailbox class should be focused only on managing a group of
messages. Mixing in peripheral concepts makes a class harder to understand, test, and
maintain. However, in Listing 11-2, the Mailbox class does just that.
The problem is that, in addition to its core responsibility of managing messages, the
Mailbox class also has responsibilities for formatting dates. The major clues are
instance variables storing the names of days and months along with methods to turn a
date into a formatted string and calculate the day of the week.
Particularly in a program where a Date class already exists, these instance variables and
methods should be moved there. In general, a class should delegate work to “helper
615
Limiting each class to one cohesive set of responsibilities makes the class easier to
understand. It’s easier to test one set of responsibilities than to test two or more that
are intertwined in the same class. Finally, if changes need to be made, it’s easier to fig-
ure out where and actually make the changes in well-focused classes.
Many of these coding heuristics relate to four features that have been long recognized
as crucial to quality code. These four features have stood the test of time, across many
different programming languages and development methodologies. They seem to be
invariant. Defined in terms of Java, they are:
➤ Encapsulation—Grouping data and related services into a class.
➤ Cohesion—The extent to which each class models a single, well-defined
abstraction and each method implements a single, well-defined task.
➤ Information hiding—Hiding and protecting the details of a class’s operation
from others.
➤ Coupling—The extent to which interactions and dependencies between classes
are minimized.
KEY IDEA Each of these relates to a fundamental problem in constructing software: managing
Managing complexity complexity. Most programs have a huge number of details, each affecting the overall
is one of the hardest correctness of the system. Managing them so that a change to one detail does not cre-
parts of writing ate a problem with another is difficult!
software.
Let’s define detail, for the moment, as either a method or an instance variable, and define
interaction as a method calling another method or accessing an instance variable.
We can get an idea of the complexity of a program by making a diagram with circles
representing details and lines representing interactions. For example, consider a class
with one instance variable and three methods. Two methods access the instance vari-
able and one method calls the third as a helper method. The complexity diagram
would appear as shown in Figure 11-9. The instance variable is shown with crossed
lines within it.
Figure 11-10 shows a diagram for a considerably more complex program. We will use
it to show how the ideas of encapsulation, cohesion, information hiding, and coupling
can help us manage complexity.
(figure 11-10)
Unconstrained
interactions between
details
The best solution to date for managing complexity is to impose voluntary constraints KEY IDEA
on how we write programs so that some interactions can’t happen, and to organize the Java allows us to write
other interactions so that they are easier to think about. Encapsulation, cohesion, poorly designed
information hiding, and coupling all have a role to play in managing complexity programs. As
programmers, we
through voluntary constraints. must choose to write
excellent programs.
Encapsulation
(figure 11-11)
KEY IDEA The first group are interactions between details within the same class. Of all the possi-
Encapsulation divides ble interactions in our program, encapsulation focuses our attention on a group that
interactions into works together using closely related details. These details, and the interactions between
those within a class
them, are the primary concern of the programmer or small group of programmers
and those between
classes. responsible for implementing and maintaining the class.
The second group of interactions are the primary concern of programmers using the
class—the interactions between details in different classes. Of all the possible interactions
within a program, encapsulation helps these programmers focus on the most relevant ones.
By grouping interactions inside classes and between classes, we help manage complexity.
Java’s class mechanism provides a natural way to group details. Unfortunately, Java
does not force them to be grouped—that requires good design decisions on our part.
The heuristics noted earlier that support encapsulation include the following:
➤ Keep Data and Processing Together
➤ Delegate Work to Helper Classes
CHAPTER 11 | BUILDING QUALITY SOFTWARE 618
Cohesion
In the mail program presented earlier in this chapter, one could imagine the Message KEY IDEA
class containing instance variables and methods related to both dates and contacts In a highly cohesive
(senders and receivers). This would represent “low cohesion.” The ideal is “high cohe- program, each class is
sion” in which details related to dates are split into their own class, as are the details focused on one
abstraction.
related to contacts. This kind of change is illustrated in Figure 11-12.
(figure 11-12)
Encapsulated, cohesive
classes that do not yet
meet the ideal for
information hiding and
coupling
Cohesive classes and methods make it easier for us to understand them. It’s easier to
focus on just one abstraction or one task than to understand two or more that are
mixed together.
(figure 11-13)
Information hiding
emphasizes some
details and hides the rest
from view
Information hiding distinguishes what a class can do from how it does it. The parts
that are left exposed (declared public) are always methods. Their names and docu-
mentation indicate what can be done with the class. The details of the instance vari-
ables required and the helper methods used are all hidden inside the class.
KEY IDEA For programmers who want to use a class, information hiding eliminates many possi-
Information hiding ble interactions from their consideration and helps manage the complexity.
removes many
possible interactions Another advantage, as noted before, is that hiding details allows us to change how the
from a programmer’s class operates without affecting the code that uses the class. As long as the public meth-
consideration. ods continue to behave as before, the details of just how they work can change to
accommodate better approaches.
Information hiding also allows us to limit our testing to only the public parts. If they
are tested thoroughly, we can be more relaxed about testing internal details.
Coupling
Ideally, we would like to be able to understand or change one class with only minimal
knowledge or changes of other classes. When this is true, we say the classes are
“weakly coupled.” See Figure 11-14.
(figure 11-14)
Information hiding already reduces the coupling between classes by forcing the classes
to interact only through public methods. We can go one step further, however, and ask
ourselves whether we have the right public methods.
For example, the Message class in Listing 11-3 has public instance variables but no
methods that really do anything, resulting in strong coupling. The Mailbox class is
forced to interact often with the Message class in order to do its work.
Simply hiding the instance variables and making public accessor methods would not KEY IDEA
improve the coupling, however. The Mailbox class would still need many interactions Weakly coupled
with the Message class. The coupling between these two classes improved dramati- classes use powerful
methods to minimize
cally, however, when we wrote higher-level methods like constructReply and save,
dependencies
substantially reducing the dependency on accessor methods like getSubject. between classes.
11.4.1 Exceptions
We learned about exceptions in Section 8.4. We learned that exceptions are thrown
when an exceptional circumstance arises. The exception interrupts the program’s nor-
mal flow of control and if nothing is done to intervene, the program will stop with an
error message displayed on the console. Programmers can intervene in this process
with the try-catch statement. Statements that may throw an exception are placed in
the try clause. A series of catch clauses after it can include code to handle exceptions
that are thrown.
Using exceptions effectively is an important part of writing quality software for a num-
ber of reasons. First, exceptions provide a uniform approach to reporting and handling
errors. Languages that do not support exceptions force programmers to adopt ad hoc
methods for reporting errors using an instance variable to signal that an error occurred
or returning an error code from a method. Figuring out a variety of error reporting
methods takes time and increases the probability of mistakes being made.
When using ad hoc methods to report errors, programmers often do not bother to
check the error indicators, resulting in software that sometimes fails. Forcing program-
mers to confront possible errors and decide how to handle them is the second advan-
tage of exceptions. For example, Java forces the programmer to think about how to
CHAPTER 11 | BUILDING QUALITY SOFTWARE 622
Third, exceptions separate error-handling code from the code for normal processing.
This separation makes the logic for both the normal processing and handling errors
easier to understand.
Many beginning programmers dread exceptions because they are often the first sign of
an unwelcome debugging session. This is the wrong attitude. Instead, exceptions
should be welcomed as a programmer’s friends. They tell us as soon as possible, with
helpful debugging information, what went wrong. Finding a bug that is exposed right
away, with helpful information, is much easier than debugging in languages without
exceptions. In those languages, an error may go undetected until much later in the pro-
gram’s execution. When it is finally noticed, finding its cause may be very difficult.
One excellent use for exceptions is to inform programmers when a method has been
called with inappropriate arguments. For example, consider the deleteMessage
method in the e-mail program. It takes a single argument, the number of the message
to delete. This number is really the index into the partially filled array of messages and
must be between 0 and this.getSize()-1, inclusive. If the message number is out-
side of this range, it represents a bug and an exception, typically
IllegalArgumentException, should be thrown:
publicƒvoidƒdeleteMessage(intƒmsgNum)
{ƒifƒ(msgNumƒ<ƒ0ƒ||ƒmsgNumƒ>ƒthis.getSize()-1)
ƒƒ{ƒthrowƒnewƒIllegalArgumentException("msgNum = "ƒ+ƒmsgNum
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ+ƒ"; must be 0.."ƒ+ƒ(this.getSize()-1));
ƒƒ}
The requirement that msgNum be between 0 and this.getSize()-1 is called a KEY IDEA
precondition. More generally, a precondition is anything that must be true when the Verifying
method is called for it to execute correctly. It is the responsibility of the method’s client preconditions is a
huge favor to those
to ensure that the preconditions are met. Checking the preconditions inside the method
using the method—
is simply a favor to those using the method: the method fails quickly with an appropri- which usually
ate exception that helps them find their bugs more easily. But it is a favor that should includes the
programmer who
wrote it.
623
All of these results are undesirable and easily prevented by checking parameters for
validity.
If the method’s client has met the preconditions, the method is obligated to meet its
postconditions. A postcondition is what should be true after the method executes. If
the preconditions have not been met, the postconditions likely won’t be met either.
The postcondition for deleteMessage is that the given message, n, has been removed
from the list and all the remaining messages are renumbered to fill the “hole.”
/** Delete message number n from this mailbox, renumbering all messages from
* n+1..this.getSize()-1 to have numbers n...this.getSize()-2. The renumbering
* preserves the order of the messages.
* @param n The number of the message to delete
* @throws IllegalArgumentException if n is outside the range 0..this.getSize()-1 */
Pre- and postconditions are often viewed as contracts between the client and the
method and using them consistently is called design by contract 1. The core idea of this
phrase is that each interaction between a client and a method is bound by a contract.
The contract specifies what the client and the server can each expect of the other.
The contract between a client and method is similar to the contracts we encounter in
everyday life. For example, a cell phone provider may have a contract that says if you
(the client) pay $20.00 per month, they (the server) will provide up to 500 minutes of
cell phone service per month. If you sign the contract and live up to your responsibili-
ties of paying $20.00 per month, they are obligated to provide the service. If they
don’t, you could take them to court and sue for breach of contract. On the other hand,
1 This phrase was trademarked by Bertrand Meyer, the developer of the Eiffel programming language.
Eiffel makes extensive use of the ideas behind “Design by Contract.”
CHAPTER 11 | BUILDING QUALITY SOFTWARE 624
if you don’t pay the $20.00, they are under no obligation to provide the cell phone ser-
vice. They might, but they certainly don’t have to.
11.4.3 Assertions
privateƒvoidƒgrowArray()
{ƒassertƒthis.sizeƒ==ƒthis.msgs.length;
ƒƒ...
Second, assertions can be turned off easily. Some assertions might slow the program
down unacceptably. For example, some searching techniques require the array being
searched to be sorted. Checking that precondition takes longer than doing the actual
search. Such a precondition can be used during development and debugging, but then
turned off so they have no effect on the program when deployed to users. To turn
assertion checking on, execute the program using the following command line:
javaƒ–enableassertionsƒ«ClassName»
If you use an IDE, find the place where command line arguments are set and add
–enableassertions.
It may seem natural to use assert to check preconditions as well. Throwing a speci-
fied exception, however, is a better solution in this case because it can document the
error more accurately in terms the method’s user can understand.
(figure 11-15)
Iterative user
interface design and Design
evaluation process
User Testing
and Evaluation
Prototyping
In the user evaluation and testing phase, users work with the prototype to evaluate it.
Evaluation often involves the five E’s:
➤ Effective—The completeness and accuracy with which users achieve their goals.
➤ Efficient—The speed and accuracy with which users can complete their tasks.
➤ Engaging—The degree to which the tone and style of the interface makes the
product pleasant or satisfying to use.
CHAPTER 11 | BUILDING QUALITY SOFTWARE 626
➤ Error tolerant—How well the design prevents errors or helps with recovery
from those that do occur.
➤ Easy to learn—How well the product supports both initial orientation and
deepening understanding of its capabilities.
Ideally, each of the five E’s is objectively measured and compared to a stated goal. For
example, effectiveness might be measured by having a group of users complete a pre-
scribed set of tasks, measuring their error rate. Efficiency might be measured by count-
ing keystrokes and mouse clicks on a set of realistic tasks. Whether the user interface is
engaging might be measured with user interviews or questionnaires. Counting the
“false starts” users make would be one way to measure whether the user interface is
easy to learn.
Obviously, evaluation of a user interface requires users. Having the developers act like
users is not good enough—they know too much about the application and how it works.
Modern user interfaces should give the user as much control over the process as is con-
sistent with the user’s knowledge and skill level. Whenever possible, allow the user to
choose the ordering of tasks and subtasks. Allow the user to choose between using the
keyboard or a mouse. Assume that the user will be interrupted and need to come back
to the task later. Allow the user to customize the interface to suit his own preferences.
Responsive
Users need constant feedback to tell them how the system has interpreted their com-
mands. To understand why, put on a blindfold and attempt to send an e-mail message.
How far can you get (without specialized tools to give you feedback)?
627
Another side of a responsive system is that the feedback comes fast enough to keep the
user working at full speed, whenever possible.
Understandable
Consistency is one way to keep a user interface understandable. Users will be able to
transfer what they have learned in one part of the application to another, and probably
even from one application to another. Examples of consistency include using the same
language to describe the same concepts, organizing the controls on dialog boxes in the
same way, using the same kinds of controls to achieve distinct but similar tasks, and so
on. A set of design guidelines helps achieve consistency, as does using a standard
library of user interface controls (such as javax.swing).
Structuring information and controls also helps promote understandability. For exam-
ple, the print dialog box shown in Figure 11-16 has information grouped into three
areas: Printer, Print Range, and Copies. All of the information in the Printer area is
about the physical printer—which one to use, its type, whether it’s ready, and so on.
Many psychology studies have shown that such structure makes it easier for users to
find, organize, and use the information presented to them.
(figure 11-16)
Finally, make use of the fact that people recognize information much more easily than they
recall it. For example, the drop-down list of printer names contains two names not shown
CHAPTER 11 | BUILDING QUALITY SOFTWARE 628
Forgiving
Finally, a quality user interface should be forgiving of user mistakes. Ideally, the inter-
face should prevent as many mistakes as possible by, for example, disabling commands
that are not applicable in the current context. But users will still make mistakes due to
fatigue, distraction, uncertainty, and so on. The user interface should make it easy to
correct these mistakes. It might do this by allowing the user to undo commands, or by
allowing users to correct their input and reissue commands.
PROBLEM SET
defines requirements
an iterative important
development designs defines classes and
the architecture methods
process
ver
nouns and help disco
verbs e
refin
rep
by usin ed
eat
walk
nc
g
ed
is enha
throughs use
ly
CRC cards
re
implements and
co
classes,
rd
evaluates scenarios
responsibilities
software and collaborators
quality
is im
prov
is im ed b encapsulation
prov y
ed b
y
ca
high cohesion
n
be
ev
proactively
alu
information hiding
can
responding to
at
ed
be
exceptions
fro
eva
weak coupling
m
lua
ted
understandable
fro
a programmer's programs
includes
m
perspective of
quality
includes testable
inclu programs
des
maintainable
a user's perspective programs
Written Exercises
11.1 Make CRC cards based on the transcript in Section 11.2.2. For each subprob-
lem, a–d, hand in updated CRC cards and a transcript of the walk-through.
a. Finish walking through the scenario of renting one video. For example, how
does the system’s user know how much to charge the customer?
b. Walk through adding a new customer.
c. Walk through the scenario of returning a video on time.
d. Walk through producing a report of all customers with videos more than
one week late.
CHAPTER 11 | BUILDING QUALITY SOFTWARE 630
11.2 The alarm clock case study in Section 8.3 uses the newAudioClip method in
the Applet class to load a sound file from the disk drive. If the sound file does
not exist or is in the wrong format, the newAudioClip method doesn’t do
anything at all. Is this a good idea? Defend your answer.
11.3 Is the Video class shown in Figure 11-6 immutable? Why or why not?
11.4 Explain why the Customer class, as shown in Figure 11-6, is mutable. Is this
class a good candidate for an immutable class? Why?
11.5 Expand the list of scenarios for the video store given in Section 11.2.1. (Hint: It
would be fruitful to imagine the program having just been installed in a store.
What must be done before it can be used to rent a video?) Now consider the
iterative nature of the software development process, as shown in Figure 11-1.
Organize the scenarios into three groups: the set to implement first, the set to
implement next, and the set to implement last. Give a brief rationale describing
why you grouped the scenarios as you did.
11.6 For each of the following requirements documents, hand in the following:
a. Rewritten requirements (see Figure 11-4)
b CRC cards that result from the rewritten requirements
c. List of scenarios
d. Transcript of walking through one scenario
e. CRC cards as refined by the walk-through
f. Class diagram constructed from the CRC cards
Requirements Document 1: The concert hall’s ticketing system is used to sell
concert tickets to concert hall patrons. The system has a list of patrons who
have previously purchased tickets as well as a list of upcoming concerts. It also
has a map of the concert hall showing how many rows of seats there are and
how many seats are in each row.
Users must be able to add new patrons, add newly scheduled concerts, and sell
tickets to a particular concert to patrons. Patrons may request of block of adja-
cent seats in the same row.
The hall’s manager will want periodic reports of how many tickets have been
sold for each upcoming concert as well as patrons who have purchased tickets
to more than four concerts in the last 18 months.
Requirements Document 2: A program is required to synchronize the files in
two directories. This is useful, for example, to synchronize a person’s laptop
computer with files on their primary desktop computer.
Each file has a name and a modification date (the date and time when it was
last changed). Each directory has a list of files it contains.
Input to the program are the paths to the two directories to synchronize. The
program also has a list of the files that existed the last time the directories were
synchronized. Let’s refer to the two directories as A and B and to the list as L.
631
PROBLEM SET
For each file f in directory A, the program will copy f to B if it does not appear
in either B or L (it’s a newly created file). It will delete f from A if f appears in L
but not in B (it was previously deleted from B). It will copy f to B if it appears
in B and L, and the modification date of f is newer than the modification date
of the copy in B and the copy in B is older than L (it was changed in A but not
B). It will ask the user what to do if f and the corresponding copy in B are both
newer than L.
Similar processing also occurs for each file in B.
After both directories have been processed, they should both have exactly the
same files. Write the list of those files out to use the next time the directories
are synchronized.
Requirements Document 3: The game of Adventure has a series of rooms in a
cave. Each room has a passage to at least one other room. Rooms may also
have treasures such as lamps, keys, gold, and so on. Each treasure has an asso-
ciated weight and value. A player moves between rooms with commands such
as “north,” “west,” and “down.” Each room is described when the player
enters it.
A player can pick up treasures (but the player has a limit to how much he can
carry—it can’t carry all of the treasures). The player can also put down trea-
sures it has previously picked up.
The game is over when the player enters the “quit” command. If the value of
the player’s accumulated treasures is high enough, the player is added to the
game’s top ten players list.
(For more information on the original adventure game, search the Web for
“Colossal Cave Adventure.”)
Requirements Document 4: An online auction service has a list of items for
sale. Each item has a description, a seller, a current bid, and an auction close
date. Buyers may search the list for items with descriptions that contain the
search terms they enter. If buyers see an item they want to buy, they may bid on
the item, provided the auction close date has not passed and their bid is higher
than all previous bids for the item.
Once per day, the system notifies buyers and sellers of auctions that closed
that day.
11.7 This chapter mentions the term refactor but does not describe it extensively.
Research this term on the Web and write a short essay on what the term
means. Some sites give concrete refactoring patterns. Describe two or three of
these patterns and how they can improve code quality.
CHAPTER 11 | BUILDING QUALITY SOFTWARE 632
Programming Exercises
11.8 Listing 11-4 shows how replyToMessage could be written if there was a
constructReply method in the Message class. Write constructReply.
Programming Projects
11.9 Find the code to the e-mail program shown in Listings 11-2 and 11-3. Rewrite
the program using the heuristics in Section 11.3 and adding exceptions where
appropriate. You should not need to modify MailUI.java, but other classes
may need changing and new classes may be added. The user of your rewritten
program should not be able to detect any differences between it and the origi-
nal. Only the programmers working with it will know how much the quality
has improved.
11.10 Consider the video store program discussed in Section 11.2.
a. Write code so that the tests given in Listing 11-1 will pass.
b. Walk through the scenario of adding a customer. Develop tests for this sce-
nario and implement the code required to pass the tests. Assume that a user
interface gathers the required information about the customer and provides
it to your code.
c. Walk through the scenario of renting a video to an existing customer.
Develop tests and implement the code to pass the tests. Assume that a user
interface gathers a customer identification number and a video identification
number and provides them to your code.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 633
Chapter 12 Polymorphism
Chapter Objectives
After studying this chapter, you should be able to:
➤ Write a polymorphic program using inheritance
➤ Write a polymorphic program using an interface
➤ Build an inheritance hierarchy
➤ Use the Strategy and Factory Method patterns to make your programs more flexible
➤ Override standard methods in the Object class
In science fiction movies, an alien sometimes morphs from one shape to another, as the
need arises. Someone shaped like a man may reshape himself into a hawk or a panther
or even a liquid. Later, after using the advantages the new shape gives him, he changes
back into his original shape.
Morph is a Greek word that means “shape.” The prefix poly means “many.” Thus,
polymorph means “many shapes.” The movie alien is truly polymorphic. However,
even though he has many outward shapes, the core of his being remains unchanged.
Java is also polymorphic. A class representing a core idea can morph in different ways
via its subclasses. After studying inheritance in Chapter 2, this may sound like nothing
new. However, in that chapter, we usually added new methods to a subclass. In this
chapter, we will focus much more on overriding methods from the superclass. The
power of this technique will become evident when we are free from knowing whether
we’re using the superclass or one of its subclasses.
633
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 634
634
CHAPTER 12 | POLYMORPHISM
Let’s define two rather fanciful robots that dance, one to the left and one to the right,
as they move to the next intersection. The arrows in Figure 12-1 show the paths they
take as they move from their initial position (shown in black) to their final position
(shown in white). LeftDancer is labeled with an “L” and RightDancer is labeled
with an “R”.
(figure 12-1)
L R
Paths that dancing robots
L R take as they move to the
next intersection
635
12.1 INTRODUCTION
Listing 12-1: A robot that dances to the left as it moves forward (continued)
18 ƒƒƒƒsuper.move();
19 ƒƒƒƒthis.turnRight();
TO
20 ƒƒƒƒsuper.move();
POLYMORPHISM
21 ƒƒƒƒthis.turnLeft();
22 ƒƒ}
23 }
How Java determines which method to execute is called method resolution. This con-
cept was first discussed in Section 2.6.2, but it is worth reviewing because it is impor-
tant to understand how these classes work.
When a method is invoked, say karel.move(), Java looks for the move method
beginning with the object’s class. If the object was originally created with the phrase
newƒLeftDancer(...), Java will look for the move method beginning with the
LeftDancer class. That class has a move method, so it’s executed.
On the other hand, suppose that the turnLeft method was invoked. Once again, the
search for the method begins with the object’s class, LeftDancer. That class, however,
doesn’t have a turnLeft method. The search continues in its superclass, RobotSE. It
doesn’t have a turnLeft method either and so the search continues in its superclass.
Robot has a turnLeft method; that’s the method that is executed. The search for the
method to execute starts with the object’s actual class and proceeds up the inheritance
hierarchy until it is found. If no such method exists, that fact is determined when the
program is compiled and an error message is issued.
LOOKING AHEAD When the statement uses super to call the method, the search starts at a different
What would happen place—the superclass of the class containing the method. Thus, the statement
if line 16 was super.move() at lines 16, 18, and 20 in Listing 12-1 begins to search for move in the
this.move()? See RobotSE class, executing the first move method found as it moves up the inheritance
Written Exercise 12.1.
hierarchy. In this case, it executes the move method in the Robot class, resulting in the
familiar movement from one intersection to another.
So far we haven’t seen anything new. LeftDancer could have been an assignment in
Chapter 2. So where is the polymorphism? It’s in how these classes are used.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 636
636
CHAPTER 12 | POLYMORPHISM
Let’s use these classes in a way that appears silly at first: Let’s assign a LeftDancer to
a RobotSE reference variable, as follows:
RobotSEƒkarelƒ=ƒnewƒLeftDancer(...);
Java allows this kind of assignment, as long as the reference on the right is a subclass of
the reference on the left. It would not work to assign a LeftDancer to a City variable
or even to a RightDancer variable because neither is a superclass of LeftDancer.
If we can do this, we can also put several LeftDancers and RightDancers into a sin-
gle array. Imagine a chorus line of dancing robots, as implemented in Listing 12-2. The
core feature is an array that contains all the robots, no matter what their type.
637
12.1 INTRODUCTION
For now, remember that all of the objects in the array have a move method. We can tell
each of the robots to move with the loop in lines 24–26. But how do these robots
move? Do they move like instances of RobotSE because the array is declared that way,
or do they each move like the LeftDancer, RightDancer, or RobotSE that they
TO
really are?
POLYMORPHISM
KEY IDEA The answer is that each object executes the move method in its own class. That is, a
Polymorphism uses a LeftDancer moves to the left because that’s how that kind of robot was defined to move.
subclass as if it were RightDancers move to the right, as their move method says they should. The lone
a superclass, relying
RobotSE at the end of the line moves as any other instance of RobotSE would move.
on the subclass to
override methods This is polymorphism in action: the statement chorusline[i].move() tells a robot
appropriately.
to move, but this particular statement does not need to know or care what kind of
robot it is. For example, it doesn’t need to tell the LeftDancers to move to the left. It
just tells each robot to move and that robot moves in the way it is defined to move.
This is like a choreographer telling a dance troupe to “begin on the count of three: one,
two, three.” All the dancers begin dancing their parts without individual instruction
from the choreographer.
KEY IDEA A class diagram for what we have just done is typical of polymorphic programs and is
Polymorphic shown in Figure 12-2. The characteristic feature is a superclass (RobotSE) that is
programs have key extended with at least two subclasses. Another class—DanceHall in this case—uses
identifying features.
instances of the subclasses as if they were the superclass.
(figure 12-2) *
DanceHall RobotSE
Class diagram for a -RobotSE[ ] chorusLine
polymorphic robot methods omitted methods omitted
program
LeftDancer RightDancer
+LeftDancer(...) +RightDancer(...)
+void move( ) +void move( )
publicƒclassƒLeftDancerƒextendsƒRobotSE
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 638
638
CHAPTER 12 | POLYMORPHISM
We cannot. This code will not even compile because line 1 declares that each element of
chorusline will refer to a RobotSE object or one of its subclasses. Most kinds of
robots do not have a pirouette method and so the compiler assumes the worst—that
in line 4, chorusline[i] refers to an ordinary robot that lacks a pirouette method.
The rule is this: The type of the reference variable determines the names of the methods that KEY IDEA
can be called; the type of the actual object determines which code is executed. In this exam- The reference’s type
ple, chorusline[i] is the reference variable and its type is RobotSE. Therefore, the only determines which
methods you can call are methods that appear in the RobotSE class. On the other hand, methods can be
called; the object’s
when you call one of those methods (like chorusline[i].move()), the type of the actual type determines
object (for example, LeftDancer) is what determines how the robot moves. which code is
executed.
To include the pirouette method in a dancer’s repertoire, we need to add a new class,
as shown in Figure 12-3. The Dancer class extends RobotSE and adds a pirouette
method. The DanceHall class is changed to use an array of Dancer objects rather than
RobotSE. This implies that the single RobotSE object shown at line 17 of Listing 12-2
can no longer be included in the array because it is not a subclass of Dancer.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 639
639
12.1 INTRODUCTION
(figure 12-3)
RobotSE
Using an abstract class;
abstract classes and methods omitted
methods are labeled
TO
in italics
POLYMORPHISM
* Dancer
DanceHall
-Dancer[ ] chorusLine
methods omitted +Dancer(...)
+void pirouette( )
LeftDancer RightDancer
+LeftDancer(...) +RightDancer(...)
+void move( ) +void move( )
+void pirouette( ) +void pirouette( )
Abstract Classes
When we write the code for the Dancer class, should pirouette turn to the left like
a LeftDancer or turn to the right like a RightDancer? No matter which choice we
make, it will be wrong for at least one of the subclasses.
The best option is to make pirouette an abstract method. Such a method includes
only the access modifier, return type, and signature (method name and parameter list).
The method body is replaced with a semicolon. For example:
/** Turn this dancer around 360 degrees in its preferred direction. */
publicƒabstractƒvoidƒpirouette();
KEY IDEA The purpose of an abstract method is to declare a name that can be used polymorphi-
An abstract cally, even though it does not declare how the method will be implemented.
method enables
polymorphism even Abstract methods must be overridden in a subclass to supply a method body. For
when implementation example, pirouette is overridden in LeftDancer with a method that turns to the
details are not known. left; in RightDancer, it is overridden with a method that turns to the right.
A class that declares or inherits a method without a body is called an abstract class and
must be declared with the keyword abstract, as follows:
publicƒabstractƒclassƒDancerƒextendsƒRobotSE
An abstract class such as Dancer can be extended by another class, X, even though X
does not supply a body for pirouette. However, X must also be declared abstract.
640
CHAPTER 12 | POLYMORPHISM
When an abstract method or class is shown in a class diagram, its name will be in ital- KEY IDEA
ics, as shown in Figure 12-3. Names of abstract
methods and classes
are shown in italics in
12.1.3 Examples of Polymorphism class diagrams.
Let’s take a brief break from robots to examine a number of other examples where
polymorphism may be appropriate. All of these cases have the same basic structure as
the DanceHall example shown in Figure 12-2. In Figure 12-4, we give the participat-
ing classes more general names so that in the examples that follow, we can identify
how the classes interact. We will use the names as follows:
➤ The client class uses the services of another class. In the previous example,
DanceHall is the client that uses the services (move) of another class—it just
happens to use them polymorphically.
➤ The abstract class is used to declare variables in the client. It also lists the methods
that can be used by the client. In Figure 12-2, RobotSE is the abstract class; in
Figure 12-3, it’s Dancer. (The class that defines the names used polymorphically
is called “abstract” even though it might not use the abstract keyword.)
➤ A concrete class implements the methods named in the abstract class. In the pre-
vious example, LeftDancer and RightDancer are both concrete classes.
(figure 12-4)
Client * Abstract
-Abstract[ ] list Common pattern for
inheritance-based
polymorphism
Concrete1 Concrete2
A Bank class (the client) has many Accounts (the abstract class). The Account class
has both an instance variable to maintain the account’s balance and methods to deposit
money, withdraw money, and transfer money to another account. It also has methods
to get the balance and to charge a service fee at the end of the month. See Figure 12-5.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 641
641
12.1 INTRODUCTION
(figure 12-5) *
Bank Account
Class diagram for a bank -Account[ ] accounts -double balance
methods omitted +Account(...)
+void deposit(...)
+void withdraw(...)
TO
+void transfer(...)
POLYMORPHISM
+void getBalance( )
+void serviceFee( )
MinBalanceAccount PerUseAccount
-boolean wentBelowMin -int numWithdrawals
+MinBalanceAccount(...) +PerUseAccount(...)
+void withdraw(...) +void withdraw(...)
+void serviceFee(...) +void serviceFee(...)
With this design, the Bank class can process every transaction in the same way. It doesn’t
need to know or care what kind of account the customer has because each account will
Polymorphic Call handle the transaction in a manner that is appropriate for that account.
A drawing program constructs a drawing out of different kinds of shapes: ovals, rec-
tangles, lines, polygons, characters, and so on. In this case, Drawing would be the
client class. It has an array of Shape objects. Shape is the abstract class. It’s most cru-
cial method is draw.
Classes like Oval, Rectangle, and Line are the concrete classes that extend Shape.
Each of them override the draw method to draw the appropriate shape: an Oval draws
an oval, a Rectangle draws a rectangle, and a Line draws a line.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 642
642
CHAPTER 12 | POLYMORPHISM
With this design, the Drawing class can draw the entire image with a simple for loop,
which tells each Shape object in its array to draw itself, as follows:
forƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.numShapes;ƒi++)
{ƒthis.shapes[i].draw(...);
}
Polymorphic Call
Computer versions of chess, Monopoly, and various card games have two or more
players. Sometimes the players are people and sometimes the computer controls the
extra players. Sometimes the computer has several skill levels.
Each Player object must have a strategy for generating its next move. There might be
several ways to do this: ask a human for the next move, find the first legal move, gen-
erate a random move, or invoke some sophisticated “artificial intelligence.”
The idea of polymorphism is useful here. Player is the client class. Rather than an array,
it has a single instance variable holding a MoveStrategy object. This class is the abstract
class shown in Figure 12-4. Its most important method is getNextMove. MoveStrategy Strategy
is extended by several concrete classes: AskUserStrategy, FirstLegalStrategy,
RandomStrategy, and AIStrategy. They each override getNextMove to get the next
move for the player in their own particular way (see Figure 12-6).
RandomStrategy FirstLegalStrategy
When the game program is set up this way, a Player object can ask for its next move
without knowing or caring which particular strategy is being used to generate the
move. The strategy can even be changed mid-game by simply assigning a new subclass
of MoveStrategy to the Player object’s instance variable.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 643
643
12.1 INTRODUCTION
12.1.4 Polymorphism via Interfaces
In a polymorphic program, the client says what it wants done (move) but not how.
How the task is accomplished is determined by the details of the concrete classes.
TO
POLYMORPHISM
When polymorphism is achieved via inheritance, the abstract class and the superclass
are the same. That combination constrains both what can be done and how it can be
implemented. The superclass already contains the methods that can be called, limiting
what can be done by the client. The fact that the concrete classes extend the abstract
class means that they are not free to extend another class, thus limiting how tasks are
accomplished.
KEY IDEA Java interfaces provide another way to implement polymorphism that cleanly separates
Java interfaces what can be done from how it can be implemented. Recall from Section 7.6 that inter-
separate what an faces list method signatures and return types, but do not provide the method bodies.
object can do from For example, the following is an interface for classes that can move:
how it can be
implemented. publicƒinterfaceƒIMove
{
ƒƒ/** Move this object. */
ƒƒpublicƒvoidƒmove();
}
We can use this interface with LeftDancer and RightDancer by including the
implements keyword and the interface name in the class declaration, as follows:
publicƒclassƒLeftDancerƒextendsƒRobotSEƒimplementsƒIMove
{ƒ// Constructor omitted.
ƒƒpublicƒvoidƒmove()
ƒƒ{ƒ// Same as the move method in Listing 12-1.
ƒƒ}
}
The implements clause causes the compiler to verify that LeftDancer, or one of its
superclasses, implements all of the methods listed in the IMove interface.
DanceHall, the client class, can use the interface to declare the array of dancers, as
follows:
IMove[]ƒchoruslineƒ=ƒnewƒIMove[5];
chorusLine[0]ƒ=ƒnewƒLeftDancer(stage,ƒ1,ƒ0,ƒDirection.EAST);
However, an instance of RobotSE cannot be inserted into the array because it does not
implement IMove.
It may seem that we haven’t gained anything by introducing IMove. But imagine a
parade of robots where a LeftDancer and a RightDancer are carrying a banner. We
want the banner to float above everything in the city and display the text “Robot
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 644
644
CHAPTER 12 | POLYMORPHISM
Parade”. The banner should move as the robots move. Figure 12-7 shows two screen
captures of such a program.
(figure 12-7)
A class implementing such a banner is shown in Listing 12-3. It displays a small win-
dow that floats above all other windows. It has a move method to move it a given dis-
tance. It extends JDialog but also implements the IMove interface and can therefore
be put in the same array as the robots that carry it, as follows:
ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
ƒƒ{ƒCityƒcƒ=ƒnewƒCity();
ƒƒƒƒmovers[0]ƒ=ƒnewƒLeftDancer(c,ƒ1,ƒ1,ƒDirection.EAST);
ƒƒƒƒmovers[1]ƒ=ƒnewƒRightDancer(c,ƒ3,ƒ1,ƒDirection.EAST);
ƒƒƒƒmovers[2]ƒ=ƒnewƒBanner(80,ƒ165,ƒ40,ƒ"Robot Parade");
ƒƒƒƒforƒ(intƒnumMovesƒ=ƒ0;ƒnumMovesƒ<ƒ2;ƒnumMoves++)
ƒƒƒƒ{ƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒmovers.length;ƒi++)
ƒƒƒƒƒƒ{ƒmovers[i].move();
ƒƒƒƒƒƒ}
ƒƒƒƒ}
ƒƒ}
For both the banner and the robots, the client can say what to do (move), but the
details of how they move are very different. This is the essence of polymorphism, but
using an interface instead of extending a class.
645
12.1 INTRODUCTION
Listing 12-3: A banner that floats above a city and everything in it (continued)
TO
7 {ƒprivateƒintƒx;
POLYMORPHISM
8 ƒƒprivateƒintƒy;
9 ƒƒprivateƒintƒdeltaX;
10
11 ƒƒ/** Display a message in a floating window.
12 ƒƒ*ƒ@param initX The initial x position of the banner.
13 ƒƒ*ƒ@param initY The initial y position of the banner.
14 ƒƒ*ƒ@param moveX The distance to move.
15 ƒƒ*ƒ@param msg The msg to display. */
16 ƒƒpublicƒBanner(intƒinitX,ƒintƒinitY,ƒintƒmoveX,ƒStringƒmsg)
17 ƒƒ{ƒsuper();
18 ƒƒƒƒthis.deltaXƒ=ƒmoveX;
19 ƒƒƒƒthis.xƒ=ƒinitX;
20 ƒƒƒƒthis.yƒ=ƒinitY;
21 ƒƒƒƒthis.setSize(20,ƒ60);
22 ƒƒƒƒthis.setLocation(this.x,ƒthis.y);
23 ƒƒƒƒthis.setAlwaysOnTop(true);
24 ƒƒƒƒthis.setContentPane(newƒJLabel(msg));
25 ƒƒƒƒthis.setVisible(true);
26 ƒƒ}
27
28 ƒƒ/** Move the banner. */
29 ƒƒpublicƒvoidƒmove()
30 ƒƒ{ƒthis.xƒ+=ƒthis.deltaX;
31 ƒƒƒƒthis.setLocation(this.x,ƒthis.y);ƒ
32 ƒƒ}
33 }
For example, consider an automobile rental agency that has vans, sports cars, and
sedans. If a customer calls a week ahead to reserve an automobile, the agency can sub-
stitute a van if that’s what is most available. A van is a kind of automobile and can do
everything an automobile can do. On the other hand, if the customer called to reserve
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 646
646
CHAPTER 12 | POLYMORPHISM
a van, the agency cannot substitute a sports car. Maybe the customer specifically needs
the extra passenger space provided by the van.
In Java, a subclass can always be used anywhere the superclass can be used. A KEY IDEA
LeftDancer (the subclass) can always be substituted for a Dancer (the superclass)— A subclass that does
just like a van (the subclass) can be substituted for an automobile (the superclass). things differently can
Why? Inheritance guarantees that a LeftDancer has all of the methods that a be substituted for the
superclass.
RobotSE has.
Similarly, a class such as Banner can be substituted for its interface because the com- KEY IDEA
piler guarantees that every method named in the interface will be implemented in the Classes can be
concrete class. substituted for the
interfaces they
A polymorphic program exploits the fact that even though a concrete class may be sub- implement.
stituted for the abstract class, they do not necessarily act the same way. The key feature
of a polymorphic program is setting up the classes so that some operations can be per-
formed without knowing the actual types of the objects being used.
We’ve seen that a polymorphic program can be written using either interfaces or inher-
itance. On what basis do we choose one approach over the other?
The simple rule is to use an interface unless there is some commonality between all the KEY IDEA
concrete classes that can be implemented in a superclass. Use interfaces for
polymorphism unless
In the first example, LeftDancer and RightDancer have many common details that there is a reason to
are implemented in RobotSE and its superclasses. These include the ability to move the use inheritance.
usual way, turn left or right, and display itself in the city. In the second example, there
are no such commonalities. The robots and the banner are implemented completely
differently with different superclasses—and thus an interface was appropriate.
In Chapter 11 we talked about loose coupling being a good design decision. That is,
classes should depend on each other as little as possible. Modern object-oriented
design makes extensive use of interfaces to cleanly separate what classes do from how
they do them. That is, a client that uses interfaces is less dependent than one that does-
n’t. If another concrete class becomes available that implements the interface, it can be
substituted with no change to the client. That’s loose coupling!
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 647
647
(figure 12-8) 1. Read the description of what the program is supposed to do, highlighting the nouns
Object-oriented design and noun phrases. These are the objects your program must declare.
a. If there are any objects that cannot be directly represented using existing types,
methodology
define classes to represent such objects.
b. If two or more classes have common attributes and pass the ‘is-a’ test, consolidate
those attributes into a superclass, and extend the superclass to define the classes.
2. Highlight the verbs and verb phrases in the description. These are the services.
If a service is not predefined:
a. Define a method to perform the service.
b. Place it in the class responsible for providing the service.
c. Where necessary, override methods in subclasses.
d. If a class is responsible for a service but cannot implement it, declare an
abstract method.
3. Apply the services from Step 2 to the objects from Step 1 in a way that solves
the problem.
In this section, we’ll see how this methodology works by applying it to an invoicing
application. The problem statement (or specification) and a sample invoice are shown
in Figure 12-9.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 648
648
CHAPTER 12 | POLYMORPHISM
(figure 12-9)
Print an invoice to request payment for items provided to a customer by the company.
The invoice shows the customer’s name and address, and the total invoice amount. Problem statement
for a simple invoicing
In addition to the above, add one line item for each group of identical items sold.
Each line item shows the quantity of items sold, a description, the unit cost, and the application
total amount charged for items in the group.
A sample invoice is shown below. Notice that some of the variation between different
kinds of items is shown in the description.
Computers To You
1 Byte Way
Waterloo, Ontario N2G 3H4
Step 1 in the object-oriented design methodology (Figure 12-8) tells us to highlight the
nouns and noun phrases. Recall that a noun is a word that can refer to a person, place,
or thing and is often the subject or object of a verb. The nouns and noun phrases in the
problem statement are listed in Figure 12-10 in the left column.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 649
649
Some of the nouns are not relevant and can be eliminated. For example, “payment for
items provided” is in a clause explaining the purpose of the system and represents some-
thing the customer does in response to receiving an invoice. Similarly, “group of identical
items sold” seems to define the term “line item.” These two noun phrases are crossed out
in the list.
Some nouns in the list duplicate each other. For example, two entries in the table talk about
“unit cost.” Furthermore, the sample invoice shows the hourly rate for consulting in the
unit cost column. They can probably all be combined into the single term “unit cost.”
Some of these nouns can be represented with existing types such as integers and strings.
These are noted in the middle column. Other nouns will require that we define a class, as
suggested by Step 1a of Figure 12-8. Suggested class names are shown in the right column.
Class Relationships
LOOKING BACK We’ve identified a number of potential classes in Figure 12-10. How are they related to
“Is-a” and “Has-a” each other? If we use the “is-a” and “has-a” tests, the sentence “An invoice has a
are two ways of customer” makes much more sense than “An invoice is a customer.” Similarly, “A cus-
relating classes. They
tomer has an address” and “An invoice has a line item” make more sense than saying
were discussed in
Section 8.1.3. “A customer is an address” or “An invoice is a line item.”
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 650
650
CHAPTER 12 | POLYMORPHISM
Examining the specification’s three paragraphs related to the three kinds of items indi-
cates that Goods have amounts charged, quantities, and unit costs. Services, on the
other hand, have amounts charged, quantities, unit costs per month, and the number of
months. Finally, Consulting objects have hourly rates and time spent. We see that
these classes definitely have some common attributes; therefore, Step 1b of Figure 12-8
(which suggests forming a superclass) may apply. The phrase “three kinds of items”
suggests that we might name the superclass Item and already hints that inheritance
may be appropriate.
The remaining question is whether these classes pass the “is-a” test. Recall that the “is-
a” test consists of forming a sentence using “is-a” or “is a kind of” with the two classes
in question. For example, “A Service is a kind of Item” or “A Consulting is a kind
of Item.”
These sentences don’t sound quite right. The problem might be that the inheritance
relationship isn’t correct. However, the specification explicitly says that there are
“three kinds of items: goods, services, and consulting.”
Perhaps the problem with these sentences is the names we’ve chosen. “Service” and “con- KEY IDEA
sulting” refer to what the company provided to the customer. In programming the invoic- Choose appropriate
ing system, we are really concerned with what goes on the invoice to represent the goods names for classes.
and the consulting. That is, we’re most concerned with the line items. The sample invoice
shown in Figure 12-9 has five line items. The first line item is for three computers, the sec-
ond line item is for an office suite, the third is for service contracts, and the last two line
items are for consulting.
The three “kinds of items” the specification refers to are three kinds of line items. If we
name them GoodsLineItem, ServicesLineItem, and ConsultingLineItem, then
an is-a statement like “A GoodsLineItem is a kind of LineItem” makes sense. We
can conclude that inheritance is appropriate.
These relationships are shown in Figure 12-11. Observe the striking resemblance to the
common pattern for polymorphism shown in Figure 12-4.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 651
651
Invoice LineItem
-LineItem[ ] items 1..* -int quantity
-int numItems -String description
-double unitCost
Assigning Attributes
Some of the nouns and noun phrases will correspond to attributes in these classes. An
initial assignment is also shown in Figure 12-11. The Customer and Address classes
are not relevant to the main topics of this chapter and are omitted from the rest of the
discussion.
We know from the specification’s second paragraph that each line item shows a quan-
tity, description, unit cost, and amount. These seem like good attributes to add to the
LineItem class. Before we do that, however, we should check two things. First, is it
better to compute the value or store it in an attribute? The amount seems like a value
that is better computed by a method than stored in an attribute, especially given the
extensive explanations about how to calculate it from other values.
KEY IDEA Second, before placing these attributes in the LineItem class we should ask whether
If an attribute is in a they apply to all of LineItem’s subclasses. A quick glance at the sample invoice shows
superclass, it should that each kind of line item shows all the values. Therefore we conclude that quantity,
be applicable to all description, and unit cost can go into LineItem.
the subclasses.
KEY IDEA
The time spent consulting and the number of months a service is provided are obvi-
ously unique to ConsultingLineItem and ServicesLineItem, respectively.
Some attributes do
not belong in the The remaining attributes all seem to be variations of attributes we have already discussed.
superclass.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 652
652
CHAPTER 12 | POLYMORPHISM
As with nouns, some of the verb phrases may not belong. For example, “request pay-
ment” describes the purpose of the invoice and “provide items to a customer” is some-
thing the company does. Neither are things that this computer system should do. Both
are crossed off the list.
Each line item must calculate the amount to charge for the goods, services, or consult-
ing it represents. On the other hand, we also know that these values are all calculated
differently, strongly suggesting that calcAmount should be an abstract method in
LineItem. This allows it to be called polymorphically, but defers the decision of how
to calculate the amount to the appropriate subclasses.
The sample invoice shows that the description is displayed differently for each kind of
line item. A ConsultingLineItem displays the number of hours and a
ServicesLineItem displays the number of months. This seems similar to
calcAmount, suggesting another abstract method. However, we also need an accessor
method in LineItem for description, suggesting an accessor method that is over-
ridden as needed in the subclasses.
Figure 12-13 shows the class diagram with these assignments made.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 653
653
Implementing Methods
Relevant portions of the LineItem class are shown in Listing 12-4. There is nothing
unusual about it except for the abstract method to calculate the line item’s amount
(line 23) and the resulting abstract keyword applied to the class (line 4).
654
CHAPTER 12 | POLYMORPHISM
19 ƒƒƒƒthis.unitCostƒ=ƒaUnitCost;
20 ƒƒ}
21
22 ƒƒ/** Calculate the total amount owing due to this line item. */
23 ƒƒpublicƒabstractƒdoubleƒcalcAmount();
24
25 ƒƒ// Accessor methods omitted.
26 }
This class provides a body for calcAmount (lines 20–23). Because the quantity and
unit cost of the service contracts are stored in LineItem, accessor methods are used to
get their values.
655
A12.2
CASE STUDY: INVOICES
Listing 12-5: Implementing the interesting methods in the ServicesLineItem class (continued)
16 ƒƒƒƒthis.numMonthsƒ=ƒaNumMonths;
17 ƒƒ}
18
19 ƒƒ/** Calculate the total amount owing due to this line item. */
20 ƒƒpublicƒdoubleƒcalcAmount()
21 ƒƒ{ƒreturnƒthis.getQuantity()ƒ*ƒthis.getUnitCost()ƒ
22 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ*ƒthis.numMonths;
23 ƒƒ}
24
25 ƒƒ/** Get the description of the services represented by this line item. */
26 ƒƒpublicƒStringƒgetDescription()
27 ƒƒ{ƒreturnƒsuper.getDescription()ƒ+ƒ
28 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ" ("ƒ+ƒthis.numMonthsƒ+ƒ" months)";
29 ƒƒ}
30 }
The last step in the object-oriented design methodology shown in Figure 12-8 is to
“apply the services from Step 2 to the objects from Step 1 in a way that solves the prob-
lem.” We won’t solve the entire problem here. We will focus on the print method in
Invoice to show how it uses polymorphism and the inheritance hierarchy we’ve built.
We’ll also briefly discuss how to read invoices from a file.
Printing Invoices
The following pseudocode for print follows directly from the sample invoice shown
in Figure 12-9.
This code is polymorphic because it does not need to know or care what kind of line
item object is in the array of line items. Thanks to polymorphism, the print method
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 656
656
CHAPTER 12 | POLYMORPHISM
can simply call the calcAmount and getDescription methods and they will return
a value appropriate to their actual type.
However, we should recall the lessons learned in Chapter 11. The code inside the loop
separates the processing (printing a line item) from the data (information stored in the
line item). A better design would keep the data and processing together by placing a
print method in the LineItem class. The print method in the Invoice class calls
LineItem’s print polymorphically, as shown in Listing 12-6.
657
KEY IDEA Polymorphism is at work in this example in two ways. The first is calling print poly-
Polymorphism can morphically from the Invoice class. The second is that each use of the keyword this
also occur when an inside LineItem’s print method refers to one of the three concrete classes. Therefore,
overridden method is
this.getDescription() will search for the method getDescription beginning
called from a
superclass. with the concrete class, one of GoodsLineItem, ServicesLineItem, or
ConsultingLineItem. If getDescription was overridden, the more specialized
version will be called. Furthermore, when calcAmount is called in line 13, the version
in this line item’s concrete class will be called. This is polymorphism because the client,
LineItem, doesn’t need to know or care what kind of line item it is. Because it is call-
ing methods that may be overridden, this method has a lot in common with the
Template Method pattern studied in Section 3.5.3.
Reading an invoice from a file is trickier than the examples covered in Chapter 9
because of polymorphism. The file must contain all the information needed to recon-
struct the different kinds of line items. This has two implications. First, the file must
indicate which of the various subclasses of LineItem to construct; second, the file
must store more data for some line items than for others.
KEY IDEA One possible file format is shown in the example in Figure 12-14. It contains customer
Include data in the information followed by the line items. Each line item uses two or more lines. The first
file that says what line in each group is a string indicating which class to construct. The remaining lines in
kind of subclass to the group contain the data used to initialize the objects.
construct.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 658
658
CHAPTER 12 | POLYMORPHISM
(figure 12-14)
Byron Weber Becker
122 Nomad Street Customer information One possible file format
Waterloo, ON N2L 3G1
GoodsLineItem One line item for storing line items
3 1750.00 Desktop computers
ConsultingLineItem Another line item
1 75.00 Consulting re: LAN wiring
5.00
ServicesLineItem Type of line item
3 5.95 Computer service contracts
12
Information common to all line items
ConsultingLineItem (quantity, unit price, description)
1 75.00 Consulting re: printer installation
0.75 Information specific to
GoodsLineItem ConsultingLineItem
1 750.00 Premium office suite (number of hours)
An Invoice constructor that reads this file is shown in Listing 12-8. It repeatedly
reads a line identifying the type of line item required (line 13). The cascading-if state-
ment in lines 14–22 calls the appropriate constructor based on the name that was read.
By the time control returns to line 12, all of the data for that line item has been read,
and the program is ready to read the name of the next subclass.
659
25
26 ƒƒ/** Add one line item to items array. Enlarge the array, if necessary. */
27 ƒƒpublicƒvoidƒaddLineItem(LineItemƒitem)
28 ƒƒ{ƒ// Remainder of method omitted.
29 ƒƒ}
30 }
KEY IDEA The remaining task is to write the constructors needed to read a line item. A total of
Each class reads the four are required: one for LineItem and one for each of the subclasses. The
data it needs to LineItem constructor will be called using super in each of the subclass constructors.
initialize itself. After it has read the information it requires, reading will resume in the subclass con-
structor. It reads any remaining information to initialize its own instance variables.
Listing 12-9 shows the relevant code for LineItem, and Listing 12-10 shows the rele-
vant code for ConsultingLineItem.
Listing 12-9: A constructor to read information for one LineItem object from a file
ch12/invoice/
1 publicƒabstractƒclassƒLineItemƒextendsƒObject
2 {ƒprivateƒintƒquantity;
3 ƒƒprivateƒdoubleƒunitCost;
4 ƒƒprivateƒStringƒdescription;
5
6 ƒƒpublicƒLineItem(Scannerƒin)
7 ƒƒ{ƒsuper();ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ// Read only the data stored in this class.
8 ƒƒƒƒthis.quantityƒ=ƒin.nextInt();
9 ƒƒƒƒthis.unitCostƒ=ƒin.nextDouble();
10 ƒƒƒƒthis.descriptionƒ=ƒin.nextLine();
11 ƒƒ}
12
13 ƒƒ// Remainder of class omitted.
14 }
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 660
660
CHAPTER 12 | POLYMORPHISM
The approach shown in the preceding listings works. Its disadvantage is the complex-
ity in the Invoice constructor that has little to do with invoices and much to do with
line items.
A better approach is to move the complexity of determining which subclass to con- KEY IDEA
struct and the actual construction into a static method named read in the LineItem A static method can
class. That method can determine which kind of line item is next in the file, construct return the required
one, and return it. read must be a method and not a constructor because a method can subclass of
LineItem.
return a subclass of LineItem—something a constructor can’t do.
The read method, shown in Listing 12-11, is very similar to lines 13–22 in Listing 12-8.
It must be static so that it can be called without an instance of an object.
661
12.3 POLYMORPHISM
Listing 12-11: A factory method (continued)
11 ƒƒƒƒ{ƒreturnƒnewƒConsultingLineItem(in);
12 ƒƒƒƒ}ƒelse
WITHOUT
13 ƒƒƒƒ{ƒthrowƒnewƒError("Unknown subclass: "ƒ+ƒsubclassƒ+ƒ".");
14 ƒƒƒƒ}
15 ƒƒ}
ARRAYS
16 }
1 publicƒInvoice(Scannerƒin)
2 {ƒthis.customerƒ=ƒnewƒCustomer(in);
3
4 ƒƒƒƒ// Read and construct the line items, putting them in the array.
5 ƒƒƒƒwhileƒ(in.hasNextLine())
6 ƒƒ{ƒthis.addLineItem(LineItem.read(in));
7 ƒƒ}
8 }
doubleƒamtƒ=ƒthis.items[i].calcAmount();
Arrays, however, are not a requirement for using polymorphism. In Listing 12-7 we
called this.getDescription() and the correct subclass of LineItem returned the
answer.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 662
662
CHAPTER 12 | POLYMORPHISM
In fact, the potential for polymorphism exists any time you have a reference to an KEY IDEA
object. What are some other examples? Polymorphism is a
possibility any time
A method may return a reference that is used polymorphically. For example, Invoice you have a reference
might have a method to return the most expensive line item, for printing in a report. to an object.
The report’s method could use it this way:
LineItemƒexpensiveƒ=ƒanInvoice.getMostExpensiveLineItem();
doubleƒcostƒ=ƒexpensive.calcAmount();
The call to calcAmount is polymorphic because this code does not need to know what
kind of LineItem it’s dealing with. In fact, this code could be written without using
the variable expensive:
doubleƒcostƒ=ƒ
ƒƒanInvoice.getMostExpensiveLineItem().calcAmount();
An instance variable can also hold an object reference that is used polymorphically.
12.4.1 toString
Overriding toString was discussed in Section 7.3.3. There isn’t much to add here
except to note that we now know in more detail how Java chooses which toString
method to execute—and that when toString is called, thanks to polymorphism, the
caller doesn’t need to know or care which subclass of Object calculates the answer.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 663
663
LOOKING BACK Section 8.2.4 discussed comparing objects for equivalence. The example was to check
The DateTime class whether two dates “mean” the same thing. We discovered that comparing them with
in the becker library == was not the right thing to do. To check for equivalence, a method is required. At
includes the notion of that point we wrote the following method:
time. We’re ignoring
IN
that here. 1 publicƒclassƒDateTimeƒextendsƒObject
OBJECT
2 {ƒprivateƒintƒyear;
3 ƒƒprivateƒintƒmonth;
4 ƒƒprivateƒintƒday;
5
6 ƒƒ// Other methods omitted.
7
8 ƒƒ/** Return true if this date represents the same date as other. */
9 ƒƒpublicƒbooleanƒisEquivalent(DateTimeƒother)
10 ƒƒ{ƒreturnƒotherƒ!=ƒnullƒ&&ƒthis.yearƒ==ƒother.yearƒ&&ƒ
11 ƒƒƒƒƒƒƒƒƒƒthis.monthƒ==ƒother.monthƒ&&ƒthis.dayƒ==ƒother.day;
12 ƒƒ}
13 }
This method is fine except that the designers of Java provide a method in the Object
class for this purpose: booleanƒequals(Objectƒother). Their intent is that we
override equals with the correct implementation for classes we write.
Objectƒd1ƒ=ƒnewƒDateTime(2008,ƒ1,ƒ1);
DateTimeƒd2ƒ=ƒnewƒDateTime(2008,ƒ1,ƒ1);
KEY IDEA To override equals correctly, we must use the same signature as defined in Object:
Use the right publicƒbooleanƒequals(Objectƒother).
signature to override
equals. Overloading In the isEquivalent method, we know that the object passed via the parameter is a
isn’t good enough. DateTime object. With equals, any object at all may be passed. We first need to ver-
ify that other is an instance of the right type, DateTime. Fortunately, Java provides a
Boolean operator for that purpose. If x is a reference variable and T is the name of a
class or interface, then xƒinstanceofƒT returns true if x is a non-null reference
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 664
664
CHAPTER 12 | POLYMORPHISM
that can call all of the methods specified in T. The type of x might be T, a subclass of T,
or a class that implements the interface T.
We can use instanceof as a key part of our equals method, as follows: KEY IDEA
instanceof is used
ifƒ(!(otherƒinstanceofƒDateTime))
to verify the type of
{ƒ// other isn’t a DateTime object, so it can’t possibly be equal to this DateTime object.
an object.
ƒƒreturnƒfalse;
}
We can tell the compiler to make an exception with a cast. A cast is our assurance to
the compiler that we believe other will, in fact, refer to a DateTime object when the
code executes. The compiler doesn’t completely trust us, however. It will verify at run-
time that other can substitute for an object of the specified type. If it cannot, a
ClassCastException will be thrown.
The syntax for casting an object is like that for casting a primitive type, as in the
following:
DateTimeƒdtƒ=ƒ(DateTime)other;
The meaning, however, is different. When casting a primitive type, the value is actually KEY IDEA
changed. For example, intƒiƒ=ƒ(int)3.99999 assigns i the value 3. When an Casting an object
object reference is cast, the type of the object doesn’t change; it’s the program’s inter- reference changes the
pretation of the object that changes. Instead of interpreting it as an instance of program’s
interpretation of the
Object, the program now interprets it as an instance of what it really is, DateTime. object.
returnƒthis.yearƒ==ƒdt.yearƒ&&ƒthis.monthƒ==ƒdt.monthƒ
ƒƒƒƒƒƒƒƒ&&ƒthis.dayƒ==ƒdt.day;
Lastly, an object is compared to itself surprisingly often. This test can be performed very
efficiently with == and is often included before any of the other tests discussed here.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 665
665
IN
6 ƒƒƒƒreturnƒfalse;ƒƒƒƒƒƒ// other is not an instance of DateTime (or a subclass).
OBJECT
7
8 ƒƒ// Compare the relevant fields for equality.
9 ƒƒDateTimeƒdtƒ=ƒ(DateTime)other;
10 ƒƒreturnƒthis.yearƒ==ƒdt.yearƒ&&ƒthis.monthƒ==ƒdt.month
11 ƒƒƒƒƒƒƒƒ&&ƒthis.dayƒ==ƒdt.day;
12 }
KEY IDEA When should equals be overridden? Classes that represent a value such as Integer,
Many classes should DateTime, or Color should have their own equals method. Classes where an object
not override equals. is only equal to itself should not. Examples include Student (two students may have
the same name, but they are not equal to each other) or BankAccount (my account
shouldn’t be “equal” to your account, even if the balances happen to be the same).
Sometimes an exact copy of an object is required. Suppose, for example, that a back
order in our invoicing system begins by requesting a duplicate of a line item. The Java
system provides a convention for providing this service based on the clone method
that all classes inherit from the Object class.
666
CHAPTER 12 | POLYMORPHISM
Using Clone
Suppose that someone has already implemented clone for the LineItem class. We
could then use it to create a duplicate line item object like this:
LineItemƒduplicateƒ=ƒ(LineItem)aLineItem.clone();
The cast to a LineItem is required because the clone method is declared to return an
Object.
Polymorphism comes into play here because clone may be overridden in the sub-
classes of LineItem, but we don’t need to know or care. The correct method will be
called for the actual run-time type of aLineItem.
Implementing Clone
The clone method in the object class implements the following pseudocode:
The clone method in Object returns an object with the same run-time type as the
original object and the same values for all its instance variables.
It may sound like the existing clone method is all that’s required. Unfortunately, that’s
not the case. It’s dangerous to call clone unless issues have been thought about care-
fully for subclasses (more on these issues will follow). To help ensure that clone can-
not be called without thinking these issues through, Java’s designers have done two
things. First, clone is protected, meaning it can only be called by a subclass.
Therefore, the only way to effectively use clone is to override the method and declare
it public.
Second, the clone method implemented in Object checks to make sure that the
class implements the Cloneable interface. If it doesn’t, clone throws the
CloneNotSupportedException. Either that exception must be caught or your
clone method must declare that it also throws the exception. It’s worth noting that
this is an unusual use of an interface; it affects the behavior of an existing method
rather than guaranteeing the presence of methods. Many programmers believe that this
design is a serious mistake. Nevertheless, clone is used widely enough that it’s worth
understanding how it works.
667
IN
5
OBJECT
6 ƒƒ/** Make a duplicate copy of this object. */
7 ƒƒpublicƒObjectƒclone()
8 ƒƒ{ƒtryƒ
9 ƒƒƒƒ{ƒreturnƒsuper.clone();
10 ƒƒƒƒ}ƒcatchƒ(CloneNotSupportedExceptionƒe)ƒ
11 ƒƒƒƒ{ƒ// CloneNotSupportedException should never be thrown because we have
12 ƒƒƒƒƒƒ// implemented Cloneable. Error is an unchecked exception.
13 ƒƒƒƒƒƒthrowƒnewƒError("Should never happen.");
14 ƒƒƒƒ}
15 ƒƒ}
16 }
It seems like the clone method in the Object class does everything required. Why is it
so thoroughly protected? The clone method in Object simply copies the value in
each instance variable from the original object to the new object. For primitive types
like integers, characters, and immutable classes like String, this works very well. For
reference types, it often does not.
LOOKING BACK Consider cloning an Invoice object. The items instance variable refers to an array.
The value in a The value it stores is a reference to the array, not the array itself. If we call clone to
reference variable is clone the invoice, it will copy this array reference but it won’t make a copy of the array
the address of itself. Both invoices then refer to the same array of line items. If a line item is deleted
an object, not the
object itself.
from the copy of the invoice, it would also be deleted from the original invoice.
See Section 8.2.1. However, the original’s numItems variable would not be updated, probably leading to
nasty results.
Figure 12-15 shows what it is known as a shallow copy. That’s where only the values in
the instance variables are copied from one object to the other. Cloning an invoice
should make a deep copy. A deep copy also clones objects that the object references.
The result of a deep copy is shown in Figure 12-16.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 668
668
CHAPTER 12 | POLYMORPHISM
Shallow copy of an
Invoice Invoice object
items: LineItem[ ]
numItems: 3 length 4
[0] 3, computers, 1750.00
copy [1]
[2] 1, office suite, 750.00
Invoice [3]
items: 3, service contracts, 5.95, 12
numItems: 3
For a deep copy, we need to create a new array and clone each element in the old array.
The clone method in Listing 12-14 shows how.
669
WITH INTERFACES
15 ƒƒƒƒƒƒ}
16 ƒƒƒƒƒƒreturnƒcopy;
17 ƒƒƒƒ}ƒcatchƒ(CloneNotSupportedExceptionƒe)ƒ
18 ƒƒƒƒ{ƒthrowƒnewƒError("Should never happen.");
19 ƒƒƒƒ}
20 ƒƒ}
21 }
KEY IDEA Sometimes a deep copy is not needed, even though references are being used. If the refer-
Immutable objects ence is to an immutable object such as String, a shallow copy is sufficient. Immutable
can’t change and objects can’t change after they are constructed, so there is no danger in having the clone
thus don’t need to and the original object share the same strings—or any other immutable object.
be cloned.
The original sorting method is reproduced in Listing 12-15. The statements shown in bold
must change to sort LineItem objects. The required changes fall into three categories:
➤ The documentation, which is very specific to the original project
➤ The references to a specific array to sort
➤ The condition used to sort the array
Listing 12-15: The sorting algorithm from the Big Brother/Big Sister project. Required changes to
sort line items are shown in bold
1 publicƒclassƒBBBSƒextendsƒObject
2 {ƒ...ƒpersonsƒ...ƒƒƒƒƒƒƒƒ// an array of Person objects
3
4 ƒƒ/** Sort the persons array in increasing order by age. */
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 670
670
CHAPTER 12 | POLYMORPHISM
Listing 12-15: The sorting algorithm from the Big Brother/Big Sister project. Required changes to
sort line items are shown in bold. (continued)
5 ƒƒpublicƒvoidƒsortByAge()
6 ƒƒ{ƒforƒ(intƒfirstUnsorted=0;ƒ
7 ƒƒƒƒƒƒƒƒƒfirstUnsortedƒ<ƒthis.persons.lengthƒ-ƒ1;
8 ƒƒƒƒƒƒƒƒƒfirstUnsorted++)
9 ƒƒƒƒ{ƒ// Find the index of the youngest unsorted person.
10 ƒƒƒƒƒƒintƒextremeIndexƒ=ƒfirstUnsorted;
11 ƒƒƒƒƒƒforƒ(intƒiƒ=ƒfirstUnsortedƒ+ƒ1;ƒ
12 ƒƒƒƒƒƒƒƒƒƒƒƒiƒ<ƒthis.persons.length;ƒi++)
13 ƒƒƒƒƒƒ{ƒifƒ(this.persons[i].getAge()ƒ<ƒ
14 ƒƒƒƒƒƒƒƒƒƒƒƒthis.persons[extremeIndex].getAge())
15 ƒƒƒƒƒƒƒƒ{ƒextremeIndexƒ=ƒi;
16 ƒƒƒƒƒƒƒƒ}
17 ƒƒƒƒƒƒ}
18
19 ƒƒƒƒƒƒ// Swap the youngest unsorted person with the person at firstUnsorted.
20 ƒƒƒƒƒƒPersonƒtempƒ=ƒthis.persons[extremeIndex];
21 ƒƒƒƒƒƒthis.persons[extremeIndex]ƒ=ƒ
22 ƒƒƒƒƒƒƒƒƒƒƒƒthis.persons[firstUnsorted];
23 ƒƒƒƒƒƒthis.persons[firstUnsorted]ƒ=ƒtemp;
24 ƒƒƒƒ}
25 ƒƒ}
26 }
Of the three categories of change identified earlier, the first two are easy. Generalizing
the documentation is trivial, and the problems with the names can be handled with
appropriate parameters. Once we use parameters, all reliance on instance variables is
removed, and the sort method can be made a class (static) method in a utilities
class. The first set of changes is shown in Listing 12-16. The only part left is to figure
out how to replace the pseudocode in line 10, which will influence the type of array
passed as an argument in line 4 and the type of temporary variable in line 16.
1 publicƒclassƒUtilitiesƒextendsƒObject
2 {
3 ƒƒ/** Sort an array of objects. */
4 ƒƒpublicƒstaticƒvoidƒsort(????[]ƒa)
5 ƒƒ{ƒforƒ(intƒfirstUnsortedƒ=ƒ0;ƒfirstUnsortedƒ<ƒa.length-1;
6 ƒƒƒƒƒƒƒƒƒfirstUnsorted++)
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 671
671
WITH INTERFACES
11 ƒƒƒƒƒƒƒƒ{ƒextremeIndexƒ=ƒi;
12 ƒƒƒƒƒƒƒƒ}
13 ƒƒƒƒƒƒ}
14
15 ƒƒƒƒƒƒ// Swap the extreme unsorted element with the element at firstUnsorted.
16 ƒƒƒƒƒƒ????ƒtempƒ=ƒa[extremeIndex];
17 ƒƒƒƒƒƒa[extremeIndex]ƒ=ƒa[firstUnsorted];
18 ƒƒƒƒƒƒa[firstUnsorted]ƒ=ƒtemp;
19 ƒƒƒƒ}
20 ƒƒ}
21 }
Line 10 of Listing 12-16 requires comparing two elements in the array to determine
which is “less” than the other, or which one should occur first in sorted order.
It would be really nice if the Object class had an isLessThan method similar to the
equals method. If it did, we could pass an array of Objects in line 4 and replace the
pseudocode in line 10 with:
ifƒ(a[i].isLessThan(a[extremeIndex]))ƒ
and the sort method would be done. It would depend, of course, on subclasses of
Object overriding isLessThan appropriately. Unfortunately, Object does not pro-
vide such a method.
Another approach is to define isLessThan in the LineItem class and declare sort to
take an array of LineItem objects as its parameter. This works, but only allows sort
to sort LineItems and subclasses of LineItem. It would be preferable to have a solu-
tion that is much more general.
KEY IDEA An excellent solution is to use an interface. This allows a class such as LineItem to
Implementing an have an extra type—the type of the interface. Java already provides such an interface,
interface gives Comparable. It’s included in the package java.lang, which is automatically
the class an imported into every class. The interface is defined as shown in Listing 12-17.
additional type.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 672
672
CHAPTER 12 | POLYMORPHISM
1 publicƒinterfaceƒComparable
2 {ƒ/** Compare this object with the specified object for order. Return a negative number
3 ƒƒ*ƒif this object is less than the specified object, a positive number if this object is greater,
4 ƒƒ*ƒand 0 if this object is equal to the specified object.
5 ƒƒ*ƒ@param o The object to be compared. */
6 ƒƒpublicƒintƒcompareTo(Objectƒo);
7 }
To use this interface, we need to make the three changes to the sort method shown in
Listing 12-16:
➤ In line 4, declare the array parameter variable using Comparable: public
staticƒvoidƒsort(Comparable[]ƒa).
➤ Declare the type of the temporary variable used to swap elements in line 16
using Comparable.
➤ Change line 10 to { ifƒ(a[i].compareTo(a[extremeIndex])ƒ<ƒ0).
Finally, in any class that we want to sort with this method, we need to implement
Comparable. To sort the line items by description, we would change LineItem as
follows:
1 publicƒabstractƒclassƒLineItemƒextendsƒObjectƒ
2 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒimplementsƒComparable
3 {ƒprivateƒStringƒdescription;
4
5 ƒƒ// Other instance variables, constructors, and methods omitted.
6
7 ƒƒpublicƒintƒcompareTo(Objectƒo)
8 ƒƒ{ƒLineItemƒitemƒ=ƒ(LineItem)o;
9 ƒƒƒƒreturnƒthis.description.compareTo(item.description);
10 ƒƒ}ƒ
11 }
673
WITH INTERFACES
KEY IDEA The Java library includes a sort method very similar to the one we have written except
Use Java’s sort that it is much faster, particularly on large arrays. It’s in the java.util.Arrays class
instead of writing and has the following signature:
your own.
publicƒvoidƒsort(Object[]ƒa)
If you want to sort a partially filled array, you can use a companion method with the
following signature:
publicƒvoidƒsort(Object[]ƒa,ƒintƒfromIndex,ƒintƒtoIndex)
These two methods have arrays of objects as parameters rather than arrays of
Comparable like our sort method. How does that work?
The documentation states that all of the elements must implement Comparable and
that compareTo must not throw an exception for any pair of elements. If these condi-
tions are violated, sort will throw a ClassCastException. We can make our ver-
sion of sort behave the same way by making two changes to Listing 12-16. First,
change the type of the parameter in line 4 from Comparable[] to Object[]. Second,
include a cast inside the loop that calls compareTo, as follows:
9 ƒƒƒƒƒforƒ(intƒiƒ=ƒfirstUnsortedƒ+ƒ1;ƒiƒ<ƒa.length;ƒi++)
10 ƒƒƒƒƒ{ƒifƒ(((Comparable)a[i]).compareTo(a[extremeIndex])ƒ<ƒ0)
11 ƒƒƒƒƒƒƒ{ƒextremeIndexƒ=ƒi;
12 ƒƒƒƒƒƒƒ}
13 ƒƒƒƒƒ}
Mixin Interfaces
A mixin is a type that supplements the “primary type” of a class. It provides some
behavior that is mixed in with the normal behavior of the primary type. Comparable
is one such mixin that allows comparing objects and thus sorting them.
674
CHAPTER 12 | POLYMORPHISM
➤ Observer: An interface used when one object wants to “observe” what hap-
pens in another. We’ll use a variation of this when we write graphical user
interfaces in Chapter 13.
➤ Paintable: The class we used in Section 6.1 to ensure that SimpleBots
could be painted on the screen could just as easily have been a mixin interface.
You may want to define your own interface to use as a mixin when an application needs
to process similarly a number of classes that don’t have a natural common superclass.
Implementing the Comparable interface in LineItem is fine if you want to sort the
line items in only one way. But suppose you are writing a report program for the mar-
keting department. They want line items from all the invoices gathered into a single
report, sorted by total amount. We can’t redefine the compareTo method just for
them, so what do we do?
The Strategy pattern uses objects that define a family of interchangeable algorithms.
For sorting, we’ll use a strategy object that defines the comparison algorithm. When
we want a different sort order, we pass a different strategy object (defining a different Strategy
algorithm) to the sort method. This is facilitated with the Comparator interface, as
shown in Figure 12-17. Notice that the sort method in Utilities takes an instance
of Comparator as an argument.
The Comparator interface is defined in the java.util package and is quite similar to KEY IDEA
Comparable. They both define a method that compares two objects and returns an Many different objects
integer whose sign indicates which object is smaller. A key difference is that a can implement
Comparator is passed both objects as parameters rather than comparing one object to Comparator, each
comparing objects in
its own way.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 675
675
publicƒinterfaceƒComparator
{ƒ/** Compare obj1 and obj2 for order. Return a negative number if obj1 is less than
ƒƒ*ƒobj2, a positive number if obj1 is greater than obj2, and 0 if they are equal.
ƒƒ*ƒ@param obj1 One object to be compared.
ƒƒ*ƒ@param obj2 The other object to be compared. */
WITH INTERFACES
ƒƒpublicƒintƒcompare(Objectƒobj1,ƒObjectƒobj2);
}
The following class defines a strategy object that can compare line items when sorting
the marketing department’s report. Notice that it includes the phrase implements
Comparator in line 3. Lines 7–9 are formatted differently than we have seen before to
save space.
1 /** Compare two line items using the value calculated by calcAmount. */
2 publicƒclassƒLineItemAmountComparatorƒextendsƒObjectƒ
3 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒimplementsƒComparator
4 {ƒpublicƒintƒcompare(Objectƒobj1,ƒObjectƒobj2)
5 ƒƒ{ƒdoubleƒamt1ƒ=ƒ((LineItem)obj1).calcAmount();
6 ƒƒƒƒdoubleƒamt2ƒ=ƒ((LineItem)obj2).calcAmount();
7 ƒƒƒƒifƒ(amt1ƒ<ƒamt2)ƒƒƒƒƒƒƒƒƒƒƒƒƒƒ{ƒreturnƒ-1;}ƒ
8 ƒƒƒƒelseƒifƒ(amt1ƒ>ƒamt2)ƒƒƒƒƒƒƒƒƒ{ƒreturnƒ1;ƒ}ƒ
9 ƒƒƒƒelseƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ{ƒreturnƒ0;ƒ}
10 ƒƒ}
11 }
The sort method also needs to take an instance of Comparator as a parameter. This
is shown in Listing 12-18. The method is just like the previous version of sort except
for lines 4 and 11. In line 4, there is a new parameter to pass the strategy object imple-
menting the comparison algorithm. In line 10, it’s used to compare two line items.
With these changes, we can use sort to sort an array of any kind of object in any order
we want, as long as we can provide a comparison strategy object. That’s a lot of
flexibility!
KEY IDEA In practice, however, we would not write our own sort routine. We would only write
Use the Comparator and use it with the sort method in java.util.Arrays.
java.util.Arrays
rather than writing
your own sort method. Listing 12-18: A sort method that uses a comparator method
1 publicƒclassƒUtilitiesƒextendsƒObject
2 {
Strategy 3 ƒƒ/** Sort a partially-filled array of objects. */
4 ƒƒpublicƒstaticƒvoidƒsort(Object[]ƒa,ƒComparatorƒc)
5 ƒƒ{ƒforƒ(intƒfirstUnsortedƒ=ƒ0;ƒfirstUnsortedƒ<ƒa.length-1;
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 676
676
CHAPTER 12 | POLYMORPHISM
6 ƒƒƒƒƒƒƒƒfirstUnsorted++)
7 ƒƒƒƒ{ƒ// Find the index of extreme ("smallest") unsorted element.
8 ƒƒƒƒƒƒintƒextremeIndexƒ=ƒfirstUnsorted;
9 ƒƒƒƒƒƒforƒ(intƒiƒ=ƒfirstUnsortedƒ+ƒ1;ƒiƒ<ƒa.length;ƒi++)
10 ƒƒƒƒƒƒ{ƒifƒ(c.compare(a[i],ƒa[extremeIndex])ƒ<ƒ0)
11 ƒƒƒƒƒƒƒƒ{ƒextremeIndexƒ=ƒi;
12 ƒƒƒƒƒƒƒƒ}
13 ƒƒƒƒƒƒ}
14
15 ƒƒƒƒƒƒ// Swap the extreme unsorted element with the element at firstUnsorted.
16 ƒƒƒƒƒƒObjectƒtempƒ=ƒa[extremeIndex];
17 ƒƒƒƒƒƒa[extremeIndex]ƒ=ƒa[firstUnsorted];
18 ƒƒƒƒƒƒa[firstUnsorted]ƒ=ƒtemp;
19 ƒƒƒƒ}
20 ƒƒ}
21 }
Suppose the marketing department wanted a report with all line items sorted first by
description, and if the descriptions happen to be the same, then in descending order by
total amount of the line item. The description is called the primary key. It is the most
important determinant of the order. If two objects have different primary keys, then
those keys alone are used to determine the order. However, if the primary keys are
equal, then the secondary key is used to determine the order. In this case, total amount
is the secondary key.
677
WITH INTERFACES
21 }
KEY IDEA Notice the if statement for the secondary key in lines 13–17. Normally we return a neg-
Sort in descending ative number when the first argument is less than the second. Here we return positive 1,
order by reversing the and –1 when the first argument is larger. Reversing these two values sorts the objects in
signs of the returned descending order. Larger amounts are interpreted as “smaller” by this comparator.
values.
Small strategy objects such as Comparator are so common that Java’s designers
included a shortcut for defining them quickly and easily. This shortcut is called an
anonymous class. An anonymous class has the following properties:
➤ The class doesn’t have a name (that’s why it’s called anonymous).
➤ It combines declaring a class and instantiating one (and only one) object.
➤ An anonymous class is defined at the same place the object it defines is needed.
This can, if the class is small, improve the understandability of your code.
The following is an example of an anonymous class that sorts line items by description
using a sort method from the Java library. To use this code, you must import
java.util.Arrays and java.util.Comparator.
1 privateƒvoidƒsortLineItems()
2 {ƒ// An anonymous class to compare line items by description.
3 ƒƒComparatorƒcƒ=ƒnewƒComparator()
4 ƒƒ{
5 ƒƒƒƒpublicƒintƒcompare(Objectƒobj1,ƒObjectƒobj2)
6 ƒƒƒƒ{ƒLineItemƒli1ƒ=ƒ(LineItem)obj1;
7 ƒƒƒƒƒƒLineItemƒli2ƒ=ƒ(LineItem)obj2;
8
9 ƒƒƒƒƒƒreturnƒli1.getDescription().compareTo(
10 ƒƒƒƒƒƒƒƒƒƒƒƒƒli2.getDescription());
11 ƒƒƒƒ}
12 ƒƒ};
13
14 ƒƒArrays.sort(this.items,ƒc);
15 }
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 678
678
CHAPTER 12 | POLYMORPHISM
The anonymous class appears in lines 3–12. Line 3 looks like any other object instanti-
ation except that the semicolon is missing from the end of the line and Comparator is
an interface rather than a class. Comparator can be replaced by the interface the
anonymous class is to implement or the class it is to extend.
The body of the anonymous class appears between the “constructor” and the semi-
colon terminating the assignment statement. In the previous code, the body appears in
lines 4–12.
Because the anonymous class has no name, it can’t have a constructor, only methods. It
may have instance variables, but they are uncommon and must always be initialized in
their declaration because there is no constructor.
An anonymous class can be used to create exactly one object. This one is assigned to
the variable c. This variable isn’t required. In fact, experienced programmers will often
replace the variable c in line 14 with the code between the equals in line 3 and the
semicolon in line 12. However, this practice makes the code more difficult to read.
Strategy objects are widely used for more than sorting. They often have a single
method but could have more. Here are a few uses:
➤ The Java library has a number of static sorting methods in the
java.util.Arrays class that take a Comparator strategy object.
➤ Strategy objects are used to arrange components in graphical user interfaces.
We’ll discuss this more in Section 12.6.
➤ Many games use strategy objects to define different approaches for choosing
the next move.
➤ Several classes within the becker.robots package, including Robot, include
methods—such as examineThings—that take a strategy object as an argu-
ment. The method returns references to objects the robot may want to “exam-
ine.” The strategy object determines which objects should be examined.
One particular use for strategy objects is handling objects that change behavior over
time. For example, an employee might move from hourly compensation to a salary and
perhaps to being compensated by contract over her tenure with a company. Using an
inheritance-based approach would require replacing an HourlyEmployee object with a
SalariedEmployee object, for example, as the employee is compensated differently.
Representing this kind of variation with subclasses creates problems as soon as there is
more than one kind of variation. Suppose that mode of work (telecommute vs. office)
is also represented with subclasses. Now we need HourlyTelecommutingEmployee,
SalariedTelecommutingEmployee, ContractTelecommutingEmployee, plus
three more for office employees. This quickly becomes unmanageable. Using strategy
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 679
679
WITH INTERFACES
Java interfaces allow several implementations to be used the same way. This can allow
you to more easily change our minds later. For example, Java provides a set of classes
for storing collections of objects, similar to arrays. Two of these classes are ArrayList
and LinkedList. The two classes have many methods in common: add, remove,
contains, and so on. They also implement the same interface, List.
Why provide two classes that apparently do exactly the same thing? The answer is that
they are implemented differently and have different speed characteristics, as summa-
rized in Table 12-1.
If your application adds and removes objects infrequently but uses get a lot, then
ArrayList looks like a good choice. On the other hand, if get is infrequent but there
are many additions and deletions, LinkedList seems better.
KEY IDEA Sound complicated? Afraid you might make the wrong choice and you’ll want to
Interfaces make it change your mind later? Then use the List interface to declare your variables. This
easier to change can isolate the decision of which class to use to a single point—which constructor to
which class is used.
call when the list is first created. If you change your mind, there is only one place to
change, and the entire program can take advantage of your new approach.
For example, an inventory program might include a method to remove the items just
sold from the items in stock, as sketched in the code fragment shown in Listing 12-19.
Note that List is used throughout, leaving lots of flexibility to use either ArrayList,
LinkedList, or some other implementation of the List interface as the actual class.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 680
680
CHAPTER 12 | POLYMORPHISM
By using List to declare variables in lines 2, 3, and 9, the programmer has left lots of
flexibility to change the actual classes being used. For example, the ArrayList in line 2
could be changed to a LinkedList with no further changes in the rest of the program.
The default layout strategy for a JPanel is an instance of FlowLayout. It adds com-
ponents to the current row until there is no more room. It then starts a new row. The
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 681
681
The left image in Figure 12-18 shows four components organized with a FlowLayout
strategy. The components are displayed left to right, top to bottom, in the same order
they were added. The right image shows how those same components are reorganized
when the frame is narrower.
(figure 12-18)
The FlowLayout
strategy
A FlowLayout object centers rows by default. It can also be set to align them on either
the left or right side of the panel.
(figure 12-19)
The GridLayout
strategy
Setting a JPanel’s layout strategy is done with its setLayout method, as shown in
lines 17–18 of Listing 12-20. This listing is already showing the program structure we
will adopt for our graphical user interfaces. A group of components is combined by
extending JPanel. Laying out the components is a distinct task that is delegated to a
private helper method called layoutView.
682
CHAPTER 12 | POLYMORPHISM
Listing 12-20: A JPanel extended to show a group of buttons, organized with a grid strategy
ch12/layoutManagers/
1 importƒjava.awt.*;
2 importƒjavax.swing.*;
3
4 publicƒclassƒDemoGridLayoutƒextendsƒJPanel
5 {
6 ƒƒprivateƒJButtonƒoneƒ=ƒnewƒJButton("One");
7 ƒƒprivateƒJButtonƒtwoƒ=ƒnewƒJButton("Two");
8 ƒƒ// Instance variables for the last four buttons are omitted.
9
10 ƒƒpublicƒDemoGridLayout()
11 ƒƒ{ƒsuper();
12 ƒƒƒƒthis.layoutView();
13 ƒƒ}
14
15 ƒƒprivateƒvoidƒlayoutView()
16 ƒƒ{ƒ// Set the layout strategy to a grid with 2 rows and 3 columns.
17 ƒƒƒƒGridLayoutƒstrategyƒ=ƒnewƒGridLayout(2,ƒ3);
18 ƒƒƒƒthis.setLayout(strategy);
Strategy
19
20 ƒƒƒƒ// Add the components.
21 ƒƒƒƒthis.add(this.one);
22 ƒƒƒƒthis.add(this.two);
23 ƒƒƒƒ// Code to add the last four buttons is omitted.
24 ƒƒ}
25 }
683
(figure 12-20)
The BorderLayout
strategy
Areas that do not have a component will not take any space. For example, if the button
was left out of the east area in Figure 12-20, the center area would simply expand to fill it.
The layout managers we’ve seen previously arrange the components according to the
order in which they are added to the panel. BorderLayout handles positioning with a
constraint, which is specified when the component is added. The constraint says where
the component should be placed.
Listing 12-20 could be modified to use a BorderLayout strategy by changing line 17 to:
17 ƒƒƒƒBorderLayoutƒstrategyƒ=ƒnewƒBorderLayout();
and changing the lines that add the components to use the required constraints.
21 ƒƒƒƒthis.add(this.one,ƒBorderLayout.EAST);
22 ƒƒƒƒthis.add(this.two,ƒBorderLayout.NORTH);
Like GridLayout, GridBagLayout uses a grid. However, its cells can vary in size,
and a component can take up more than one cell in the grid. To accomplish all this, it
uses a fairly complex constraint, called GridBagConstraints.
684
CHAPTER 12 | POLYMORPHISM
A single layout strategy is usually not enough for a complex graphical user interface.
Consider Figure 12-21, for example. None of the simpler layout strategies we’ve covered
can handle this by themselves. GridBagLayout and SpringLayout could do it, but
using them would involve a tremendous amount of work in setting all the constraints.
(figure 12-21)
A complex layout task
LOOKING AHEAD
An excellent solution is based on the fact that JPanel is also a component. It can be added
Programming
to another JPanel that is organized by its own layout strategy object. The user interface in
Exercise 12.12 asks
Figure 12-21 is organized with four JPanel objects, as shown in Figure 12-22. you to finish
implementing
HangmanView.
(figure 12-22)
Laying out a complex user
interface using nested
panels, each with its own
layout strategy
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 685
685
This interface can be implemented with code similar to that shown in Listing 12-22.
686
CHAPTER 12 | POLYMORPHISM
27 ƒƒƒƒhangman.add(buttons,ƒBorderLayout.EAST);
28 ƒƒ}
29
30 ƒƒ/** Layout and return a subpanel with all the buttons. */
31 ƒƒprivateƒJPanelƒbuttonsPanel()
32 ƒƒ{ƒ// A JPanel holding 26 buttons, one for each letter of the alphabet.
33 ƒƒƒƒJPanelƒlettersƒ=ƒnewƒJPanel();
34 ƒƒƒƒletters.setLayout(newƒGridLayout(13,ƒ2));
35 ƒƒƒƒforƒ(charƒchƒ=ƒ'A';ƒchƒ<=ƒ'Z';ƒch++)
36 ƒƒƒƒ{ƒletters.add(newƒJButton(" "ƒ+ƒch));
37 ƒƒƒƒ}
38
39 ƒƒƒƒ// A JPanel holding the Forfeit and New Game buttons is omitted.
40
41 ƒƒƒƒreturnƒletters;
42 ƒƒ}
43 }
12.7 Patterns
Context: You are writing a program that handles several variations of the same general
idea (for example, several kinds of bank accounts). Each kind of thing has similar
behaviors, but the details may differ.
Solution: Use a polymorphic method call so that the actual object being used deter-
mines which method is called. The most basic form of the pattern is identical to the
Command Invocation pattern from Chapter 1 except for how the «objReference» is
given its value. For example,
«varTypeName»ƒ«objReference»ƒ=ƒ«instanceƒofƒobjTypeName»;
...
«objReference».«serviceName»(«parameterList»);
There are many variations. For example, «objReference» could be a simple instance
variable, an array, a parameter, or a value returned from a method.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 687
687
12.7 PATTERNS
Consequences: «varTypeName» determines the names of the methods that can be
called using «objReference», but «objTypeName» determines the code that is actu-
ally executed.
Name: Strategy
Context: The way an object behaves may change over time or from application to
application. Examples include how an employee is compensated as the nature of his or
her employment changes, how a game chooses its move as the player adjusts prefer-
ences, or how a JPanel lays out the components it contains.
Solution: Identify the methods that may need to be executed differently, depending on
the strategy. Define these methods in a superclass or an interface. Write several subclasses
that implement the behavior required at specific phases in a program’s life.
For example, in a game, a player object needs to make its next move depending on the
preferences of the user. The Player class could be defined as follows, where
MoveStrategy is either the superclass of several different strategy classes or an inter-
face that is implemented by several strategy classes.
publicƒclassƒPlayerƒextendsƒ...
{ƒprivateƒMoveStrategyƒmoveStrategyƒ=ƒ
ƒƒƒƒƒƒƒƒƒƒƒƒnewƒDefaultMoveStrategy();
ƒƒ...
ƒƒpublicƒvoidƒsetMoveStrategy(MoveStrategyƒaStrategy)
ƒƒ{ƒthis.moveStrategyƒ=ƒaStrategy;
ƒƒ}
ƒƒpublicƒMoveƒgetMove(...)
ƒƒ{ƒreturnƒthis.moveStrategy.getMove(...);
ƒƒ}
}
Consequences: The behavior of a class can be easily changed as the program proceeds
simply by supplying a different strategy object.
Related Patterns:
➤ This pattern is a specialization of the Has-a (Composition) pattern.
➤ The Polymorphic Call pattern is used to call the methods in the strategy
object.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 688
688
CHAPTER 12 | POLYMORPHISM
Context: Objects must be compared for equivalency with each other. Comparisons may
be done using such library code as ArrayList or HashSet, and so a standard
approach must be used.
Solution: Override the equals method in the Object class. It is designated to take
any instance of Object (including subclasses) as its argument, so care must be taken to
ensure that the two objects can be compared. The following general template may
be used:
publicƒclassƒ«className»ƒ...
{ƒprivateƒ«primitiveType»ƒ«primitiveField1»
ƒƒ...
ƒƒprivateƒ«primitiveType»ƒ«primitiveFieldN»
ƒƒprivateƒ«referenceType»ƒ«referenceField1»
ƒƒ...
ƒƒprivateƒ«referenceType»ƒ«referenceFieldN»
ƒƒpublicƒbooleanƒequals(Objectƒother)
ƒƒ{ƒifƒ(thisƒ==ƒother)
ƒƒƒƒƒƒreturnƒtrue;
ƒƒƒƒifƒ(!(otherƒinstanceofƒ«className»))
ƒƒƒƒƒƒreturnƒfalse;
ƒƒƒƒ«className»ƒoƒ=ƒ(«className»)other;
ƒƒƒƒreturnƒ
ƒƒƒƒƒƒƒthis.«primitiveField1»ƒ==ƒo.«primitiveField1»ƒ&&
ƒƒƒƒƒƒƒ...
ƒƒƒƒƒƒƒthis.«primitiveFieldN»ƒ==ƒo.«primitiveFieldN»ƒ&&
ƒƒƒƒƒƒƒthis.«referenceField1».equals(o.«referenceField1»)ƒ&&
ƒƒƒƒƒƒƒ...
ƒƒƒƒƒƒƒthis.«referenceFieldN».equals(o.«referenceFieldN»);
ƒƒ}
}
where == is used for primitive fields and equals is used for object references. It may
be that only a subset of the object fields are used to determine equality.
Consequences: The equals method can be used to check any object for equivalence
with any other object.
Related Pattern: This pattern should be used in place of the Equivalence Test pattern.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 689
689
Solution: Write a method that determines which subclass to instantiate and then
returns it. In general,
publicƒstaticƒ«superClassName»ƒ«factoryMethodName»(...)
{ƒ«superClassName»ƒinstanceƒ=ƒnull;
ƒƒifƒ(«testForSubclass1»)
ƒƒ{ƒinstanceƒ=ƒnewƒ«subclassName1»(...);
ƒƒ}ƒelseƒifƒ(«testForSubclass2»)
ƒƒ{ƒinstanceƒ=ƒnewƒ«subclassName2»(...);
ƒƒ}ƒelseƒ...
ƒƒreturnƒinstance;
}
Consequences: A specific subclass is chosen to be instantiated and then returned for use.
The program typically calls a method defined by X but the behavior is determined by
the object’s actual class, Y. This allows:
➤ a collection of objects to be handled uniformly but still have individual differences
➤ the behavior of an object to be easily changed by changing a strategy object
➤ an alternative implementation to be used with a minimum number of changes
to the client code
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 690
690
CHAPTER 12 | POLYMORPHISM
an interface
be
may
the reference
variable‘s type may
be
is a superclass
als
by
oc
may
ed
all
in
ed
rm
be
te
has metho
de
an inte
may be a
e
ar
the abstract
method names class
rface imple
that can be called
ds overrid
superclass
the concrete
class
the behavior of
me
den by
nted by
are executed is d d
ete lle
rm
ine ca
db also
y is
Written Exercises
12.1 The move method in the LeftDancer class (see Listing 12-1) contains the
statement super.move() (lines 16, 18, and 20). What would happen if one of
those statements were this.move()?
12.2 Polymorphism is like a ship’s commanding officer yelling, “Battle stations!” Each
member of the crew knows exactly what he should do in response to that
order—and does it. The commander doesn’t need to give each crew member indi-
vidual instructions. Think of three more real-life analogies for polymorphism.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 691
691
Programming Exercises
12.7 In the dancing robots example, it appears the fundamental difference between
a LeftDancer and a RightDancer is not in how they move but in their
favored direction to turn. Refactor the dancing robots example shown in
Figure 12-3 so that move and pirouette are completely defined in the
abstract class in terms of turn and antiTurn. These last two methods are
abstract and must be overridden in both LeftDancer and RightDancer.
12.8 Investigate the documentation for becker.robots.IPredicate. For each of
the following, write the predicate and a simple robot test program.
a. Write a predicate to identify a Streetlight that is on. Use it to turn off
several streetlights.
b. The City class has a method named setThingCountPredicate. If
showThingCounts is set to true, the number of things on each intersec-
tion that meet the predicate’s criteria will be shown. The default counts the
number of things that can be moved by a robot. Change it to show the total
of all things, except robots.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 692
692
CHAPTER 12 | POLYMORPHISM
693
(figure 12-23)
Possible layouts
12.12 Finish the program in Listing 12-22 so that it also displays Forfeit and New
Game buttons, as shown in Figure 12-21. Include a main method that displays
HangmanView in a JFrame. You won’t be able to play a game with your pro-
gram, but it should look good.
For an additional challenge, read about the Box class and figure out how to use
it to replace a JPanel organized with a BoxLayout strategy.
12.13 In Section 10.1.5, we discussed various operations on those elements of an
array that satisfy a specified property. For example, calculate the average age of
everyone who is a “Little” or print all the people who are “Bigs.”
Download the Big Brother/Big Sister example from Chapter 10 (ch10/bbbs/).
Add an interface, IInclude, which has a single method,
booleanƒinclude(Personƒp). Add the following methods to
BigBroBigSis.java:
a. intƒcountSatisfy(IIncludeƒinclude) counts those persons who sat-
isfy include.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 694
694
CHAPTER 12 | POLYMORPHISM
Programming Projects
12.14 Implement a simple bank application. The bank will have many accounts, each
with an account number and a balance. A command interpreter will allow cus-
tomers to enter one of the following commands:
➤ dƒxxxƒyyy (deposits the amount xxx to account number yyy).
➤ wƒxxxƒyyy (withdraws the amount xxx from account yyy).
➤ tƒxxxƒyyyƒzzz (withdraws the amount xxx from account yyy and
deposits the same amount in account zzz).
➤ bƒyyy (displays the balance of account yyy).
The bank has two kinds of accounts. A PerUseAccount charges a set fee of
$0.50 for each withdrawal. A MinBalanceAccount charges a fee of $1.00 for
each withdrawal if the balance is less than $1,000. If the balance is $1,000 or
more, no fee is charged.
a. Implement the banking system without using polymorphism or inheritance.
Write a brief document outlining in point form what would have to be done
to add a new kind of bank account to the system.
b. Implement the banking system using an inheritance hierarchy for the
account classes. Take advantage of polymorphism but minimize the use of
casting. Write a brief document outlining in point form what would have to
be done to add a new kind of bank account to the system.
12.15 Implement a simple guessing game in which the user chooses a number that the
program will try to guess. After each guess, the user will answer with either H
(the guess was too high), L (the guess was too low), or C (the guess was correct).
Allow the user to easily change the guessing strategy used by the program at the
beginning of each game. Strategies should include at least two of the following:
a. Guess a random number.
b. Guess a number that is one larger than the previous guess. The first guess
should be the smallest legal number for the game.
c. Guess the smallest legal number. As long as the user responds with L, guess
a number that is 10 larger than the previous guess. When the user says it’s
too large, start guessing a number that is one less than the previous guess.
12 Chapter C5743 40143.ps 11/30/06 1:16 PM Page 695
695
696
CHAPTER 12 | POLYMORPHISM
ACME’s Action XYZ’s Action Value to ACME Value to XYZ (table 12-2)
Company actions
Cooperate Cooperate 3 3
Cooperate Defect -2 5
Defect Cooperate 5 -2
Defect Defect 0 0
If both companies want to maximize their profit, what should their strategies
be? Cooperate all of the time? Cooperate most of the time but defect occasion-
ally? Cooperate as much as the other company cooperates?
First, develop three strategies that implement the following interface. They
might be as simple as always cooperating, cooperating with the same probabil-
ity that the other player has cooperated in the past, repeating the other player’s
last decision, or always defecting.
publicƒinterfaceƒICommerceStrategy
{ƒpublicƒstaticƒfinalƒintƒDEFECTƒ=ƒ0;
ƒƒpublicƒstaticƒfinalƒintƒCOOPERATEƒ=ƒ1;
ƒƒ/**ƒDecide whether to cooperate with the other player, given the other's history of
ƒƒ*ƒƒcooperating with this player.
ƒƒ*ƒƒ@param other ƒƒThe decisions made by the other player in previous turns. Each
ƒƒ*ƒƒƒƒƒƒƒƒƒƒ ƒƒelement of the array is one of {DEFECT, COOPERATE}.
ƒƒ*ƒƒ@param numTurns The number of turns made (other is partially filled).
ƒƒ*ƒƒ@return one of {DEFECT, COOPERATE} */
ƒƒpublicƒintƒgetDecision(int[]ƒother,ƒintƒnumTurns);
}
Second, develop a program that plays each strategy against all the other strate-
gies, including a copy of itself. Print the cumulative score for each strategy to
determine the best one. Assume that the players do not know how many turns
there will be. (Does it change your strategy if you know this is your last turn?)
Chapter 13 Graphical User Interfaces
Chapter Objectives
After studying this chapter, you should be able to:
➤ Write a graphical user interface using existing Java components
➤ Implement interfaces using the Model-View-Controller pattern
➤ Structure a graphical user interface using multiple views
➤ Write new components for use in graphical user interfaces
A graphical user interface (GUI) often gives us the first glimpse of a new program. The
information it displays indicates the program’s purpose, whereas a quick review of the
interface’s controls and menus gives us a feel for what the program can do.
This chapter pulls together the graphical user interface thread running through each
chapter and adds new material, enabling us to design and build graphical user inter-
faces for our programs.
697
CHAPTER 13 | GRAPHICAL USER INTERFACES 698
13.1 Overview
Building the graphical user interface (GUI) for a program can be one of the more
rewarding parts of programming. Finally, we begin to see the results of our labor and
are able to manipulate our program directly. The user interface is also a place where we
can use aesthetic skills and sensibilities.
On the other hand, creating GUIs can involve a lot of time and frustration. Developing
them will call upon every skill we’ve learned so far: extending existing classes, writing
methods, using collaborating classes and instance variables, using Java interfaces, and
so on. However, following a concrete set of steps will make the job easier. Watch for
patterns that occur repeatedly. Master those patterns, and you’ll be able to write GUIs
like a professional.
We will proceed by developing a variant of the game of Nim. The requirements are
specified in Figure 13-1.
Recall from Chapter 8 that graphical user interfaces are usually structured using the
Model-View-Controller pattern. Figure 13-2, reproduced here from Section 8.6.2,
shows the core ideas.
699
13.1 OVERVIEW
(figure 13-2)
User Interface
View and controller
interact with the user and View
the model
Model
Controller
The model is the part of the program that represents the problem at hand. In our game
of Nim, it’s the model that will keep track of how many tokens remain on the pile,
whose turn it is to move next, and who (if anyone) has won the game. The model also
enforces rules. For example, it will not allow a player to take more than three tokens.
KEY IDEA The user interface is composed of the view and the controller. The user, represented by
The model maintains the eye and the mouse, uses the view to obtain information from the model. It’s the
relevant information, view, for example, that displays the current size of the pile and whose turn it is. The
the view displays it,
user interacts with the controller to change the model. In the case of Nim, the con-
and the controller
requests changes troller is used to remove some tokens or to start a new game.
to it.
The arrow between the controller and the view indicates that the controller will need
to call methods in the view. The lack of an arrow going the other way indicates that the
view will generally not need to call the controller’s methods. The two arrows between
the user interface and the model indicate that both the view and the controller will
have reason to call the model’s methods—the view to obtain information to display
and the controller to tell the model how the user wants it to change. The dotted arrow
from the model to the user interface indicates that the model will be very restrictive in
how it calls methods in the interface. Essentially, it will call only a single method to tell
the view that it has changed and that the view needs to update the display.
The interaction of the controller, model, and view may seem complicated at first.
However, it follows a standard pattern, which includes the following typical steps, per-
formed in the following order:
➤ The user manipulates the user interface—for example, enters text in a component.
➤ The user interface component notifies its controller by calling a method that
we write.
➤ The controller calls a mutator method in the model, perhaps supplying addi-
tional information such as text that was entered in the component.
➤ Inside the mutator method, the model changes its state, as appropriate. Then it
calls the view’s update method, informing the view that it needs to update the
information it displays.
➤ Inside the update method, the view calls accessor methods in the model to
gather the information it needs to display. It then displays that information.
CHAPTER 13 | GRAPHICAL USER INTERFACES 700
Our first graphical user interface will use a single view and controller. We will learn in KEY IDEA
Section 13.5, however, that using multiple views and controllers can actually make an Interfaces usually
interface easier to build. We will plan for that possibility from the beginning. have more than
one view.
Models, views, and controllers make up a pattern that occurs repeatedly. The steps for
using this pattern are shown in Figure 13-3. You’ll find that many of the steps are
familiar from previous chapters in the book. None of this is truly new material; it just Model-View-Controller
puts together what we have already learned in a specific way, resulting in a graphical
user interface.
(figure 13-3)
Set up the Model and View
Steps for building a
1. Write three nearly empty classes: graphical user interface
a. The model, implementing becker.util.IModel.
b. The view, extending JPanel and implementing becker.util.IView.
The constructor takes an instance of the model as an argument.
c. A class containing a main method to run the program.
2. In main, create instances of the model and the view. Display the view in a
frame.
Build and Test the Model Build the View and Controllers
13.2 SETTING
(figure 13-4) IView
IModel
Partial class diagram
for Nim
UP THE
NimModel NimView
-ArrayList<IView> views -NimModel model
Nim
The model’s primary purpose is to model the problem, in our case the game of Nim. It
must also inform the views each time the model changes (and therefore the view needs to
change the information it displays). It is this update function that we are focusing on now.
LOOKING BACK It’s possible that a model may have several views, and we will provide for that possi-
ArrayLists were bility right away by keeping a list of views that we need to inform of changes. These
discussed in requirements are embodied in the IModel interface. It specifies that a model needs to
Section 8.5.1,
be able to add a view, remove a view, and update all views. The model will only need
interfaces in
Section 7.6. to call one method in the views, updateView. It expects each view to implement the
IView interface.
KEY IDEA
The IModel interface A class with this infrastructure is shown in Listing 13-1. Every model will start out just
specifies methods like this except that the name of the class, the constructor, and the class documentation
needed in the model will change to reflect the program’s purpose.
to manage views.
Listing 13-1: The model’s class with infrastructure to inform views of changes
ch13/nim 1 importƒbecker.util.IModel;
Infrastructure/ 2 importƒbecker.util.IView;
3 importƒjava.util.ArrayList;
4
5 /** A class implementing a version of Nim. There is a (virtual) pile of tokens. Two
6 *ƒ players take turns removing 1, 2, or 3 tokens. The player who takes the last token
7 *ƒ wins the game.
8 *
9 * @author Byron Weber Becker */
10 publicƒclassƒNimModelƒextendsƒObjectƒimplementsƒIModel
CHAPTER 13 | GRAPHICAL USER INTERFACES 702
Listing 13-1: The model’s class with infrastructure to inform views of changes (continued)
11 {ƒprivateƒArrayList<IView>ƒviewsƒ=ƒnewƒArrayList<IView>();
12
13 ƒƒ/** Construct a new instance of the game of Nim. */
14 ƒƒpublicƒNimModel()
15 ƒƒ{ƒsuper();
16 ƒƒ}
17
18 ƒƒ/** Add a view to display information about this model.
19 ƒƒ*ƒ@param view The view to add. */
20 ƒƒpublicƒvoidƒaddView(IViewƒview)
21 ƒƒ{ƒthis.views.add(view);
22 ƒƒ}
23
24 ƒƒ/** Remove a view that has been displaying information about this model.
25 ƒƒ*ƒ@param view The view to remove. */
26 ƒƒpublicƒvoidƒremoveView(IViewƒview)
27 ƒƒ{ƒthis.views.remove(view);
28 ƒƒ}
29
30 ƒƒ/** Inform all the views currently displaying information about this model that the
31 ƒƒ*ƒmodel has changed and their display may need changing too. */
32 ƒƒpublicƒvoidƒupdateAllViews()
33 ƒƒ{ƒforƒ(IViewƒviewƒ:ƒthis.views)
34 ƒƒƒƒ{ƒview.updateView();
35 ƒƒƒƒ}
36 ƒƒ}
37 }
Using AbstractModel
These three methods are always required to implement a model. Instead of writing
them each time we create a model class, we can put them in their own class. Our model
can simply extend that class.
703
13.2 SETTING
Such a class, AbstractModel, is in the becker.util package. Its code is almost
exactly like the code in Listing 13-1 except for the name of the class. NimModel is then
implemented as follows:
UP THE
importƒbecker.util.AbstractModel;
The Java library has a class named Observable that is very similar to AbstractModel. It
is designed to work with an interface named Observer that is very similar to IView. Why
don’t we use them instead? There are two reasons.
Second, and more importantly, the Java library doesn’t have an interface corresponding
to IModel. Therefore, the model must always extend Observable. Sometimes this isn’t
a problem (as with NimModel), but other times the model must extend another class. In
those situations, the missing interface is required, and these classes can’t be used.
At the time of this writing, Java library contains 6,558 classes. A number of those
classes define their own versions of Observer and Observable, as we have done. It’s
interesting to note that none of the classes use Observer and Observable.
KEY IDEA Each view will be a subclass of JPanel1 that contains the user interface components
A component is required to interact with the model. For now, however, we will provide only the infra-
nothing more than an structure for updating the view. That consists of implementing the IView interface,
object designed for which specifies the updateView method called by the model in updateAllViews.
user interfaces.
Buttons, scroll bars,
This is all shown in Listing 13-2.
and text fields are all
examples of
components.
1This is true most of the time. It’s convenient for menus to extend JMenuBar and toolbars to extend
JToolBar.
CHAPTER 13 | GRAPHICAL USER INTERFACES 704
The view is passed an instance of the model when it is constructed. The model is saved
in an instance variable, and the view adds itself to the model’s list of views. Finally, the
view must update the information it displays by calling updateView in line 16.
Listing 13-2: The view’s class set up to receive notification of changes in the model
ch13/nim
1 importƒjavax.swing.JPanel; Infrastructure/
2 importƒbecker.util.IView;
3
4 /** Provide a view of the game of Nim to a user.
5 *
6 * @author Byron Weber Becker */
7 publicƒclassƒNimViewƒextendsƒJPanelƒimplementsƒIView
8 {ƒprivateƒNimModelƒmodel;
9
10 ƒƒ/** Construct the view.
11 ƒƒ* @param aModel The model we will be displaying. */
12 ƒƒpublicƒNimView(NimModelƒaModel)
13 ƒƒ{ƒsuper();
14 ƒƒƒƒthis.modelƒ=ƒaModel;
15 ƒƒƒƒthis.model.addView(this);
16 ƒƒƒƒthis.updateView();
17 ƒƒ}
18
19 ƒƒ/** Called by the model when it changes. Update the information this view displays. */
20 ƒƒpublicƒvoidƒupdateView()
21 ƒƒ{
22 ƒƒ}
23 }
The last step in setting up the infrastructure is to write the main method. It constructs
an instance of the model and an instance of the view. It then displays the view in an
appropriately sized frame. This is shown in Listing 13-3.
705
THE
5 *
6
MODEL
* @author Byron Weber Becker */
7 publicƒclassƒNim
8 {
9 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)
10 ƒƒ{ƒNimModelƒmodelƒ=ƒnewƒNimModel();
11 ƒƒƒƒNimViewƒviewƒ=ƒnewƒNimView(model);
12
13 ƒƒƒƒJFrameƒfƒ=ƒnewƒJFrame("Nim");
14 ƒƒƒƒf.setSize(250,ƒ200);
15 ƒƒƒƒf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
16 ƒƒƒƒf.setContentPane(view);
17 ƒƒƒƒf.setVisible(true);
18 ƒƒ}
19 }
The requirements in Figure 13-1 specify that the first player and the initial size of the
pile are chosen randomly. The default constructor will do that, but since randomness
makes the class hard to test, we’ll also add a private constructor, allowing our test har-
ness to easily specify the pile size and first player.
LOOKING BACK Representing the two players is a perfect job for an enumeration type. We will use three
Enumerations were values: one for the red player, one for the black player, and one for nobody. The last
discussed in one might be used, for example, as the answer to the query of who has won the game
Section 7.3.4. (if the game isn’t over yet, nobody has won).
CHAPTER 13 | GRAPHICAL USER INTERFACES 706
The Player enumeration is shown in Listing 13-4, and the NimModel class is shown KEY IDEA
in Listing 13-5. In NimModel, the only method (other than the constructors) that Call
changes the model’s state is removeTokens. After it has made its changes, it calls updateAllViews
before returning from
updateAllViews at line 96 to inform the views that they should update the informa-
a method that
tion they display. changes the model.
1 /** The players in the game of Nim, plus NOBODY to indicate situations where ch13/nimOneView/
2 * ƒneither player is applicable (for example, when no one has won the game yet).
3 *
4 * ƒ@author Byron Weber Becker */
5 publicƒenumƒPlayerƒ
6 {ƒRED,ƒBLACK,ƒNOBODY
7 }
27
28 ƒƒ/** We need a way to create a nonrandom game for testing purposes. */
29 ƒƒprivateƒNimModel(intƒpileSize,ƒPlayerƒnext)
30 ƒƒ{ƒsuper();
THE
31 ƒƒƒƒthis.pileSizeƒ=ƒpileSize;
32
MODEL
ƒƒƒƒthis.whoseTurnƒ=ƒnext;
33 ƒƒ}
34
35 ƒƒ/** Generate a random number between two bounds. */
36 ƒƒprivateƒstaticƒintƒrandom(intƒlower,ƒintƒupper)
37 ƒƒ{ƒreturnƒ(int)(Math.random()*(upper-lower+1))ƒ+ƒlower;
38 ƒƒ}
39
40 ƒƒ/** Choose a player at random.
41 ƒƒ*ƒ@return Player.RED or Player.BLACK with 50% probability for each */
42 ƒƒprivateƒstaticƒPlayerƒchooseRandomPlayer()
43 ƒƒ{ƒifƒ(Math.random()ƒ<ƒ0.5)
44 ƒƒƒƒ{ƒreturnƒPlayer.RED;
45 ƒƒƒƒ}ƒelse
46 ƒƒƒƒ{ƒreturnƒPlayer.BLACK;
47 ƒƒƒƒ}
48 ƒƒ}
49
50 ƒƒ/** Get the current size of the pile.
51 ƒƒ*ƒ@return the current size of the pile */
52 ƒƒpublicƒintƒgetPileSize()
53 ƒƒ{ƒreturnƒthis.pileSize;
54 ƒƒ}
55
56 ƒƒ/** Get the next player to move.
57 ƒƒ*ƒ@return Either Player.RED or Player.BLACK if the game has not yet been won,
58 ƒƒ* ƒor Player.NOBODY if the game has been won. */
59 ƒƒpublicƒPlayerƒgetWhoseTurn()
60 ƒƒ{ƒreturnƒthis.whoseTurn;
61 ƒƒ}
62 ƒ
63 ƒƒ/** Get the winner of the game.
64 ƒƒ*ƒ@return Either Player.RED or Player.BLACK if the game has already been won;
65 ƒƒ*ƒPlayer.NOBODY if the game is still in progress. */
66 ƒƒpublicƒPlayerƒgetWinner()
67 ƒƒ{ƒreturnƒthis.winner;
68 ƒƒ}
CHAPTER 13 | GRAPHICAL USER INTERFACES 708
69
70 ƒƒ/** Is the game over?
71 ƒƒ*ƒ@return true if the game is over; false otherwise. */
72 ƒƒprivateƒbooleanƒgameOver()
73 ƒƒ{ƒreturnƒthis.pileSizeƒ==ƒ0;
74 ƒƒ}
75
76 ƒƒ/** Remove one, two, or three tokens from the pile. Ignore any attempts to take
77 ƒƒ*ƒtoo many or too few tokens. Otherwise, remove howMany tokens from the pile
78 ƒƒ*ƒand update whose turn is next.
79 ƒƒ*ƒ@param howMany How many tokens to remove.
80 ƒƒ*ƒ@throws IllegalStateException if the game has already been won */
81 ƒƒpublicƒvoidƒremoveTokens(intƒhowMany)
82 ƒƒ{ƒifƒ(this.gameOver())
83 ƒƒƒƒ{ƒthrowƒnewƒIllegalStateException(
84 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒ"The game has already been won.");
85 ƒƒƒƒ}
86
87 ƒƒƒƒifƒ(this.isLegalMove(howMany))
88 ƒƒƒƒ{ƒthis.pileSizeƒ=ƒthis.pileSizeƒ-ƒhowMany;
89 ƒƒƒƒƒƒifƒ(this.gameOver())
90 ƒƒƒƒƒƒ{ƒthis.winnerƒ=ƒthis.whoseTurn;
91 ƒƒƒƒƒƒƒƒthis.whoseTurnƒ=ƒPlayer.NOBODY;
92 ƒƒƒƒƒƒ}ƒelse
93 ƒƒƒƒƒƒ{ƒthis.whoseTurnƒ=ƒ
94 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒNimModel.otherPlayer(this.whoseTurn);
95 ƒƒƒƒƒƒ}
96 ƒƒƒƒƒƒthis.updateAllViews();ƒ
97 ƒƒƒƒ}ƒƒƒƒ
98 ƒƒ}
99
100 ƒƒ// Is howMany a legal number of tokens to take?
101 ƒƒprivateƒbooleanƒisLegalMove(intƒhowMany)
102 ƒƒ{ƒreturnƒhowManyƒ>=ƒ1ƒ&&ƒhowManyƒ<=ƒMAX_REMOVEƒ&&ƒ
103 ƒƒƒƒƒƒƒƒƒƒƒhowManyƒ<=ƒthis.pileSize;
104 ƒƒ}
105
106 ƒƒ// Return the other player.
107 ƒƒprivateƒstaticƒPlayerƒotherPlayer(Playerƒwho)
108 ƒƒ{ƒifƒ(whoƒ==ƒPlayer.RED)
109 ƒƒƒƒ{ƒreturnƒPlayer.BLACK;
110 ƒƒƒƒ}ƒelseƒifƒ(whoƒ==ƒPlayer.BLACK)
111 ƒƒƒƒ{ƒreturnƒPlayer.RED;
709
13.4 BUILDING
Listing 13-5: The completed NimModel class (continued)
112 ƒƒƒƒ}ƒelse
THE
113 ƒƒƒƒ{ƒthrowƒnewƒIllegalArgumentException();
KEY IDEA Java comes with many user interface components including buttons, text fields, menus,
The program shown sliders, and labels. Some of these are shown in Figure 13-5. Designing an interface
in Figure 13-5 includes deciding which of these components are most appropriate both to display the
contains lots of code model and to accept input from the user, and how to best arrange them on the screen.
to help get you
started using
For now, while we’re learning the basics, we will restrict ourselves to labels for dis-
components. playing information and text fields to accept input from the user. In Section 13.7, we
will explore other components.
CHAPTER 13 | GRAPHICAL USER INTERFACES 710
(figure 13-5)
Application demonstrating
many of the components
available for
constructing views
ch13/component
Demo/
Our first view will appear as shown in Figure 13-6. It shows the end of the game after
Red has won. The text areas (one has “2” in it, the other has “3”) are enabled when it’s
the appropriate player’s turn and disabled when it isn’t. When the game is over, both
are disabled, as shown here.
(figure 13-6)
13.4 BUILDING
13.4.2 Laying Out the Components
The components for any view can be divided into those that require ongoing access and
THE
those that don’t. In this view, the following five components require ongoing access
LOOKING BACK The components that do not require ongoing access include several JPanel objects
Layout managers used to organize the components and the borders around them. Instance variables stor-
were discussed in ing references to these components are not required.
Section 12.6.
These components are laid out using four nested JPanels, as shown in Figure 13-7.
The task of laying out the components occurs when the view is constructed and is usu-
ally complex enough to merit a helper method called from the constructor. We’ll call
the helper method layoutView, as shown in Listing 13-6. The method carries out the
following tasks:
➤ The first JPanel, named red, is defined in lines 12–15. It contains a
JTextField to accept information from the red player and a label to
announce if red is the winner. The JPanel itself is wrapped with a border to
label it in line 15.
➤ The second JPanel, black, is just like red except that it contains compo-
nents for the black player.
➤ The third JPanel, pSize, contains the label used to display the size of the
pile. It, too, has a border to label it.
➤ The fourth JPanel, center, is not directly visible in the user interface. It
exists solely to group the red and blackƒJPanels into a single component
that can be placed as a whole.
Finally, recall that NimView is itself a JPanel that can have its own layout manager. It
is set in line 36 to be a BorderLayout. Only two of the layout’s five areas are used, the
center and the south side. The center section grows and shrinks as its container is
resized. That’s where we put the center panel containing red and black. The south
area contains pSize.
Adding the layoutView method to NimView, as shown in Listing 13-6, and running
the program results in something that looks much like Figure 13-6. The pile size won’t
be displayed and both players will be declared winners. To display that information
correctly we need to update the view with information from the model.
Listing 13-6: A helper method to lay out the view for Nim
ch13/nimOneView/
1 publicƒclassƒNimViewƒextendsƒJPanelƒimplementsƒIView
2 {ƒ// Instance variables omitted.
3
4 ƒƒpublicƒNimView(NimModelƒaModel)
5 ƒƒ{ƒ// Details omitted.
6 ƒƒƒƒthis.layoutView();
7 ƒƒ}
8
9 ƒƒ// Layout the view.
10 ƒƒprivateƒvoidƒlayoutView()
11 ƒƒ{ƒ// A panel for the red player.
12 ƒƒƒƒJPanelƒredƒ=ƒnewƒJPanel();
13 ƒƒƒƒred.add(this.redRemoves);
14 ƒƒƒƒred.add(this.redWins);
713
13.4 BUILDING
Listing 13-6: A helper method to lay out the view for Nim (continued)
15 ƒƒƒƒred.setBorder(BorderFactory.createTitledBorder("Red"));
THE
16
➤ Make redWins visible when the red player wins the game and invisible when
it hasn’t, with similar behavior for blackWins.
Recall that the constructor received a reference to the model as a parameter. This refer-
ence was stored in an instance variable named, appropriately, model. We will use it to
retrieve the necessary information from the model to carry out these tasks.
The component to display the size of the pile is a JLabel. It has a method, setText,
which takes a string and causes the label to display it. Thus, we can update the pile size
display with the following statement:
this.pileSize.setText(""ƒ+ƒthis.model.getPileSize());
The result from getPileSize is an int. “Adding” it to the empty string forces Java
to convert it to a string, which is what setText requires.
If you run the program now, the user interface should show the pile size.
redRemoves is the name of the text field used by the red player to say how many
tokens to remove. To enable or disable it, we’ll use the setEnabled method, passing
true to enable the component and false to disable it. We want the text field enabled
when the following Boolean expression is true:
this.model.getWhoseTurn()ƒ==ƒPlayer.RED
If this expression is false (it’s not red’s turn), the component should be disabled. Thus,
this.redRemoves.setEnabled(
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒthis.model.getWhoseTurn()ƒ==ƒPlayer.RED);
enables redRemoves when it’s the red player’s turn and disables it otherwise. Recall
that when the game is over, getWhoseTurn returns Player.NOBODY, resulting in both
text fields being disabled.
When the game is over, we want either redWins or blackWins to become visible. If
the game isn’t over, we want both to be invisible. Every component has a method
named setVisible that makes the component visible when passed the value true
715
13.4 BUILDING
and invisible when passed the value false. We can again use a simple Boolean expres-
sion to pass the correct value:
this.redWins.setVisible(
THE
ƒƒƒƒƒƒƒƒƒƒƒƒƒthis.model.getWinner()ƒ==ƒPlayer.RED);
Listing 13-7: Updating the view with current information from the model
ch13/nimOneView/
1 publicƒclassƒNimViewƒextendsƒJPanelƒimplementsƒIView
2 {ƒprivateƒNimModelƒmodel;
3 ƒƒprivateƒJTextFieldƒredRemovesƒ=ƒnewƒJTextField(5);
4 ƒƒ// Other instance variables, constructor, and methods omitted.
5
6 ƒƒ/** Called by the model when it changes. Update the information this view displays. */
7 ƒƒpublicƒvoidƒupdateView()
8 ƒƒ{ƒ// Update the size of the pile.
9 ƒƒƒƒthis.pileSize.setText(""ƒ+ƒthis.model.getPileSize());
10
11 ƒƒƒƒ// Enable and disable the text fields for each player.
12 ƒƒƒƒthis.redRemoves.setEnabled(
13 ƒƒƒƒƒƒƒƒƒƒƒƒthis.model.getWhoseTurn()ƒ==ƒPlayer.RED);
14 ƒƒƒƒthis.blackRemoves.setEnabled(
15 ƒƒƒƒƒƒƒƒƒƒƒƒthis.model.getWhoseTurn()ƒ==ƒPlayer.BLACK);
16
17 ƒƒƒƒ// Proclaim the winner, if there is one.
18 ƒƒƒƒthis.redWins.setVisible(
19 ƒƒƒƒƒƒƒƒƒƒƒƒthis.model.getWinner()ƒ==ƒPlayer.RED);
20 ƒƒƒƒthis.blackWins.setVisible(
21 ƒƒƒƒƒƒƒƒƒƒƒƒthis.model.getWinner()ƒ==ƒPlayer.BLACK);
22 ƒƒ}
23 }
CHAPTER 13 | GRAPHICAL USER INTERFACES 716
Understanding Events
What is important is that when one of these events occurs, two things happen. First,
the component constructs an event object describing the event and containing such
information as when the event occurred, if any keys were pressed at the time, and
which component created it.
Second, the component calls a specific method, passing the event object as an argu-
ment. This method is one that we write as part of our controller. It’s in this method that
we have an opportunity to take actions specific to our program, such as calling the
removeTokens method in the model.
1 publicƒclassƒJTextFieldƒextendsƒ...
2 {ƒprivateƒActionListenerƒactionListener;
3 ƒƒprivateƒFocusListenerƒfocusListener;
4
5 ƒƒpublicƒvoidƒaddActionListener(ActionListenerƒaListener)
6 ƒƒ{ƒthis.actionListenerƒ=ƒaListener;
7 ƒƒ}
8
9 ƒƒpublicƒvoidƒaddFocusListener(FocusListenerƒfListener)
10 ƒƒ{ƒthis.focusListenerƒ=ƒfListener;
11 ƒƒ}
12
13 ƒƒprivateƒvoidƒhandleEvent()
14 ƒƒ{ƒifƒ(user pressed the “Enter” key)
717
13.4 BUILDING
Listing 13-8: A simplified version of JTextField (continued)
THE
16 ƒƒƒƒƒƒthis.actionListener.actionPerformed(event);
KEY IDEA Obviously, the method called has a name. That means that our controller must have
In Java, controllers a method with the same name. Ensuring that it does is a perfect job for a Java inter-
implement methods face. The names ActionListener and FocusListener at lines 2, 3, 5, and 9 in
defined in interfaces
Listing 13-8 are, in fact, the names of Java interfaces. Our controllers will always
with names ending in
Listener. implement at least one interface whose name ends with Listener.
KEY IDEA There are, unfortunately, two competing terminologies. “Controller” is a well-
In Java, we use established name for the part of a user interface that interprets events and calls the
listener interfaces to appropriate commands in the model. Java uses the term listener for a class that is called
implement when an event occurs. Most of the time the two terms mean the same thing.
controllers.
Implementing a Controller
When the user presses the Enter key inside a JTextField component, the component calls
a method named actionPerformed. This method is defined in the ActionListener
interface (and is, in fact, the only method defined there). It takes a single argument of type
ActionEvent. Therefore, the skeleton for our controller class will be:
importƒjava.awt.event.ActionListener;
importƒjava.awt.event.ActionEvent;
publicƒclassƒRemovesControllerƒextendsƒObjectƒ
ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒimplementsƒActionListener
{
ƒƒpublicƒvoidƒactionPerformed(ActionEventƒe)
ƒƒ{
ƒƒ}
}
CHAPTER 13 | GRAPHICAL USER INTERFACES 718
Inside actionPerformed, we need to obtain the value the user typed into the text field LOOKING AHEAD
and then call the model with that value. One approach is to have instance variables stor- Implementing
ing references to the text field and the model for the game. Then actionPerformed can controllers can use a
number of shortcuts.
be written as
Some of them will
publicƒvoidƒactionPerformed(ActionEventƒe) be explored in
{ƒStringƒenteredTextƒ=ƒthis.textfield.getText(); Section 13.6,
ƒƒintƒremoveƒ=ƒconvert enteredText to an integer; Controller Variations.
ƒƒthis.model.removeTokens(remove);
}
The conversion from a string to an integer can be done with parseInt, a static
method in the Integer class. It will throw a NumberFormatException if the user
enters text that is not a valid integer. If this exception is thrown, we’ll recover in the
catch clause by selecting the entered text and ignoring what was entered.
The full method is shown in lines 21–29 of Listing 13-9. The rest of the listing, lines 11–19,
is simply declaring the instance variables needed and initializing them in a constructor.
13.4 BUILDING
Listing 13-9: A controller for a text field (continued)
26 ƒƒƒƒ}ƒcatchƒ(NumberFormatExceptionƒex)ƒ
THE
27 ƒƒƒƒ{ƒthis.textfield.selectAll();
Registering Controllers
The very last step to make this user interface interactive is to construct the controllers
and register them with the text fields. Recall that the simplified version of JTextField
shown in Listing 13-8 contained methods such as addActionListener and
addFocusListener. They each took an instance of the similarly named interface and
saved it in an instance variable. Registering our controller simply means calling the
appropriate addXxxListener method for the relevant component, passing an
instance of the controller as an argument.
KEY IDEA We’ve only written one controller class, but we’ll use one instance of it for the
A controller must be redRemoves text field and a second instance for the blackRemoves text field. A user
registered with a interface often has several controllers, so it makes sense to have a helper method,
component.
registerControllers, just for constructing and registering controllers. It is called
from the view’s constructor.
The code in Listing 13-10 registers the red controller in two steps but combines the
steps for the black controller.
Listing 13-10: A method registering the controllers with the appropriate components
1 publicƒclassƒNimViewƒextendsƒJPanelƒimplementsƒIView
2 {ƒ// Instance variables omitted.
3
4 ƒƒpublicƒNimView()
5 ƒƒ{ƒ// Some details omitted.
6 ƒƒƒƒthis.registerControllers();
7 ƒƒ}
8
9 ƒƒ/** Register controllers for the components the user can manipulate. */
10 ƒƒprivateƒvoidƒregisterControllers()
11 ƒƒ{ƒRemoveControllerƒredControllerƒ=ƒ
12 ƒƒƒƒƒƒƒƒnewƒRemoveController(this.model,ƒthis.redRemoves);
13 ƒƒƒƒthis.redRemoves.addActionListener(redController);
CHAPTER 13 | GRAPHICAL USER INTERFACES 720
Listing 13-10: A method registering the controllers with the appropriate components (continued)
14
15 ƒƒƒƒthis.blackRemoves.addActionListener(
16 ƒƒƒƒƒƒƒnewƒRemoveController(this.model,ƒthis.blackRemoves));
17 ƒƒ}
18 }
If you run the program with these additions, you should be able to play a complete,
legal game, as shown in Figure 13-8.
(figure 13-8)
User interface as it
appears at each stage of a
complete game
a) The game begins with a pile of 10. Red b) Red takes two tokens; now it’s black’s
has the first turn. turn. The player must click in its text field
before entering a value.
c) Black takes three tokens. It’s red’s turn. d) Red takes one token; now it’s black’s
The “2” from red’s previous turn still shows. turn. The 3 from the previous turn still
Red does not need to click in its text field shows in the text field.
before entering a value but must delete the
old value before entering a new one.
e) Black takes two tokens, setting up red for f) Red takes two tokens and is proclaimed
a win. the winner.
721
13.4 BUILDING
13.4.5 Refining the View
The program runs, as shown in Figure 13-8. However, there are three areas in which
THE
improvements could be made.
Focus
In any given user interface, one component at most will receive input from the user’s
keyboard. This component is said to have the keyboard focus. Usually a component
will give some visible sign when it has the focus. A component that accepts text will
show a flashing bar called the insertion point. A button that has the focus will often
have a subtle box around its label.
Focus normally shifts from one component to the next in the order that they were
added to their container. In the case of Nim, however, the component that should have
the focus depends on whose turn it is. So, in the updateView method, we can update
which component has the focus with the following code. This code also replaces the
previously entered value with an empty string.
ifƒ(this.model.getWhoseTurn()ƒ==ƒPlayer.RED)
{ƒthis.redRemoves.requestFocusInWindow();
ƒƒthis.redRemoves.setText("");
}ƒelseƒifƒ(this.model.getWhoseTurn()ƒ==ƒPlayer.BLACK)
{ƒthis.blackRemoves.requestFocusInWindow();
ƒƒthis.blackRemoves.setText("");
}
Fonts
A larger font for the various components can be specified with the setFont method.
Its argument is a Font object describing the desired font. The following code could be
included in the layoutView method to change the font for the five components.
CHAPTER 13 | GRAPHICAL USER INTERFACES 722
The first argument to the Font constructor specifies to use a font with serifs. Such fonts
have short lines at the ends of the main strokes of each letter. Common fonts that have
serifs include Times New Roman, Bookman, and Palatino. The string “SansSerif” can be
used to specify a font without serifs. Helvetica is a common sans serif font. The string
“monospaced” indicates a font using a fixed width for each letter. An example is Courier.
You can also specify an actual font name like “Helvetica” as the first argument.
However, you can’t be sure that the font is actually installed on the computer unless
you check. The program in Listing 13-11 will list all the names of all the fonts that are
installed. Try it for yourself to see which fonts are installed on your computer.
The second argument to the Font constructor is the style. There are three basic styles,
defined as constants in the Font class: PLAIN, ITALIC, and BOLD. ITALIC makes the let-
ters slant and BOLD makes the strokes thicker. A bold, italic font can also be specified by
adding the BOLD and ITALIC constants together and passing the result to the constructor.
723
13.4 BUILDING
The third argument to the Font constructor is the font’s size. The size is measured in
points, where one point is 1/72 of an inch. Ten to 12 points is a comfortable size for
reading; use 16 points or larger for labels and headlines.
THE
This finishes our first view. The complete code is shown in Listing 13-12. Most com-
Listing 13-12: The completed code for the NimView class (continued)
35 ƒƒƒƒthis.model.addView(this);
36 ƒƒƒƒthis.updateView();
37 ƒƒ}
38
39 ƒƒ/** Called by the model when it changes. Update the information this view displays. */
40 ƒƒpublicƒvoidƒupdateView()
41 ƒƒ{ƒthis.pileSize.setText(""ƒ+ƒthis.model.getPileSize());
42
43 ƒƒƒƒthis.redRemoves.setEnabled(
44 ƒƒƒƒƒƒƒƒƒƒthis.model.getWhoseTurn()ƒ==ƒPlayer.RED);
45 ƒƒƒƒthis.blackRemoves.setEnabled(
46 ƒƒƒƒƒƒƒƒƒƒthis.model.getWhoseTurn()ƒ==ƒPlayer.BLACK);
47 ƒƒƒƒthis.redWins.setVisible(
48 ƒƒƒƒƒƒƒƒƒƒthis.model.getWinner()ƒ==ƒPlayer.RED);
49 ƒƒƒƒthis.blackWins.setVisible(
50 ƒƒƒƒƒƒƒƒƒƒthis.model.getWinner()ƒ==ƒPlayer.BLACK);
51
52 ƒƒƒƒifƒ(this.model.getWhoseTurn()ƒ==ƒPlayer.RED)
53 ƒƒƒƒ{ƒthis.redRemoves.requestFocusInWindow();
54 ƒƒƒƒƒƒthis.redRemoves.setText("");
55 ƒƒƒƒ}ƒelseƒifƒ(this.model.getWhoseTurn()ƒ==ƒPlayer.BLACK)
56 ƒƒƒƒ{ƒthis.blackRemoves.requestFocusInWindow();
57 ƒƒƒƒƒƒthis.blackRemoves.setText("");
58 ƒƒƒƒ}
59 ƒƒ}
60
61 ƒƒ/** Layout the view. */
62 ƒƒprivateƒvoidƒlayoutView()
63 ƒƒ{ƒ// A panel for the red player
64 ƒƒƒƒJPanelƒredƒ=ƒnewƒJPanel();
65 ƒƒƒƒred.add(this.redRemoves);
66 ƒƒƒƒred.add(this.redWins);
67 ƒƒƒƒred.setBorder(BorderFactory.createTitledBorder("Red"));
68
69 ƒƒƒƒ// A panel for the black player
70 ƒƒƒƒJPanelƒblackƒ=ƒnewƒJPanel();
71 ƒƒƒƒblack.add(this.blackRemoves);
72 ƒƒƒƒblack.add(this.blackWins);
73 ƒƒƒƒblack.setBorder(BorderFactory.createTitledBorder("Black"));
74
75 ƒƒƒƒ// Pilesize info.
76 ƒƒƒƒJPanelƒpSizeƒ=ƒnewƒJPanel();
725
13.4 BUILDING
Listing 13-12: The completed code for the NimView class (continued)
77 ƒƒƒƒpSize.add(this.pileSize);
THE
78 ƒƒƒƒpSize.setBorder(
Views can be complex. However, they follow a common pattern, shown in Listing 13-13,
which makes them much easier to understand and implement.
CHAPTER 13 | GRAPHICAL USER INTERFACES 726
1 importƒbecker.util.IView;
2 importƒjavax.swing.JPanel;
3 «listƒofƒotherƒimports»
4
5 publicƒclassƒ«viewName»ƒextendsƒJPanelƒimplementsƒIView
6 {ƒprivateƒ«modelClassName»ƒmodel;
7
8 ƒƒ«componentƒdeclarations»
9
10 ƒƒpublicƒ«viewName»(«modelClassName»ƒaModel)
11 ƒƒ{ƒsuper();
12 ƒƒƒƒthis.modelƒ=ƒaModel;
13 ƒƒƒƒthis.layoutView();
14 ƒƒƒƒthis.registerControllers();
15 ƒƒƒƒthis.model.addView(this);
16 ƒƒƒƒthis.updateView();
17 ƒƒ}
18
19 ƒƒpublicƒvoidƒupdateView()
20 ƒƒ{ƒ«statementsƒtoƒupdateƒtheƒcomponentsƒinƒtheƒview»
21 ƒƒ}
22
23 ƒƒprivateƒvoidƒlayoutView()
24 ƒƒ{ƒ«statementsƒtoƒlayƒoutƒtheƒcomponentsƒwithinƒtheƒview»
25 ƒƒ}
26
27 ƒƒprivateƒvoidƒregisterControllers()
28 ƒƒ{ƒ«statementsƒtoƒconstructƒandƒregisterƒcontrollers»
29 ƒƒ}
30 }
(figure 13-9)
We could write this user interface as one big view, as we did previously. However, this
view has a total of nine components to manage, raising the overall complexity.
Furthermore, the four components for the red player are managed almost exactly like
those for the black player. This suggests that some good abstractions might simplify the
problem.
KEY IDEA Recall that we wrote the model anticipating multiple views. The model has a list of
A view can views, and each time the model’s state changes, it goes through that list and tells each
be partitioned into view to update itself. This allows us to decompose the overall view into a number of
subviews. subviews. Each subview will add itself to the model’s list of views and will have its
updateView method called at the appropriate times.
This version of the interface will use three subviews: one for the red player, one for the
black player, and one to display the pile size. NimView will still exist to organize the
three subviews.
Dividing the view into several subviews has two distinct advantages. First, each view
can focus on a smaller part of the overall job, allowing it to be simpler, easier to under-
stand, easier to write, and easier to debug. Second, subviews can be easily changed or
even replaced without fear of breaking the rest of the interface.
CHAPTER 13 | GRAPHICAL USER INTERFACES 728
NimView is the overall view of the game. It is composed of the three subviews for the
players and the pile size. NimView does not (directly) display information about the
model nor does it (directly) update the model. Both of those tasks are delegated to the
subviews. NimView’s only task is to organize the subviews in a panel.
As seen in Listing 13-14, all NimView does is instantiate and lay out the subviews.
The NimPileView class, shown in Listing 13-15, is a simple view. It does not need to
update the model, so there are no controllers. It only has a JLabel that is updated via
the updateView method. addView is called at line 17 to add this view to the model’s
list of views.
19 ƒƒ}
20
21 ƒƒ/** Update the view. Called by the model when its state changes. */
22 ƒƒpublicƒvoidƒupdateView()
23 ƒƒ{ƒthis.pileSize.setText(""ƒ+ƒthis.model.getPileSize());
24 ƒƒ}
25
26 ƒƒ/** Layout the view. */
27 ƒƒprivateƒvoidƒlayoutView()
28 ƒƒ{ƒthis.pileSize.setFont(newƒFont("Serif",ƒFont.PLAIN,ƒ24));
29 ƒƒƒƒthis.add(this.pileSize);
30 ƒƒ}
31 }
NimPlayerView is a full-fledged view. It has its own components to lay out within
itself. Those components are used to update the model, so they need to have controllers
registered. The view also displays part of the state of the model—who’s turn it is and
who has won—and so it needs an updateView method and an instance variable to
store a reference to the model.
We’ll write NimPlayerView so that one instance of the class can be used for the red
player and a second instance for the black player. To meet this goal, it must store the
player it represents (lines 14 and 29 of Listing 13-16). The player is used in the
updateView method (lines 45 and 48) to determine which buttons to enable and
whether a winner should be declared.
The view has three buttons for user interaction. They all need to be added to the
view, be enabled and disabled as appropriate, and have controllers registered. These
tasks are all made easier by placing the buttons in an array (lines 16–20) and using
loops (lines 43–46, 58–61, and 69–72).
6 importƒjava.awt.Font;
7 importƒjava.awt.GridLayout;
8
9 /** Provide a view of the game of Nim focused on one particular player to a user.
10 *
11 * @author Byron Weber Becker */
12 publicƒclassƒNimPlayerViewƒextendsƒJPanelƒimplementsƒIView
13 {ƒprivateƒNimModelƒmodel;
14 ƒƒprivateƒPlayerƒplayer;
15 ƒƒ
16 ƒƒprivateƒJButton[]ƒremoveButtonsƒ=ƒnewƒJButton[]ƒ{
17 ƒƒƒƒnewƒJButton("Remove 1 Token"),ƒ
18 ƒƒƒƒnewƒJButton("Remove 2 Tokens"),
19 ƒƒƒƒnewƒJButton("Remove 3 Tokens")
20 ƒƒ};
21 ƒƒprivateƒJLabelƒwinnerƒ=ƒnewƒJLabel("Winner!");
22
23 ƒƒ/** Construct a view for one player.
24 ƒƒ *ƒ@param aModel The game's model.
25 ƒƒ *ƒ@param player The player for which this is the view. */
26 ƒƒpublicƒNimPlayerView(NimModelƒaModel,ƒPlayerƒaPlayer)
27 ƒƒ{ƒsuper();
28 ƒƒƒƒthis.modelƒ=ƒaModel;
29 ƒƒƒƒthis.playerƒ=ƒaPlayer;
30
31 ƒƒƒƒthis.layoutView();
32 ƒƒƒƒthis.registerControllers();
33
34 ƒƒƒƒthis.model.addView(this);
35 ƒƒƒƒthis.updateView();
36 ƒƒ}
37
38 ƒƒ/** Update the view to reflect recent changes in the model's state. */
39 ƒƒpublicƒvoidƒupdateView()
40 ƒƒ{ƒPlayerƒwhoseTurnƒ=ƒthis.model.getWhoseTurn();
41 ƒƒƒƒintƒpSizeƒ=ƒthis.model.getPileSize();
42 ƒƒƒƒ// Enable buttons if it's my player's turn and there are enough tokens on the pile.
43 ƒƒƒƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.removeButtons.length;ƒi++)
44 ƒƒƒƒ{ƒthis.removeButtons[i].setEnabled(
45 ƒƒƒƒƒƒƒƒƒƒwhoseTurnƒ==ƒthis.playerƒ&&ƒiƒ+ƒ1ƒ<=ƒpSize);
46 ƒƒƒƒ}
47 ƒƒƒƒthis.winner.setVisible(
48 ƒƒƒƒƒƒƒƒƒƒthis.model.getWinner()ƒ==ƒthis.player);
CHAPTER 13 | GRAPHICAL USER INTERFACES 732
49 ƒƒ}
50
51 ƒƒ/** Lay out the components for this view. */
52 ƒƒprivateƒvoidƒlayoutView()
53 ƒƒ{ƒGridLayoutƒgridƒ=ƒnewƒGridLayout(4,ƒ1,ƒ5,ƒ5);
54 ƒƒƒƒthis.setLayout(grid);
55
56 ƒƒƒƒFontƒfontƒ=ƒnewƒFont("Serif",ƒFont.PLAIN,ƒ24);
57
58 ƒƒƒƒforƒ(JButtonƒbƒ:ƒthis.removeButtons)
59 ƒƒƒƒ{ƒthis.add(b);
60 ƒƒƒƒƒƒb.setFont(font);
61 ƒƒƒƒ}
62
63 ƒƒƒƒthis.winner.setFont(font);
64 ƒƒƒƒthis.add(this.winner);
65 ƒƒ}
66
67 ƒƒ/** Register controllers for this view's components. */
68 ƒƒprivateƒvoidƒregisterControllers()
69 ƒƒ{ƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.removeButtons.length;ƒi++)
70 ƒƒƒƒ{ƒthis.removeButtons[i].addActionListener(
71 ƒƒƒƒƒƒƒƒƒƒnewƒRemoveButtonController(this.model,ƒiƒ+ƒ1));
72 ƒƒƒƒ}
73 ƒƒ}
74 }
In our previous controller the user typed the number of tokens to remove from the pile.
We need a different way to find out how many tokens to remove. One approach is to
have a separate controller object for each button. The controller has an instance vari-
able that remembers how many tokens to remove. That instance variable is set, of
course, when the controller is constructed. We can see this at line 71 of Listing 13-16,
where a new controller is instantiated for each button.
Removing a token involves six interacting classes. This is a level of complexity that we
haven’t seen before, but it is not uncommon. To keep things in perspective, it’s impor-
tant to think locally. For each method, we can ask, what is the job that this method has
to do? What services does it need from other classes to do that job?
But a global perspective can help, too. Figure 13-10 is a sequence diagram that can help
visualize the objects involved in removing a token and the sequence of actions taking place.
CHAPTER 13 | GRAPHICAL USER INTERFACES 734
action-
Performed
remove-
Tokens
updateAllViews
update-
View
get...
get...
updateView
getWhoseTurn
getPileSize
updateView
getPileSize
The six objects involved are shown at the top of the diagram, each with their class
name. In the case of NimPlayerView there are two, so we distinguish between the
instance for the red player and the instance for the black player. There are six JButton
objects, but it isn’t important to distinguish between them, so only one is shown.
The dashed line extending down from each object is its lifeline. In a complete sequence
diagram, the lifeline would begin with the object’s construction and end when the
object is no longer needed. The boxes along the lifeline represent a method executing in
that object. The solid arrows between the boxes represent one method calling another.
A dashed arrow with an open arrowhead represents a method finishing execution and
returning to its caller.
Putting all this together, the diagram begins in the upper-left corner with the
handleEvent method in JButton being called, presumably because the user clicked
735
On the lifeline for NimModel, we see that the longest box, corresponding to
removeTokens, calls a helper method in the same class, updateAllViews. This
helper method calls all the updateView methods in the views registered with
NimModel. Each of these, of course, calls additional methods.
By the time execution returns to the handleEvent method in JButton at the bottom-
left corner of the diagram, tokens have been removed from the model and all of the
views have been updated accordingly.
An inner class is a class that is nested inside another class.2 Inner classes are most use-
ful for defining small helper classes that are very specific to a particular task. By plac-
ing inner classes inside the class they are helping, we can make that relationship more
explicit and keep the definition of the helper class very close to the class it is helping.
Beyond this, the primary advantage of an inner class is that it can access the methods
and instance variables of its enclosing class—even the private methods and instance
variables.
KEY IDEA Views are usually written with inner classes for the controllers.
An inner class can
access instance Listing 13-18 shows the NimPlayerView (Listing 13-16) and
variables and RemoveButtonController (Listing 13-17) combined in a single file by making the
methods of its controller an inner class.
enclosing class.
2 There are actually four varieties of inner classes. We will focus on member classes. The other three
KEY IDEA
The first thing to notice about Listing 13-18 is that RemoveButtonController falls
An inner class is
between the opening and closing braces of theƒNimPlayerView class. The actual
placed inside another
order of instance variables, methods, and inner classes within the outer class doesn’t class, but outside of
matter to the compiler, but inner classes are generally placed at the end. all methods.
Second, the inner class accesses the model instance variable from the outer class at line 25.
The syntax for doing so is a little odd. We cannot write this.model because then we
would be referring to an instance variable in the RemoveButtonController class. To
access the outer class, first give the name of that class and then access the variable as usual.
It is also possible to write the following and let the compiler figure it out:
model.removeTokens(this.numRemove);
Each instance of the inner class is tied to a specific instance of the outer class. For
example, the game creates two instances of NimPlayerView, one for the red player
and one for the black player. Both of these objects create three controllers. The con-
trollers created for red’s instance of the view are forever tied to that instance. They will
access the methods and instance variables in red’s instance of the view and will never
access those in black’s instance.
With this approach, the controller will have no instance variables at all. This has two
implications. First, there are no instance variables to initialize, and we can let Java provide
a default constructor for us.3 Second, every instance is just like all the other instances, and
we can use the same controller for all three buttons. Listing 13-19 shows how.
Listing 13-19: A controller that uses the event object to avoid instance variables
ch13/nimInnerClass/
1 // Import classes needed by both view and controller.
2 publicƒclassƒNimPlayerViewƒextendsƒJPanelƒimplementsƒIView
3 {ƒprivateƒNimModelƒmodel;
4 ƒƒprivateƒJButton[]ƒremoveButtonsƒ=ƒnewƒJButton[]ƒ
5 ƒƒ{ƒnewƒJButton("Remove 1 Token"),ƒ
6 ƒƒƒƒnewƒJButton("Remove 2 Tokens"),
7 ƒƒƒƒnewƒJButton("Remove 3 Tokens")
8 ƒƒ};
9
10 ƒƒ// Other instance variables, constructor, updateView, and layoutView are omitted.
3 Omitting the parameterless or default constructor is an option for every class, but we have always
included it, when applicable, for clarity. Controllers are usually so small and specialized, however,
that we can omit them without loss of clarity.
CHAPTER 13 | GRAPHICAL USER INTERFACES 738
Listing 13-19: A controller that uses the event object to avoid instance variables (continued)
11
12 ƒƒ/** Register controllers for this view's components. */
13 ƒƒprivateƒvoidƒregisterControllers()
14 ƒƒ{ƒRemoveButtonControllerƒcontrollerƒ=ƒ
15 ƒƒƒƒƒƒƒƒƒƒnewƒRemoveButtonController();
16 ƒƒƒƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.removeButtons.length;ƒi++)
17 ƒƒƒƒ{ƒthis.removeButtons[i].addActionListener(controller);
18 ƒƒƒƒ}
19 ƒƒ}
20
21 ƒƒprivateƒclassƒRemoveButtonControllerƒextendsƒObject
22 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒimplementsƒActionListener
23 ƒƒ{ƒpublicƒvoidƒactionPerformed(ActionEventƒevt)
24 ƒƒƒƒ{ƒJButtonƒsrcƒ=ƒ(JButton)evt.getSource();
25 ƒƒƒƒƒƒifƒ(srcƒ==ƒremoveButtons[0])
26 ƒƒƒƒƒƒ{ƒmodel.removeTokens(1);
27 ƒƒƒƒƒƒ}ƒelseƒifƒ(srcƒ==ƒremoveButtons[1])
28 ƒƒƒƒƒƒ{ƒmodel.removeTokens(2);
29 ƒƒƒƒƒƒ}ƒelseƒifƒ(srcƒ==ƒremoveButtons[2])
30 ƒƒƒƒƒƒ{ƒmodel.removeTokens(3);
31 ƒƒƒƒƒƒ}ƒelse
32 ƒƒƒƒƒƒ{ƒassertƒfalse;ƒƒƒƒƒƒƒƒ// Shouldn't happen!
33 ƒƒƒƒƒƒ}
34 ƒƒƒƒ}
35 ƒƒ}
36 }
Note in line 24 that the getSource method returns an Object which must be cast to
an appropriate type. The source itself will often have useful information. For example,
if it were a text field, we could get the text typed by the user.
The cascading-if structure in lines 25–33 is fine for a small number of components,
but if the components are stored in an array, a loop can be more concise, as follows:
publicƒvoidƒactionPerformed(ActionEventƒevt)
{ƒJButtonƒsrcƒ=ƒ(JButton)evt.getSource();
ƒƒintƒiƒ=ƒ0;
ƒƒwhileƒ(removeButtons[i]ƒ!=ƒsrc)
ƒƒ{ƒi++;
ƒƒ}
ƒƒassertƒremoveButtons[i]ƒ==ƒsrc;
ƒƒmodel.removeTokens(i+1);
}
739
KEY IDEA The controller and view can also be integrated into the same class without the use of an
This is not a inner class. Many examples on the Web use this approach because it is quick and easy.
recommended It introduces a significant disadvantage, however, in that there is only one controller
approach, but its use
for all of the various components. With the previous techniques, you can easily write
is widespread.
one controller for a JButton and a different controller for a JTextField. Each con-
troller has its own actionPerformed method that is specific to a particular task.
When the controller and view are integrated, a single actionPerformed method must
handle both components. In terms of the software engineering principles studied in
Section 11.3.2, such integration reduces the cohesion of the methods (recall that we
want high cohesion). Nevertheless, the technique is shown here so that you can under-
stand it if and when you see it.
The technique works by implementing the required interfaces in the view class itself. In
Listing 13-20, the ActionListener interface is listed on the class header (lines 2–3)
and its only method, actionPerformed, is implemented at lines 14–23 just like any
other method. Note that there is no inner class. The “controller” is registered with the
JButton objects in line 10. Instead of constructing a separate object, a reference to the
view itself (that is, this) is passed to the button.
Listing 13-20: A version of NimPlayerView that integrates the view and the controller
ch13/nimIntegrated/
1 // Import classes needed by both view and controller.
2 publicƒclassƒNimPlayerViewƒextendsƒJPanelƒ
3 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒimplementsƒIView,ƒActionListener
4 {ƒ
5 ƒƒ// Other instance variables, constructor, updateView, and layoutView are omitted.
6
7 ƒƒ/** Register controllers for this view's components. */
8 ƒƒprivateƒvoidƒregisterControllers()
9 ƒƒ{ƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒthis.removeButtons.length;ƒi++)
10 ƒƒƒƒ{ƒthis.removeButtons[i].addActionListener(this);
11 ƒƒƒƒ}
12 ƒƒ}
13
14 ƒƒpublicƒvoidƒactionPerformed(ActionEventƒevt)
15 ƒƒ{ƒJButtonƒsrcƒ=ƒ(JButton)evt.getSource();
16
17 ƒƒƒƒintƒiƒ=ƒ0;
18 ƒƒƒƒwhileƒ(removeButtons[i]ƒ!=ƒsrc)
19 ƒƒƒƒ{ƒi++;
20 ƒƒƒƒ}
21 ƒƒƒƒassertƒremoveButtons[i]ƒ==ƒsrc;
CHAPTER 13 | GRAPHICAL USER INTERFACES 740
Listing 13-20: A version of NimPlayerView that integrates the view and the controller (continued)
22 ƒƒƒƒmodel.removeTokens(i+1);
23 ƒƒ}
24 }
In the following sections, we’ll use these strategies to learn how to display a set of color
names to use in Nim instead of “Red” and “Black”.
There are several ways to discover available components. One is to look at “A Visual
Index to the Swing Components,” which can be found at http://java.sun.com/docs/books/
tutorial/uiswing/components/components.html. It shows a sample of each component
and has links to documentation where you can learn more. Figure 13-11 shows a part
of the Web page that looks promising. It appears that at least two kinds of components
can display lists of color names, as we would like to do.
Clicking the links labeled “Combo box” and “List” leads to pages titled “How to Use
Combo Boxes” and “How to Use Lists.” The first page refers to the component
JComboBox, and the second page refers to JList.
741
Another option is to find one of several demonstration programs available. One that
comes with this textbook is shown in Figure 13-5. If you have downloaded the exam-
ple code for the textbook, you’ll find the code in the directory ch13/componentDemo.
Running the program and playing with the components will show that JSpinner is
also a possibility. In Figure 13-5, it displays “Sunday,” but it also “spins” through the
other days of the week. It could also spin through the color names we want to display.
Any of these options could work for us. Choosing between them is largely a matter of
personal taste. For now, we’ll choose JList.
When we identify the listeners for a component, we identify what kind of events it can
tell us about and therefore what kind of controllers we can write. Every component
may have the following six kinds of listeners:
➤ Component listeners listen for changes in the component’s size, position, or
visibility. Component listeners have methods like componentHidden,
componentResized, and componentMoved.
➤ Focus listeners listen for the component gaining or losing the ability to receive
keyboard input. Focus listeners have two methods, focusGained and
focusLost.
➤ Key listeners listen for key press events. Such events are fired only by the com-
ponent that has the keyboard focus. Key listeners have keyPressed,
keyReleased, and keyTyped methods.
➤ Mouse listeners listen for mouse clicks and the mouse moving into and out of
the component’s drawing area. Mouse listeners have five methods, including
mouseEntered and mouseClicked.
➤ Mouse motion listeners listen for changes in the cursor’s position within the com-
ponent. Such listeners have two methods, mouseMoved and mouseDragged.
➤ Mouse wheel listeners listen for mouse wheel movement over the component.
They have a single method, mouseWheelMoved.
CHAPTER 13 | GRAPHICAL USER INTERFACES 742
In addition to these six listeners, components have one or more additional listeners
that vary by component type. For example, we have already seen that JTextField
and JButton objects can have ActionListeners.
(figure 13-12)
r
ne
ne er
te
en
er
Listeners used by some of
is
It ent r
is er
ne
do onL
en
st
r
r
n
ne
cu ste
Li
st
te Java’s GUI components
i
te
W ect
Ot wLi
Li ste
is
Do eLi
nL
l
m
Li
tL
Se
g
r
tio
an
em
he
re
in
st
Component
Ch
Ac
Ca
JButton
JCheckBox
JColorChooser
JComboBox
JDialog
JEditorPane
JFileChooser
JFormattedTextField
JFrame
JList
JMenu
JMenuItem
JPasswordField
JPopupMenu
JProgressBar
JRadioButton
JSlider
JSpinner
JTabbedPane
JTable
JTextArea
JTextField
JToggleButton
JTree
There are two primary sources of information for working with Java’s GUI compo-
nents: the API documentation and the Java Tutorial.
KEY IDEA One primary source of information is the API, or application programming interface,
The API documentation. It is the class-by-class documentation found at http://java.sun.com/
documentation j2se/1.5.0/docs/api/. The documentation for each class gives an overview of the class,
provides full details its inheritance hierarchy, a list of the constructors provided, and a list of the methods
about each class.
provided, including detailed descriptions of what they do.
The first time you use a component, skim this documentation looking for methods that
sound useful. There may be lots of them—don’t get overwhelmed. For JList, the doc-
umentation lists about 70 methods, plus the 344 methods it inherits from its super-
classes.
What’s important when getting started using a JList? Constructing the component,
adding items to display in the list, adding a listener, and finding out which item on the
list was selected. Skimming the documentation for methods that sound relevant yields
the following:
➤ JList(): constructs an empty JList
➤ JList(Object[]ƒlistData): constructs a JList that displays the ele-
ments in the specified array
➤ addListSelectionListener: adds a listener to the JList
➤ getSelectedIndex: returns the index of the first selected item; if nothing is
selected, it returns -1
➤ getSelectedIndicies: returns an array of all the selected indices
➤ getSelectedValue: returns the first selected value
These methods answer most of our questions. We might have expected to find an “add
item” method to add items to the list, but we didn’t. Instead, it appears that we pass an
array of items to display when the component is constructed. It also appears that several
items can be selected at one time. We may want to make note of that for future reference.
CHAPTER 13 | GRAPHICAL USER INTERFACES 744
http://java.sun.com/docs/books/tutorial/uiswing/components/componentlist.html, contains
a long list of topics with names like “How to Make Applets” and “How to Use Lists.” The
API documentation often provides direct links to these sections of the tutorial.
Clicking the “How to Use Lists” link opens a document that includes sample code and
sections titled “Initializing a List,” “Selecting Items in a List,” and “Adding Items to
and Removing Items from a List.” All sound helpful!
Building on the discoveries of someone else is always easier than starting from scratch.
When learning to use a new component, look for sample code using it. The Java
Tutorial is a good place to look, particularly in the “How to…” sections referenced
earlier.
Another source for sample code that matches the style presented in this textbook is the
componentDemo program shown in Figure 13-5. If you run the program and click an
element in the JList, an entry is added to the table at the bottom of the frame. The
view column says “ListView.” This is the name of the class containing the JList. The
second column, “Listener,” says “ListView$ListController.” That’s the name of the
controller class that handled your mouse click—the ListController class that is an
inner class within the ListView class.
Open the source for ListView and you’ll find the code constructing the JList, laying it
out within a view, and registering a controller, as well as the code for the controller itself.
Much of this code can be cut and pasted directly into the program you’re writing.
The last piece of advice is to work incrementally. Start with small goals for the compo-
nent. Meet those goals and then move on to more ambitious goals. For example, you
might begin by displaying the JList in a view. Listing 13-21 shows a minimal view
with the goal of showing a JList with the names of some colors and detecting when
one has been selected.
745
Running a program that places this view in a frame appears as shown in Figure 13-13
and proves that we have made significant progress. The list shows the seven colors and
it prints a message when one is selected. However, there are two problems. First, each
time a color is selected, two copies of the message are printed by the controller. Second,
the list has no scroll bars. If the window is made smaller than the list, part of the list
simply disappears.
(figure 13-13)
ƒƒpublicƒvoidƒvalueChanged(ListSelectionEventƒevt)
ƒƒ{ƒifƒ(!evt.getValueIsAdjusting())
ƒƒƒƒ{ƒSystem.out.println(
ƒƒƒƒƒƒƒƒƒƒ"selected "ƒ+ƒView.this.list.getSelectedValue());
ƒƒƒƒ}
ƒƒ}
The problem of the missing scroll bars can be solved by searching the JList class doc-
umentation for “scroll.” That search finds the following:
JScrollPaneƒscrollPaneƒ=ƒnewƒJScrollPane(dataList);
KEY IDEA It turns out that JPanel’s default layout manager, FlowLayout, allows the list to take
Component-size up as much space as it requests. JScrollPane does not show the scroll bars until the
problems are often available space is less than the requested space. BorderLayout is a layout manager
related to the layout that forces its components to fit within the available space. Using it to manage the
manager.
view’s layout results in the scroll bars appearing when the JList is small. The result-
ing code for layoutView is as follows:
ƒƒprivateƒvoidƒlayoutView()
ƒƒ{ƒthis.setLayout(newƒBorderLayout());
ƒƒƒƒthis.listƒ=ƒnewƒJList(newƒString[]ƒ{"Red",ƒ"Green",ƒ
ƒƒƒƒƒƒ"Blue",ƒ"Yellow",ƒ"Orange",ƒ"Pink",ƒ"Black"});
ƒƒƒƒJScrollPaneƒscrollpaneƒ=ƒnewƒJScrollPane(this.list);
ƒƒƒƒthis.add(scrollpane,ƒBorderLayout.CENTER);
ƒƒ}
As shown here, it is unrealistic to expect to understand and use a complex class like
JList on the first try. An excellent strategy is to work incrementally. Understand and
implement the basics, make note of the remaining issues, and then solve them one at a
time. Using this strategy, we are well on our way to making effective use of the JList
component. Reasonable next steps include making calls to the model in response to
user selections and, if required, learning how to add new values to the list while the
program is running.
In Section 13.8.1, we will implement a similar class to simply display a pile of tokens
for the game of Nim. In Section 13.8.2, we will go a step further and add a listener for
mouse events so that the user can utilize our new component to select the tokens to
remove from the pile.
(figure 13-14)
Custom component
representing a pile of
tokens for Nim
Two crucial parts of the class are instance variables, used to either store or acquire the
information required to do the painting (lines 4–5), and the paintComponent method
(lines 27–40).
Two instance variables are required: numTokens stores the actual number of tokens to LOOKING BACK
display; maxTokens stores the maximum number that could be displayed. The maxi- Repainting is
mum is used to scale the circles appropriately; it is set with the constructor. numTokens explained in more
detail in Section 6.7.2.
is set using a mutator method, setPileSize, called from the updateView method in
the view that contains the PileComponent object. When the pile size is changed,
this.repaint() must be called. It tells the Java system that it should call
paintComponent as soon as possible to redraw the pile.
Lines 35-39 use a loop to draw each of the tokens in the pile.
One other detail is setting the minimum and preferred size of the component in lines 12 and
13. Without these statements, the component’s size will default to a barely visible 1 x 1
pixel square.
749
We can make PileComponent interactive, enabling users to use the mouse to select a
number of tokens by performing the following steps, also illustrated in Figure 13-15.
mouse
pressed (figure 13-15)
Sequence of mouse
mo gge
dra
actions triggering a
us d
e
selection
mouse
released
You may notice similarities with what we have done before. For example, both a com- KEY IDEA
ponent and a model call a method when their state is changed (Step 3), and both have A component has
a list of objects to inform when something significant happens (Step 4). features in common
with both models
and views.
751
The first three steps in Figure 13-16 were already done in the earlier version of
PileComponent. In the following subsections, we will discuss Steps 4 and 5 in more
detail, referring to Listing 13-23, which contains the code for the completed component.
This new, interactive version of PileComponent will be called PileComponent2.
When our component is used in a view, we will want to add controllers to it that
update the model. They will implement an interface such as ActionListener or
ListSelectionListener. Now, because we are writing the component, we can
choose which listener interface to use. Of all the listeners listed in Figure 13-12,
ActionListener seems the most appropriate.
LOOKING BACK In lines 101–108 we provide a private method named handleEvent, to be called
Listing 13-8 shows a when the component detects the user selecting some tokens. It constructs an
simplified version of ActionEvent object and then loops through all the registered controllers, calling their
JTextField. It also actionPerformed method and passing the event object.
has a handleEvent
method.
Writing and Registering Listeners
The last step, and the most complicated one, is figuring out when to call the
handleEvent method. To do so, we will write two inner classes implementing
MouseListener and MouseMotionListener. The first listener 4 will be informed
each time something happens to the mouse button. The second listener will be
informed each time the mouse moves. Mouse-related events are split into two listeners
because there are many motion events. If the component only cares about mouse clicks,
we don’t want to incur the overhead associated with mouse motion events.
4 We use the term “listener” rather than “controller” because these classes will not be interacting with
Of course, the component should provide feedback on which tokens have been
selected. This is accomplished in the paintComponent method. Lines 71–75 draw the
bounding rectangle, and lines 82–84 determines if it surrounds the token currently
being drawn. If it does, an instance variable is incremented, and the token’s color is
changed to yellow. An accessor method, getNumSelected, is provided to allow
clients to get the number of selected tokens.
Listing 13-23: An interactive component that allows the user to select a number of tokens
ch13/nimMultiView/
1 importƒjavax.swing.JComponent;
2 importƒjava.awt.Graphics;
3 importƒjava.awt.Insets;
4 importƒjava.awt.Dimension;
5 importƒjava.awt.Point;
6 importƒjava.awt.Color;
7 importƒjava.awt.Rectangle;
8 importƒjava.awt.event.MouseListener;
9 importƒjava.awt.event.MouseMotionListener;
10 importƒjava.awt.event.MouseEvent;
11 importƒjava.awt.event.ActionListener;
12 importƒjava.awt.event.ActionEvent;
13 importƒjava.util.ArrayList;
14
15 /** A component that displays a pile of tokens and allows the user to select a number of
16 * them. It informs registered listeners when tokens have been selected. Allows the
17 * client to change the number of tokens in the pile.
18 *
19 * @author Byron Weber Becker */
20 publicƒclassƒPileComponent2ƒextendsƒJComponentƒ
21 { // Store the controllers to inform when a selection takes place.
753
22 ƒƒprivateƒArrayList<ActionListener>ƒactionListenersƒ=ƒ
23 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒnewƒArrayList<ActionListener>();
24
25 // Information for painting the component.
26 ƒƒprivateƒintƒnumTokensƒ=ƒ0;
27 ƒƒprivateƒintƒmaxTokens;
28
29 ƒƒprivateƒRectangleƒselectionƒ=ƒnull; // selected area
30 ƒƒprivateƒintƒnumSelectedƒ=ƒ0; // # tokens in selected area
31 ƒƒ
32 /** Create a new component.
33 * @param maxTokens The maximum number of tokens that can be displayed. */
34 ƒƒpublicƒPileComponent2(intƒmaxTokens)
35 ƒƒ{ƒsuper();
36 ƒƒƒƒthis.maxTokensƒ=ƒmaxTokens;
37 ƒƒƒƒthis.setMinimumSize(newƒDimension(40,ƒ60));
38 ƒƒƒƒthis.setPreferredSize(newƒDimension(60,ƒ90));
39
40 ƒƒ// Add the mouse listener.
41 ƒƒƒƒthis.addMouseListener(newƒMListener());
42 ƒƒƒƒthis.addMouseMotionListener(newƒMMListener());
43 ƒƒ}
44
45 /** Add an action listener to this component's list of listeners. */
46 ƒƒpublicƒvoidƒaddActionListener(ActionListenerƒlistener)
47 ƒƒ{ƒthis.actionListeners.add(listener);
48 ƒƒ}
49
50 /** Set the size of the pile.
51 * @param num The new pile size. 0 <= num <= maxTokens */
52 ƒƒpublicƒvoidƒsetPileSize(intƒnum)
53 ƒƒ{ƒifƒ(numƒ<ƒ0ƒ||ƒnumƒ>ƒthis.maxTokens)
54 ƒƒƒƒ{ƒthrowƒnewƒIllegalArgumentException("too many/few tokens");
55 ƒƒƒƒ}
56 ƒƒƒƒthis.numTokensƒ=ƒnum;
57 ƒƒƒƒthis.selectionƒ=ƒnull;
58 ƒƒƒƒthis.numSelectedƒ=ƒ0;
59 ƒƒƒƒthis.repaint();
60 ƒƒ}
61
62 /** Paint the component. */
63 ƒƒpublicƒvoidƒpaintComponent(Graphicsƒg)
CHAPTER 13 | GRAPHICAL USER INTERFACES 754
Listing 13-23: An interactive component that allows the user to select a number of tokens
(continued)
105 ƒƒƒƒforƒ(ActionListenerƒalƒ:ƒthis.actionListeners)
106 ƒƒƒƒ{ƒal.actionPerformed(evt);
107 ƒƒƒƒ}
108 }
109
110 /** Adjust the selection's size. */
111 ƒƒprivateƒvoidƒadjustSelectionSize(PointƒmPos)
112 ƒƒ{ƒthis.selection.setSize(mPos.xƒ-ƒthis.selection.x,
113 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒmPos.yƒ-ƒthis.selection.y);
114 ƒƒƒƒthis.repaint();ƒ
115 ƒƒ}
116
117 /** Listen for mouse events within the pile. */
118 ƒƒprivateƒclassƒMListenerƒextendsƒObjectƒ
119 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒimplementsƒMouseListener
120 ƒƒ{
121 /** A mousePressed event signals the beginning of a selection. */
122 ƒƒƒƒpublicƒvoidƒmousePressed(MouseEventƒe)
123 ƒƒƒƒ{ƒPileComponent2.this.selectionƒ=ƒ
124 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒnewƒRectangle(e.getPoint());
125 ƒƒƒƒ}
126
127 /** A mouseReleased event signals the end of a selection. Finish up the
128 * selection and inform the listeners. */
129 ƒƒƒƒpublicƒvoidƒmouseReleased(MouseEventƒe)
130 ƒƒƒƒ{ƒPileComponent2.this.adjustSelectionSize(e.getPoint());
131 ƒƒƒƒƒƒPileComponent2.this.handleEvent();
132 ƒƒƒƒ}
133 ƒƒƒ
134 // Required by MouseListener but not needed in this program.
135 ƒƒƒƒpublicƒvoidƒmouseClicked(MouseEventƒe)ƒƒƒ{}
136 ƒƒƒƒpublicƒvoidƒmouseEntered(MouseEventƒe)ƒƒƒ{}
137 ƒƒƒƒpublicƒvoidƒmouseExited(MouseEventƒe)ƒƒƒƒ{}
138 ƒƒ}
139
140 /** Listen for mouse events within the pile. */
141 ƒƒprivateƒclassƒMMListenerƒextendsƒObjectƒ
142 ƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒƒimplementsƒMouseMotionListener
143 ƒƒ{
CHAPTER 13 | GRAPHICAL USER INTERFACES 756
Listing 13-23: An interactive component that allows the user to select a number of tokens
(continued)
144 /** The bounds of the selection's rectangle changed. Adjust it. */
145 ƒƒƒƒpublicƒvoidƒmouseDragged(MouseEventƒe)
146 ƒƒƒƒ{ƒPileComponent2.this.adjustSelectionSize(e.getPoint());
147 ƒƒƒƒ}
148 ƒƒƒƒ
149 // Required by MouseMotionListener but not needed in this program.
150 ƒƒƒƒpublicƒvoidƒmouseMoved(MouseEventƒe)ƒƒƒƒƒ{}
151 ƒƒ}
152 }
13.9 Patterns
Name: Model-View-Controller
Context: A program requires a graphical user interface to interact with the user. You
want to program it with the good software engineering principles of encapsulation,
information hiding, high cohesion, and low coupling to facilitate future changes.
Solution: Organize the program into a model with one or more views and controllers.
The model abstracts the problem the program is designed to solve. Each view displays
some part of the model to the user, while controllers translate user actions in a view
into method calls on the model.
The Model-View-Controller pattern requires three templates: one for the model, one for
LOOKING AHEAD
the combination of a view and a controller, and one for the main method. Listing 13-13
contains an excellent start on a template for views, but needs an inner class for a controller. Written Exercise 13.2
asks you to prepare
Listing 13-1 and Listing 13-3 can be generalized for the model’s template and the main
these templates.
method’s template, respectively.
Consequences: Because the model depends only on objects implementing the IView
interface, coupling is extremely low. The interface can be changed or even completely
replaced, usually without changing the model.
757
It is also possible to create components to perform specific tasks for which no existing
component is available.
CHAPTER 13 | GRAPHICAL USER INTERFACES 758
layout
managers
organi
ze th
e pla
ce m
ent
views register of
contro
of llers
w i th
d y are co
b mpose
ate e
upd otifi
by
d of
s
ral
ed
n
lay
are
seve
components
isp
ave
is d
yh
s of
ample
ma
a model are ex
JButton,
JTextField
to ith
y
dw
db
te n re
is updated by
lis iste
lle
reg
ca
are inner class
are
n
as a
rit ten event
often w
are objects
are passed
event
contain methods
controllers
are sp
ecified
by
implement
ActionListener, listener
are examples of interfaces
ListSelectionListener
Written Exercises
13.1 Explain how using subviews (Section 13.5) is good software engineering. Refer
specifically to the concepts of cohesion and coupling.
13.2 Write the three code templates required for the Model-View-Controller pattern.
Listing 13-13 contains an excellent start on a template for views, but needs an
inner class for a controller. Listing 13-1 and Listing 13-3 can be generalized for
the model’s template and the main method’s template, respectively.
13.3 Prepare a class diagram showing the relationships between the classes in the
Model-View-Controller pattern. Assume the controller has been written in a
separate class, as shown in Listing 13-9, and implements an ActionListener.
13.4 List the signatures for all the methods required to implement a WindowListener.
759
Programming Exercises
13.7 Find the code for the version of Nim with multiple views.
a. Add a new view whose function is to offer hints to the current player. (Hint:
Assuming the rules where 1, 2, or 3 tokens may be removed, a player who
leaves 1, 2, or 3 tokens for his or her opponent has made a serious mistake.
Similarly, a player who leaves exactly four tokens is in a very strong posi-
tion. Generalize these observations.)
b. Modify the NimPlayerView class to use a JComboBox for user input
instead of JButton objects.
c. Modify the NimPlayerView class to use a JSlider for user input.
d. Add a new view whose function is to start a new game. The user should be
able to specify who starts and how large the initial pile of tokens should be.
The player should also be able to start a new game with the program choos-
ing either or both of these values randomly.
e. Views do not actually need to belong to a graphical user interface. Write a
class named NimLogger that implements IView. Modify the Nim program
to use NimLogger to write the state of the game after each move to a file.
(Hint: You should not extend JPanel or include any classes from the
javax.swing or java.awt packages. Create the NimLogger object in the
main method.)
f. Modify the model and the view so users may remove up to half of the
remaining tokens in each turn. Start the game with a random pile of 20 to
30 tokens. The existing views with three buttons each are inappropriate.
Design a new view.
g. Modify the PileComponent2 class to show the tokens as a block, three
tokens wide. The top row of the block may have less than three tokens.
h. The PileComponent2 class shown in Listing 13-23 does not work when a
user clicks and drags the mouse upward or leftward. The problem is that the
width or the length of the selection rectangle becomes negative, resulting in
an “empty” rectangle. Fix this problem.
i. The PileComponent2 class shown in Listing 13-23 currently allows the
user to select any number of visible tokens, even though the game only
allows a maximum of three tokens to be removed. Fix the component so
that the selection rectangle is not allowed to enclose more than three tokens.
CHAPTER 13 | GRAPHICAL USER INTERFACES 760
13.8 Find the code displayed in Listing 13-21. Write a simple main method to dis-
play it in a frame. Observe that it is possible to select several items at once
using the Shift or Control keys.
a. Modify the program to print all of the items that have been selected.
b. Modify the program so users can select only one item at a time.
c. The JList documentation includes sample code for a class named
MyCellRenderer. Read the documentation, and then change the program
so that each element of the list is displayed using the appropriate color.
Programming Projects
13.9 Write a program to assist users in calculating their target heart rate for an exer-
cise program. You can find many formulas on the Web for calculating target
heart rate. One is based on the user’s age, resting heart rate, and targeted inten-
sity: intensity * (220 – age – restingHR) + restingHR, where intensity is a per-
centage (typically 80 to 90%), age is the user’s age in years, and restingHR is
the user’s resting heart rate in beats per minute. The model will have mutator
methods for intensity, age, and restingHR, and accessor methods for those
three plus the target heart rate.
Two possible views are shown in Figure 13-17.
a. Write the program’s view using JTextField components.
b. Write the program’s view using JSlider components.
(figure 13-17)
13.10 Write a program that allows you to display font samples. A proposed user
interface is shown in Figure 13-18. The model for this program will have meth-
ods such as setFontName, setFontSize, setBold, setItalic, and
getFont. The components used in the interface include JComboBox,
JCheckBox, JTextArea, and becker.gui.FormLayout.
761
Two interfaces
An interface for generating font samples An interface for plotting the popularity of names
through time
(figure 13-19)
5 The original idea for this problem is attributed to Nick Parlante at Stanford University.
CHAPTER 13 | GRAPHICAL USER INTERFACES 762
13.13 Use the JEditorPane to implement a simple Web browser like the one shown
in Figure 13-19. Users should be able to type a URL into a text field and have
it displayed in the JEditorPane. Your browser should also correctly follow
links to display a new page. The JEditorPane may not be editable for links
to work. The model for the browser will be the current URL to display.
Enhancements may require adding a history list and other features to
the model.
a. Add scroll bars to the JEditorPane that show only if needed.
b. Add a toolbar with Forward, Back, and Home buttons.
c. Use a JComboBox for entering URLs. Add URLs the user has typed to the
JComboBox for easier selection in the future.
13.14 Implement the game of Tic-Tac-Toe for two users (see Figure 13-20). Search
the Web for the rules if you are unfamiliar with the game. Use a button for
each of the nine squares to gather input from the users. Disable the buttons
and change their labels as they are played. When the mouse is moved over an
unplayed square, show either X or O, depending on whose turn it is. Announce
the winner with a dialog box and start a new game.
(figure 13-20)
13.15 Write a program that displays a bouncing ball and allows for its speed
to be changed and the size of the box it bounces in to be changed
(see Figure 13-20). Note the following hints:
➤ Read the documentation for the javax.swing.Timer class. An appropriate
delay is 1000/30. There are several classes named Timer; be sure to read the
right one.
➤ Write a BallModel class with methods such as getBallBounds and
setBoxBounds. The java.awt.Rectangle class is convenient for main-
taining size and position information for both the ball and the box. The
BallModel will also contain an instance of Timer, updating the position of
the ball every time it “ticks.”
763
(figure 13-21)
Several views of a
triangle model
Epilogue
Congratulations! You’ve learned a lot about programming computers using this text-
book. However, programming represents only a portion of what computer specialists
do. If you choose to continue studying computer science, what do you have to look for-
ward to? This epilogue gives a glimpse.
The topics discussed here are based on curricular recommendations by the Association
for Computing Machinery and the Computer Society of the Institute for Electrical and
Electronic Engineers, the two leading professional organizations for computing disci-
plines. These topics represent a widely accepted core of material that undergraduate
computer science students should know.
Programming Fundamentals
Most of the material we have studied falls into the area of programming fundamentals.
Future courses include further study of data structures and recursion. Data structures
are used to organize data, arrays being one of the simplest. The HashSet and TreeMap
classes are implementations of other data structures.
Recursion occurs when a method calls itself to solve a smaller instance of the same
problem. It’s a powerful technique that is often explored at the same time as recursive
data structures, such as trees and lists.
Discrete Structures
Discrete structures are areas of mathematics that are particularly helpful to computing
professionals, including the study of sets, logic, proof techniques, graphs, and trees.
When we studied Boolean expressions, we were learning about logic.
Many problems in the real world, such as routing messages on the Internet, can be rep-
resented as a mathematical tree or graph (which is different from graphing data on a
chart). Knowledge of discrete structures is often critical to solving such problems.
765
EPILOGUE 766
When we explored enlarging arrays to hold more elements, we briefly touched on algo-
rithms and complexity. We identified two different algorithms: one that enlarges the
array by one element, and one that doubles the size of the array. However, their
complexity—the amount of time each takes to do its job—was very different.
Experimental results were given in Figure 10-19. It’s also possible to approximate it
mathematically.
These topics address how the computer hardware is organized and how the very low-
est levels of software interact with it. In Section 8.2.1 we discussed how the computer’s
memory is organized like a large array. Other topics within this area include how the
CPU (central processing unit) works, how to connect devices such as keyboards and
networks to the computer, and how your programs and data are represented within the
computer.
Operating Systems
The operating system manages the computer’s resources for its users and provides ways
for programmers to access these resources. Perhaps one of the clearest ways we use the
operating system is in performing input and output via the console or files. The oper-
ating system takes care of details like finding free space on the disk for our files, orga-
nizing the files into directories, and interacting with the electronics in the disk drive
itself to read and write the data.
Modern operating systems appear to do many things concurrently. Our brief explo-
ration of threads to run several robots concurrently (Section 3.5.2) introduce some of
the techniques involved, but not their complexity.
Net-Centric Computing
EPILOGUE
Programming Languages
Most computer scientists use only one or two languages from any given paradigm, but
often switch between paradigms depending on the problem they are solving.
Human-Computer Interaction
Our work in building graphical user interfaces provides a good introduction to this
area of study. Other topics in this area include how the psychology and physiology of
people affect the design of interfaces, techniques for evaluating the quality of inter-
faces, and processes for designing interfaces.
Graphics and visual computing is composed of four overlapping areas. First, computer
graphics is the art and science of using computers to communicate using visual images.
Such images will have an underlying model. Subareas of graphics include developing
models to facilitate creating and viewing images, designing devices and techniques that
facilitate human interaction with the model, finding techniques for converting the
model to visual form, and storing images. Applications include everything from movie
special effects to manipulating images taken with a digital camera.
Lastly, computer vision seeks to understand the properties and structure of the 3D
world using 2D images. A robot that can navigate around obstacles is a classic test for
computer vision.
EPILOGUE 768
Intelligent Systems
This area is also called artificial intelligence (AI). It is concerned with designing
autonomous agents that act rationally in interactions with their environment, other
agents, and people. Robots are one application of such techniques.
Artificial intelligence also provides techniques for solving problems that are difficult or
impractical to solve using exact methods. These include using heuristics to guide a
search, representing a human expert’s knowledge in a program to help non-experts,
and exploring ways that programs can “learn” from experience.
Information Management
Computers are a tool that can be applied to the benefit or the detriment of society.
Computing professionals must be able to ask serious questions about the impact of
their work on their users and society as a whole, and have the intellectual tools to eval-
uate the proposed answers. This area of computer science interacts with philosophy,
ethics, history, social studies, risk analysis, and similar fields.
Software Engineering
Computers represent numbers with finite precision—for example, 1/10 cannot be repre-
sented exactly in a computer. This simple fact requires techniques to overcome the com-
puter’s limitations when it is used for applications such as modeling molecules, fluids,
and drug interactions. Another area is concerned with performing calculations efficiently,
often on the huge numbers of equations necessary to forecast weather.
769
EPILOGUE
Summary
Learning to program is only the tip of the iceberg known as computer science. Your
new knowledge of programming can be a stepping stone to a successful career as a
computing professional. However, whether or not you choose to pursue a computing
career, your understanding of programming will help you use computers more effec-
tively in almost any career you choose to pursue.
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 771
Appendix A Glossary
Most scientific disciplines have a specialized vocabulary, allowing experts in the field
to communicate precisely with each other. Computer science is no different. Gathered
here are all the specialized terms used in the text, together with brief definitions. Come
here for a quick reminder of a term’s definition.
Key Terms
absolute path—A sequence of directories separated by a special character and begin-
ning with a known location that is used to specify the location of a file. See also
relative path.
abstract class—A class that declares or inherits an abstract method. Such classes are
declared using the abstract keyword and are often used to declare types in poly-
morphic programs.
abstract method—A method that does not have a body. Such methods must be
declared with the abstract keyword.
Abstract Windowing Toolkit (AWT)—A collection of classes used to implement graphical
user interfaces.
abstraction—A method of dealing with complexity that eliminates or hides irrelevant
details and groups other sets of details into coherent, higher-level chunks.
access modifier—The keywords public, private, and protected. An access modi-
fier controls which clients may have access to the method it modifies.
accessor method—A method that returns the value of an instance variable.
address—A numeric identifier for a particular memory location.
algorithm—A finite set of step-by-step instructions that specify a process.
alias—An alternate name or reference for an object. All of an object’s aliases can be
used to access the object.
and—A logical connector written in Java as &&. The result is true if and only if both
operands are true.
anonymous class—A class without a name. It is used to declare and instantiate a single
object at the point where the object is needed.
API—See application programming interface.
application programming interface (API)—The set of publicly available methods by
which a program accesses the services offered by a class or package.
architecture—The manner in which the most important classes in a program relate to
each other.
771
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 772
772
APPENDIX A | GLOSSARY
773
KEY TERMS
class—The source code that defines one or more objects that offer the same services
and have the same attributes (but not necessarily the same attribute values).
class diagram—A graphical representation of one or more classes that show their
attributes, services, and relationships with other classes.
class variable—A variable that is shared by all instances of a class. Also called a static
variable. See also instance variable, parameter variable, temporary variable.
classpath—A list of one or more file paths where the Java system looks for the com-
piled classes used in a program.
client—An object that uses the services of another object, called the server.
close—To indicate that a program is finished using a file so that resources can be
released.
closed for modification—The idea that a mature class should be extended rather than
modified when changes or enhancements are needed. See also open for extension.
cohesion—The extent to which each class models a single, well-defined abstraction and
each method implements a single, well-defined task.
collaborator—A class that works with another class to accomplish some task.
color chooser—A graphical user interface component designed to help a user choose
a color.
column-major order—A 2D array algorithm that accesses the array such that the col-
umn changes more slowly than the row. See also row-major order.
command—A service that changes the state of an object or otherwise carries out some
action. See also query, service.
command interpreter—A program that repeatedly accepts a textual command from a
user and then interprets or executes the command.
comment—An annotation in the source code intended for human readers. Comments
do not affect the execution of the program.
comment out code—To put code inside comments so that it is no longer executed when
the program is run.
comparison operator—The operators used to compare the magnitude of two values: <,
<=, ==, !=, >=, and >.
compile—To translate source code into a format more easily executed by a computer,
such as byte code.
compiler—A computer program that compiles or translates source code into a format
more easily executed by a computer.
compile-time error—A programming error that is found when the program is com-
piled. See also intent error, run-time error.
component—An object such as a button or text box that is designed to be used as part
of a graphical user interface.
composition—A relationship between two classes in which one holds a reference to the
other in an instance variable. Also known as has-a.
concatenation—Joining two strings to form a new string.
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 774
774
APPENDIX A | GLOSSARY
concept map—A diagram that uses labeled arrows to connect concepts, represented by
a few words.
concrete class—A class that implements the abstract methods named in its superclasses
or the methods named in an interface. See also abstract class.
console—A window used by a program to communicate with a person using printed
characters on lines that appear one after another.
constant—A meaningful name given to a value that does not change.
constraint—An object limiting how a user interface component may be positioned.
constructor—A service provided by a class to construct or instantiate objects belonging
to that class.
content pane—The part of a frame designed to display the components of a user interface.
contract—An agreement specifying what client and server objects can each expect from
each other.
control characters—Character codes used to control a terminal or printer. Examples
include the newline and tab characters.
controller—The part of the Model-View-Controller pattern responsible for gathering
input from the user and using it to modify the model. See also model, view.
correct—A description of a program that meets its specification.
count-down loop—A loop controlled by a counter variable that is decremented until it
reaches zero.
coupling—The extent to which the interactions between classes are minimized.
CRC card—A piece of paper recording the class name, responsibilities, and collaborators
for one class during a program’s walk-through. CRC is an abbreviation for Classes,
Responsibilities, and Collaborators. See also walk-through.
cursor—A marker that divides a program’s input into the part that has already been
read and the part that has not yet been read. Also used to refer to an insertion point.
dangling else—A combination of if statements and an else-clause where it is
unclear to which if statement the else clause belongs.
data acquisition methods—Methods used to obtain data from an input stream, such as
the Scanner class. See also data availability methods.
data availability methods—Queries used to detect the kind of data available to be read
from an input stream, such as the Scanner class. See also data acquisition methods.
debug—The process of removing bugs from a program.
debugger—A tool used to help debug programs by stopping the program’s execution at
designated points, executing the program one statement at a time, and showing the
values of the program’s variables.
declaration statement—A statement that introduces a new variable into a program.
deep copy—A copy of an object that also copies any objects to which it refers. See also
shallow copy.
delimiter—A value such as a space or colon that separates other values or groups of values.
design by contract—A method for designing a program by consistently specifying the pre-
conditions and postconditions of each method and invariants on classes as a whole.
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 775
775
KEY TERMS
detail—An informal term referring to an instance variable or a method.
development cycle—Steps that are repeated while implementing a program, including
choosing scenarios, writing code to implement them, testing the result with users,
and possibly updating the program’s design. One part of a larger development
process. See also development process.
development process—A process to direct the design and implementation of a program.
documentation comment—A comment designed to be extracted from the source code
and used as reference material.
easy to learn—One of five criteria used to evaluate user interfaces. In particular, how
well the program supports users learning to use it as well as those deepening their
understanding of it. See also five Es.
effective—One of five criteria used to evaluate user interfaces. In particular, the com-
pleteness and accuracy with which users achieve their goals for using the program.
See also five Es.
efficient—1. One of five criteria used to evaluate user interfaces. In particular, the speed
and accuracy with which users complete tasks while using the program. See also five
Es. 2. Solving a problem without wasting such resources as memory or time.
element—One item in a collection.
else clause—The part of an if statement that is executed if the Boolean expression
is false.
encapsulation—Containing an object’s attributes within itself, allowing access to them
only via the object’s public services.
engaging—One of five criteria used to evaluate user interfaces. In particular, the degree
to which the program is pleasant or satisfying to use. See also five Es.
enumerated type—A reference type that has a programmer-defined set of values.
enumeration—See enumerated type.
equivalence—See object equality.
error tolerant—One of five criteria used to evaluate user interfaces. In particular, the
degree to which the program prevents errors and facilitates recovery from those
that do occur. See also five Es.
escape sequence—An alternative means of writing characters that are normally used
for another purpose. For example, \” to include a double quote in a string.
evaluate—The process of calculating the value of an expression.
evaluation diagram—A diagram showing how an expression is evaluated.
event—An action in the user interface to which the program must respond.
event object—An object containing information about one event.
exception—A type of error message that includes information about how the program
arrived at the point at which the error occurred. See also checked exception,
unchecked exception.
exponent—The part of a number expressed in scientific notation that indicates how far
and in which direction the decimal point should be shifted. See also mantissa, sci-
entific notation.
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 776
776
APPENDIX A | GLOSSARY
777
KEY TERMS
identifier—A name for a part of a program, such as a class, a variable, or a method.
immutable—Immutable objects cannot be changed after they are created. See also mutable.
implicit parameter—A reference to the object used to call a method. May be accessed
within the method with the keyword this.
index—The position of one element within an ordered collection, such as an array, a
string, or an ArrayList.
infinite loop—A loop that lacks a way to affect the termination condition, resulting in
its indefinite execution.
infinite recursion—A situation in which a method calls itself repeatedly with no provision
for avoiding another call to itself.
information hiding—Hiding and protecting the details of a classes’ operation from others.
inherit—To receive capabilities from another class because of a superclass-subclass
relationship. The relationship between the classes is sometimes described with the
term “is-a.” See also extend.
inheritance hierarchy—The relationship of several classes that inherit from a common
superclass. See also inherit.
initial value—The first value a variable is assigned.
initial situation—A description of the desired state of a city and all that it contains,
including robots, when a program begins. See also final situation.
inner class—A class definition that is contained within the definition of another class,
allowing it to access the outer classes’ private methods and instance variables.
input—Information that is obtained from outside the program—for example, from the
person running the program.
input stream—A stream that carries information from a source to a program. See also
output stream, source, stream.
insertion point—The point on the console or in a user interface component where the
next character typed by the user will appear.
instance—Each object is one instance of a class.
instance variable—A variable that is specific to an object. See also class variable, parameter
variable, temporary variable.
instantiate—The act of constructing an instance of a class—that is, creating an object.
integer division—Division of an integer by another integer where any remainder or
fractional part in the answer is discarded.
intent error—An error in which the program does not produce the desired results,
even though it compiles correctly and does not generate run-time errors. See also
compile-time error, run-time error.
interaction—An informal term referring to a method calling another method or using
an instance variable. See also detail.
interface—A Java construct listing a set of methods. It is used to define a new type and
also to specify that a class belongs to that type because it implements all of the
methods listed by the interface.
invoke—To cause an object to perform a specific service.
I/O—An abbreviation for input and output.
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 778
778
APPENDIX A | GLOSSARY
779
KEY TERMS
memory—Part of the computer hardware that stores information, such as variables
and program instructions.
message—A client object sends a message to a server object to invoke one of its services.
method—The source code that implements a specific service.
method resolution—The process of determining the correct method to execute in
response to a method call.
mixin—A type, defined by an interface, that supplements the primary type of a class.
model—1. A simplified description of a problem, usually in a formal notation such as
mathematics or a computer program, that enables people to forecast the future,
make decisions, or otherwise solve the problem. 2. The part of the Model-View-
Controller pattern that models or abstracts a problem. 3. To create a simplified
description of something to help us make decisions, predict future events, or main-
tain relevant information.
multi-line comment—A comment that may span multiple lines. It begins with /* and
ends with */. See also comment, documentation comment.
multiplicity—The notation on arrows in a class diagram indicating how many
instances of a class are used by an object.
mutable—A mutable object can be changed after it has been created. See also
immutable.
natural language—Language used in everyday speech.
negate—To make a Boolean expression return the opposite value.
nest—To place a control statement such as if or while within another control statement.
nested loop—A loop that occurs within another loop.
newline character—A character that divides two lines of text. It can be represented in a
string with the character sequence ‘\n’.
null—A special value that can be assigned to any object reference, meaning it does not
refer to any object.
object diagram—A diagram that shows one or more specific objects and the values of
their attributes.
object equality—Tested with the equals method. Establishes whether two object ref-
erences refer to objects that are equivalent. See also object identity.
object identity—Tested with ==. Establishes whether two object references refer to the
same object. See also object equality.
object-oriented programming language—A computer programming language incorpo-
rating the ideas of encapsulation, inheritance, and polymorphism.
open—Preparing a file for input or output.
open for extension—A class that is written in such a way that it can be modified
through inheritance. See also extend, inherit.
operand—The value, variable, or query on which an operation is to be done. See also
operator.
operator—A symbol denoting an operation, such as addition or division, to be performed
on its operands. See also operand.
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 780
780
APPENDIX A | GLOSSARY
or—A logical connector written in Java as ||. The result is true if and only if at least
one of the operands is true.
origin—The place from which measurement begins. In a robot city, the intersection of
street 0 and avenue 0. On a computer screen, the upper-left corner.
output—Information that is produced by a program and displayed on a screen or written
to a file.
output stream—A stream that carries information from a program to a sink or destination.
See also input stream, sink, stream.
overload—Two or more methods with the same name but different signatures are over-
loaded. The Java system chooses which one to execute based on the actual parameters
used when the method is called. See also signature.
override—Replacing a method in the class being extended with a new version of the
method.
package—A group of classes, usually organized around a common purpose.
parameter variable—A type of variable used to communicate a value to a constructor
or service to use in accomplishing its purpose. See also class variable, instance vari-
able, temporary variable.
partially filled array—An array that uses the elements with indices 0..n-1 to store val-
ues, where n ≤ s, the size of the array. n is stored in an auxiliary variable.
picture element—A small dot displayed on a computer screen. Many picture elements
compose the image displayed. Often abbreviated as “pixel.”
pixel—See picture element.
point—A unit of measurement, used for fonts, equal to 1/72 of an inch.
polymorphism—Setting up two or more classes so that objects can be sent the same
message but respond to the message differently—that is, in ways appropriate to the
kind of object receiving the message.
postcondition—A statement of what should be true after a method executes. See also
precondition.
precedence—A rule that determines which operations are done first when an expres-
sion is evaluated.
precision—The closeness of the approximation between a value stored in the computer
and the actual value.
precondition—A situation that must be true when a method is called to ensure that it
executes correctly. See also postcondition.
predicate—1. A query (method) that returns a value of either true or false. 2. The
part of a sentence that contains a verb and explains the action or the condition of
the subject. See also subject.
primary key—When sorting records, the primary key is the most important determi-
nant of the order. See also secondary key.
primitive—The most basic available methods out of which more complex methods
are built.
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 781
781
KEY TERMS
primitive type—A type whose values can be manipulated directly by the underlying hard-
ware. In Java, they include int, double, and boolean. See also reference type.
processing stream—A stream that processes information as it flows from a source to a
sink. See also provider stream, stream.
program—A detailed set of computer instructions designed to solve a problem.
prompt—An indication to the user that some action is required. A prompt is usually
printed on the screen just before input is required from the user.
prototype—A preliminary version of a program used for evaluation or learning pur-
poses. See also high-fidelity prototype, low-fidelity prototype.
provider stream—A stream that provides information from a source or to a sink. See also
processing stream, sink, source, stream.
pseudocode—A blend of a natural language and a programming language, allowing
people to think more rigorously about programs without worrying about program-
ming language details.
query—A service or method that answers a question. See also command, method, service.
random access—A property of an information collection where every item can be
accessed as easily and as fast as every other item.
range—The number of different values belonging to a type such as int or double.
read—Obtaining input from a file or other input stream.
record—A collection of information pertaining to one thing (for example, an
employee) in a file that typically contains information about many of those things.
See also field.
refactor—The process of modifying a program to improve its overall quality without
changing its functionality.
reference—The information stored in a variable that refers or leads to a specific object.
reference type—A type whose values are defined by a class or an interface. See also
primitive type.
reference variable—A variable that refers to an object or contains null.
register—Adding an object to a list of objects that should be notified when certain
events occur.
relative path—A sequence of directories that gives a file location relative to the current
working directory. See also absolute path, working directory.
reliability—A characteristic of quality programs in which the program does not crash,
lose, or corrupt data, and is consistent in how it operates.
remainder operator—An operator that returns the remainder or part that is left after
dividing one integer by another. See also integer division.
requirements—A written statement of what a program is supposed to do. Also called
specifications.
reserved word—See keyword.
responsibility—The things a class must do to support the operation of the program.
Identified during the design of the program.
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 782
782
APPENDIX A | GLOSSARY
return—The action of going back to the statement that called the currently executing
method. If the method is a query, it also provides a value to the expression from
which it was called.
return type—The type of the value returned by a query. Specified just before the
method’s name when it is declared.
right justified—Elements (typically lines of text) that are aligned vertically on the right
side. See also left-justified.
road—A street or an avenue on which a robot may move between intersections. See
also avenue, street.
row-major order—A 2D array algorithm that accesses the array such that the row
changes more slowly than the column. See also column-major order.
run-time error—An error detected when a program executes or runs because it has exe-
cuted an instruction in an illegal context. See also compile-time error, intent error.
scenario—A specific task that a user may want to perform with the program. Also
known as use case.
scientific notation—A number expressed as the multiplication of a fractional number
(the mantissa) and 10 raised to some power (the exponent). See also exponent,
mantissa.
scope—That part of a program where an identifier is available for use.
search—The process of attempting to locate one value in a collection of values.
secondary key—When sorting records, the secondary key is used to determine the
order of records that have equal primary keys. See also primary key.
self-documenting code—Code that is written to minimize the need for documentation.
Well-chosen identifiers are the key tool used in writing self-documenting code.
semantics—The meaning of a statement. See also syntax.
sequence diagram—A diagram showing the sequence of activities among cooperating
objects.
serif—Short lines at the end of each stroke of a printed letter.
server—An object that provides services to a client object. See also client.
service—An action that an object performs in response to a message. Services are sub-
divided into queries and commands. See also command, message, method, query.
set—An unordered collection of unique objects. See also list, map.
shallow copy—A copy of an object that does not copy any objects it references. See
also deep copy.
short-circuit evaluation—Evaluating a Boolean expression so that sub-expressions that
cannot affect the result are not evaluated.
side effect—A change in state caused by executing a method.
signature—The name of a method, together with an ordered list of all the types of its
parameters.
simulate—See trace.
single-line comment—A comment extending from a double slash (//) until the end of
the line. See also multiline comment, documentation comment.
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 783
783
KEY TERMS
sink—The destination for information flowing in a stream. See also source, stream.
software object—An abstraction in an object-oriented program used to model a real-
world entity.
source—The origin of information that flows in a stream. See also sink, stream.
source code—The words and symbols written by programmers to instruct a computer
what to do.
spaghetti code—A derisive description of source code written with undisciplined use of
a goto construct (which Java does not have). See also structured programming.
special symbols—Symbols that have a special meaning in the Java language, including
braces, parentheses, and the period and semicolon characters.
specification—See requirements.
stack trace—An ordered list of which methods called which methods, extending from
the point an exception is thrown back to the main method.
state—The state of being of an object as defined by the contents of its attributes.
state change diagram—A diagram that shows how an object’s state changes over time.
statement—An individual instruction in a programming language.
static variable—See class variable.
stepwise refinement—A method of writing programs where each method is defined in
terms of helper methods, each of which implement one logical step in solving the
problem. Also known as top-down design.
Strategy pattern—A pattern where one object uses another object that defines one
algorithm from a family of algorithms, making it easy to change the behavior of the
first object.
stream—An ordered collection of information that moves from a source to a destina-
tion or sink. See also byte stream, character stream, input stream, output stream,
processing stream, provider stream.
street—A road on which robots may travel east or west. See also avenue, road.
structured programming—A programming discipline that restricts how flow of control
can be shifted from one part of the program to another. See also spaghetti code.
stub—A method that has just enough code to compile, but not enough to actually do
its job.
subclass—A class that receives part of its functionality from a superclass. See also
extend, inherit, superclass.
subject—The part of a sentence that says who or what did the action. See also predicate.
substitution principle—A key principle underlying polymorphism where an object of
one type, A, can substitute for an object of another type, B, if A can be used any-
place that B can be used. See also polymorphism.
superclass—A class that has been extended to create a subclass. See also extend,
inherit, subclass.
Swing—A newer addition to the collection of classes available to write graphical user
interfaces in Java. See also Abstract Window Toolkit.
syntax—The form of a statement. See also semantics.
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 784
784
APPENDIX A | GLOSSARY
tab stop—A predefined location where the insertion point will be located after a tab is
inserted into text.
tag—A keyword such as @param or @author used to identify standardized informa-
tion in documentation comments. See also documentation comment.
template method—A method implementing the common part of a problem that has
several variations. The differences between the variations are expressed in helper
methods contained in subclasses.
temporary variable—A variable defined within a method. The variable and the infor-
mation it contains are discarded when the method finishes execution. See also class
variable, instance variable, parameter variable.
test harness—A program used to test a method or class.
then clause—The statements that are executed when the test in an if statement is true.
thread—A sequence of statements that executes independently of other sequences of
statements. The execution of two or more threads may be interleaved. See also flow
of control.
throw—The action of interrupting the normal execution of a program with an exception.
token—A group of characters separated by delimiters. See also delimiter.
top factor—The process of removing redundant statements from the beginning of both
clauses in an if statement.
top-down design—Designing a program or a method by dividing it into logical pieces
that work together. These pieces are themselves designed using top-down design.
This process continues until a piece is so simple that it can be solved without divid-
ing it. See also stepwise refinement, top-down implementation.
top-down implementation—Implementing a method by writing it in terms of helper
methods. Helper methods may also be defined in terms of other helper methods.
Eventually, each helper method will be simple enough to implement using existing
methods or without using helper methods. See also bottom-up implementation,
top-down design.
trace—To execute a program without the aid of a computer, usually by recording state
changes in a table. Also called simulate.
type—A designation of the valid values for a variable or parameter.
unchecked exception—An exception that the compiler does not require to be caught
with a try-catch statement or declared to be thrown with a throws clause. Used
for errors from which recovery should generally not be attempted. See also checked
exception, exception.
Unicode—A character encoding standard that allows up to 65,536 different characters
to be defined.
usability—A criteria of a program’s quality from a user’s perspective, determined by
the effort required to learn, operate, prepare input, and interpret output when
compared to the alternatives.
use case—See scenario.
validation—Determining if the intent of a program or program fragment is correct.
See also verification.
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 785
785
KEY TERMS
value—One item of information stored in a map collection that is identified by a key.
See also map.
variable—A named place where a program can store information. See also instance
variable, parameter variable, temporary variable.
variable declaration—A programming language statement that introduces a variable in
the source code and specifies its type.
verification—Determining if a program or program fragment correctly implements the
intended functionality. See also validation.
view—The part of the Model-View-Controller pattern that displays relevant informa-
tion from the model to the user. See also controller, model.
walk-through—The process of simulating the execution of a program using other peo-
ple, each of which takes on the role of one or more classes.
wall—An element of a robot’s environment that it cannot move through.
waterfall model—A development process in which the output of one phase is the input
to another phase. The waterfall model does not explicitly include iteration. See also
development process.
whitespace—Characters such as spaces and tabs that appear as white space when
printed on paper.
working directory—An executing program’s default directory. Files are read and writ-
ten in the working directory unless their name includes an absolute or relative path.
wrapper class—A class whose only variable is a primitive, such as int or double.
Such classes exist so that a primitive value can be treated as an object.
write—The process of placing information in a file. See also read.
A Appendix C5743 40143.ps 11/30/06 1:32 PM Page 786
Appendix B Precedence Rules
Precedence rules establish the order of operations when an expression is evaluated. For
example, in 3 + 4 * 4, is the answer 19 or 28? It depends on whether you multiply or
add first. Normal precedence rules dictate that multiplication is done before addition.
Precedence can be overridden using parentheses. For example, (3 + 4) * 4 means that
the addition should be performed before the multiplication, yielding 28.
When two operators with the same precedence appear together, the operators are per-
formed left to right. That is, 3 * 4 / 6 is the same as (3 * 4) / 6. The only exception is
assignment. It is valid to write aƒ=ƒbƒ=ƒc, which means assign c to b and then assign
b to a. This style is not used in this book.
Some of these operators are beyond the scope of an introductory text and are marked
with an asterisk (*) on the right.
Unary operators
787
APPENDIX B | PRECEDENCE RULES 788
Cast («type»)«expr»ƒ
Multiplicative operators
Multiplication «expr»ƒ*ƒ«expr»ƒ
Division «expr»ƒ/ƒ«expr»ƒ
Remainder «expr»ƒ%ƒ«expr»ƒ
Additive operators
Addition «expr»ƒ+ƒ«expr»ƒ
Subtraction «expr»ƒ-ƒ«expr»ƒ
Relational operators
Equality operators
Equals «expr»ƒ==ƒ«expr»ƒ
PRECEDENCE
(table B-1) continued Operator Syntax
OF JAVA
operators
Conditional «expr»ƒ?ƒ«expr»ƒ:ƒ«expr» *
OPERATORS
Assignment ƒ
«var»ƒ=ƒ«expr»ƒ
«var»ƒ+=ƒ«expr»ƒ
«var»ƒ-=ƒ«expr»ƒ
«var»ƒ*=ƒ«expr»ƒ
«var»ƒ/=ƒ«expr»ƒ
«var»ƒ%=ƒ«expr»ƒ
«var»ƒ>>=ƒ«expr» *
«var»ƒ<<=ƒ«expr» *
«var»ƒ>>>=ƒ«expr» *
«var»ƒ&=ƒ«expr» *
«var»ƒ^=ƒ«expr» *
«var»ƒ|=ƒ«expr» *
C Appendix C5743 40143.ps 11/30/06 1:32 PM Page 791
Implicit variable
initialization values byte, short, int, long 0
boolean false
char '\0000'
float +0.0f
double +0.0
791
C Appendix C5743 40143.ps 11/30/06 1:32 PM Page 792
792
APPENDIX C | VARIABLE INITIALIZATION RULES
Temporary Variables
Temporary variables are not given an initial value by the compiler. The compiler
attempts to verify that each temporary variable is initialized before it is used. If the
compiler is unable to verify this property, it will issue a compile-time error.
Parameter Variables
Arrays
Each element of a newly created array is given a default value. The default value
depends on the array’s type, as shown in Table C-1.
Appendix D Unicode Character Set
Fundamentally, computers simply store and manipulate numbers. Text can be processed
because each character is assigned a number. The computer manipulates numbers but
prints them as characters.
Understanding Encoding
Many children have played some sort of spy game that involved encoding secret mes-
sages. The encoding is usually something like this:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
J H Q S I A R Z X B D N C O W M T P F U Y G K L V E
The message “GO TO THE HIDEOUT” is encoded by looking up “G” in the top row
and writing down “R”, the letter beneath it; then looking up “O” and writing down
“W”; and so on. The entire encoded message would be “RW UW UZI ZXSIWYU”.
Someone receiving the coded message could perform the reverse operation to recover
the original message.
The computer uses a similar encoding, except it matches letters with numbers:
A B C D E F G H I J K L M N O P …
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 …
When we type “GO TO THE HIDEOUT” into a program, the computer encodes it as
71 79 32 84 79 32 84 72 69 32 72 73 68 69 79 85 84. The spaces in the original mes-
sage are encoded as 32. When it is time to print a message using, for example,
System.out.println, the computer looks up the number 71 to discover it should
display dots in the shape of “G”.
793
APPENDIX D | UNICODE CHARACTER SET 794
Encoding Characters
A simple program that reads a line of text and displays the corresponding numeric
encoding is shown in Listing D-1.
Listing D-1: A program to translate a line of text into the equivalent numeric codes
appendices/
1 importƒjava.util.Scanner; charCodes/
2
3 /** Translate characters into their integer equivalents.
4 *
5 * @author Byron Weber Becker */
6 publicƒclassƒCharacterCodesƒextendsƒObject
7 {
8 ƒƒpublicƒstaticƒvoidƒmain(String[]ƒargs)ƒ
9 ƒƒ{
10 ƒƒƒƒSystem.out.println("Type a line of text to show the Unicode encoding.");
11 ƒƒƒƒSystem.out.println("Type \"quit\" to end.");
12
13 ƒƒƒƒScannerƒinƒ=ƒnewƒScanner(System.in);
14 ƒƒƒƒwhileƒ(true)
15 ƒƒƒƒ{ƒSystem.out.print("> ");
16 ƒƒƒƒƒƒStringƒinputƒ=ƒin.nextLine();
17
18 ƒƒƒƒƒƒifƒ(input.equals("quit"))
19 ƒƒƒƒƒƒ{ƒbreak;
20 ƒƒƒƒƒƒ}
21
22 ƒƒƒƒƒƒforƒ(intƒiƒ=ƒ0;ƒiƒ<ƒinput.length();ƒi++)
23 ƒƒƒƒƒƒ{ƒcharƒasCharƒ=ƒinput.charAt(i);
24 ƒƒƒƒƒƒƒƒintƒasIntƒ=ƒinput.charAt(i);
25 ƒƒƒƒƒƒƒƒSystem.out.println(""ƒ+ƒasCharƒ+ƒ" ("ƒ+ƒasIntƒ+ƒ")");
26 ƒƒƒƒƒƒ}
27 ƒƒƒƒ}
28
29 ƒƒ}
30 }
The most common encodings correspond to the ASCII character set, one of the earliest
standards. They represent the character encodings from the number 0 up to 127 and are
shown in Table D-1.
795
ENCODING CHARACTERS
(table D-1) decimal char decimal char decimal char decimal char
ASCII character set 0 NUL 32 Space 64 @ 96 `
1 SOH 33 ! 65 A 97 a
2 STX 34 " 66 B 98 b
3 ETX 35 # 67 C 99 c
4 EOT 36 $ 68 D 100 d
5 ENQ 37 % 69 E 101 e
8 BS 40 ( 72 H 104 h
9 TAB 41 ) 73 I 105 i
10 LF 42 * 74 J 106 j
11 VT 43 + 75 K 107 k
12 FF 44 , 76 L 108 l
13 CR 45 - 77 M 109 m
14 SO 46 . 78 N 110 n
15 SI 47 / 79 O 111 o
16 DLE 48 0 80 P 112 p
17 DC1 49 1 81 Q 113 q
18 DC2 50 2 82 R 114 r
19 DC3 51 3 83 S 115 s
20 DC4 52 4 84 T 116 t
21 NAK 53 5 85 U 117 u
22 SYN 54 6 86 V 118 v
23 ETB 55 7 87 W 119 w
24 CAN 56 8 88 X 120 x
25 EM 57 9 89 Y 121 y
26 SUB 58 : 90 Z 122 z
27 ESC 59 ; 91 [ 123 {
28 FS 60 < 92 \ 124 |
29 GS 61 = 93 ] 125 }
30 RS 62 > 94 ^ 126 ~
31 US 63 ? 95 _ 127 DEL
APPENDIX D | UNICODE CHARACTER SET 796
The first column of the table contains control characters. One of the main uses for
these characters is to control some types of printers. If the character CR (carriage
return) was sent to the printer, the print head would return to the beginning of the line.
The LF character (line feed) moves the paper up one line.
Some of the control characters are still used and have escape sequences so they can be
easily inserted into a string. These are shown in Table D-2.
The last three exist so that we can insert the backslash, single quote, and double quote
into strings. For example, if you really did want to print a backslash followed by the
character n, you couldn’t simply write:
System.out.println(“\n”);
because that would print a newline character. Instead, you would need to write
System.out.println(“\\n”);
City
publicƒclassƒCityƒextendsƒjava.lang.Object
Constructor Summary
City()
Construct a new City using the defaults stored in the becker.robots.ini file.
City(intƒnumVisibleStreets,ƒintƒnumVisibleAvenues)
City(intƒfirstVisibleStreet,ƒintƒfirstVisibleAvenue,
intƒnumVisibleStreets,ƒintƒnumVisibleAvenues)
City(StringƒfileName)
City(java.util.Scannerƒin)
797
APPENDIX E | SELECTED ROBOT DOCUMENTATION 798
Method Summary
protectedƒvoidƒcustomizeIntersection(Intersectionƒintersection)
IIterate<Light>ƒexamineLights()
IIterate<Robot>ƒexamineRobots()
IIterate<Thing>ƒexamineThings()
IIterate<Thing>ƒexamineThings(IPredicateƒaPredicate)
Examine all the Thing objects in this City that match aPredicate, one at a time.
protectedƒIntersectionƒgetIntersection(intƒavenue,ƒintƒstreet)
booleanƒisShowingThingCounts()
protectedƒvoidƒkeyTyped(charƒkey)
This method is called when this City’s display has the focus and a key is typed.
protectedƒIntersectionƒmakeIntersection(intƒavenue,ƒintƒstreet)
Make an Intersection that will appear at the specified avenue and street.
voidƒsave(Stringƒindent,ƒjava.io.PrintWriterƒout)
voidƒsetFrameTitle(Stringƒtitle)
voidƒsetSize(intƒwidth,ƒintƒheight)
Set the predicate for what kinds of Things to count when showing the number of
Things on each Intersection.
799
FLASHER
voidƒshowFrame(booleanƒshow)
voidƒshowThingCounts(booleanƒshow)
Show the number of Things on each Intersection, counted according to the pred-
icate set with the method setThingCountPredicate.
Direction
publicƒenumƒDirection
Field Summary
publicƒstaticƒintƒEAST
publicƒstaticƒintƒNORTH
publicƒstaticƒintƒWEST
publicƒstaticƒintƒSOUTH
Method Summary
Directionƒleft()
Directionƒopposite()
Flasher
publicƒclassƒFlasherƒextendsƒLight
Constructor Summary
Flasher(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue)
Flasher(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue,ƒbooleanƒisOn)
Flasher(RobotƒheldBy)
Method Summary
protectedƒvoidƒsave(Stringƒindent,ƒjava.io.PrintWriterƒout)
voidƒturnOff()
voidƒturnOn()
Intersection
publicƒclassƒIntersectionƒextendsƒSimƒimplementsƒILabel
Karel the Robot lives in a city composed of intersections connected by roads. Roads
that run north and south (up and down) are called “Avenues” and roads that run east
and west are called “Streets.”
Constructor Summary
Intersection(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue)
INTERSECTION
Method Summary
protectedƒvoidƒaddSim(SimƒtheThing)
intƒcountThings()
intƒcountThings(IPredicateƒpred)
Determine the number of Things currently on this Intersection that match the
given predicate.
protectedƒbooleanƒentryIsBlocked(Directionƒdir)
IIterate<Light>ƒexamineLights(IPredicateƒaPredicate)
Examine all the Light objects on this Intersection that match the given predicate,
one at a time.
IIterate<Robot>ƒexamineRobots(IPredicateƒaPredicate)
Examine all the Robot objects on this Intersection that match the given predicate,
one at a time.
IIterate<Thing>ƒexamineThings(IPredicateƒaPredicate)
Examine all the Thing objects on this Intersection that match the given predicate,
one at a time.
IIterate<Thing>ƒexamineThings()
protectedƒbooleanƒexitIsBlocked(Directionƒdir)
Determine whether something on this Intersection blocks Robots from exiting the
Intersection.
intƒgetAvenue()
protectedƒIntersectionƒgetIntersection()
StringƒgetLabel()
IntersectionƒgetNeighbor(Directionƒdir)
intƒgetStreet()
protectedƒvoidƒremoveSim(Simƒs)
Remove the given Sim (Robot, Flasher, Streetlight, Wall, and so on) from this
Intersection.
protectedƒvoidƒsave(Stringƒindent,ƒjava.io.PrintWriterƒout)
voidƒsetLabel(StringƒaLabel)
StringƒtoString()
IPredicate
publicƒinterfaceƒIPredicate
A predicate says whether something is true or false about a Sim. A class implementing
the IPredicate interface does this via the isOK method, which returns true if some
condition about a Sim is true, and false otherwise.
A typical use for a predicate is to find a certain kind of Thing for a Robot to examine—
for example, a Light. To do this, define a class implementing IPredicate as follows:
ƒƒpublicƒclassƒALightPredƒimplementsƒPredicate
ƒƒ{ƒƒ//return true if the Sim passed is a Light, false otherwise
ƒƒƒƒƒpublicƒbooleanƒisOK(Simƒs)
ƒƒƒƒƒ{ƒƒreturnƒsƒinstanceofƒLight;
ƒƒƒƒƒ}
ƒƒ}
ƒƒLightƒlightƒ=ƒthis.examineThing(newƒALightPred()).next();
803
IPREDICATE
which will return a Light from the current Intersection, if there is one, and throw
an exception if there is not. The isBesideThing method in the Robot class can be
used to determine if the specified kind of Thing is available.
The IPredicate class also defines a number of useful predicates as constants. For
example, to pick up a Thing that is a Flasher, one could write
karel.pickThing(IPredicate.aFlasher);
Field Summary
staticƒIPredicateƒaFlasher
staticƒIPredicateƒanyFlasher
staticƒIPredicateƒanyRobot
staticƒIPredicateƒanyStreetlight
staticƒIPredicateƒanyThing
staticƒPredicateƒanyWall
staticƒPredicateƒaRobot
staticƒPredicateƒaStreetlight
staticƒPredicateƒaThing
staticƒPredicateƒaWall
staticƒPredicateƒcanBeCarried
A predicate to test whether the Thing is something that a Robot can carry.
Method Summary
booleanƒisOK(SimƒtheSim)
Light
publicƒabstractƒclassƒLightƒextendsƒThing
A Light is a kind of Thing that can be turned on to make it brighter and turned off
to make it darker. Some Lights can be moved (Flasher) while others can’t
(Streetlight).
The Light class itself is abstract, meaning programmers cannot construct an instance
of Light. It must be extended to create a class that can be instantiated. This class does
define a common interface for all Lights so that any Light may be turned on or off
without knowing what specific kind of Light it is (polymorphism).
Constructor Summary
Light(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue)
ROBOT
Method Summary
booleanƒisOn()ƒ
voidƒturnOff()ƒ
voidƒturnOn()ƒ
Robot
publicƒclassƒRobotƒextendsƒSimƒimplementsƒILabel,ƒIColor
Robots exist on a rectangular grid of roads and can move, turn left ninety degrees,
pick things up, carry things, and put things down. A Robot knows which avenue and
street it is on and which direction it is facing. Its speed can be set and queried.
Constructor Summary
Robot(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue,ƒDirectionƒaDir)
Construct a new Robot at the given location in the given City with nothing in its
backpack.
Robot(Cityƒc,ƒintƒstr,ƒintƒave,ƒDirectionƒaDir,ƒintƒnumThings)
Construct a new Robot at the given location in the given City with the given number
of Things in its backpack. Override makeThing to customize the kind of Thing
added to the backpack.
APPENDIX E | SELECTED ROBOT DOCUMENTATION 806
Method Summary
protectedƒvoidƒbreakRobot(Stringƒmsg)
This method is called when this Robot does something illegal such as trying to move
through a Wall or picking up a nonexistent object. An exception is thrown that stops
this Robot’s operation.
booleanƒcanPickThing()
Determine whether this Robot is on the same Intersection as a Thing it can pick up.
intƒcountThingsInBackpack()
intƒcountThingsInBackpack(IPredicateƒkindOfThing)
Examine all the Light objects that are on the same Intersection as this Robot, one
at a time.
IIterate<Robot>ƒexamineRobots()
Examine all the Robot objects that are on the same Intersection as this Robot, one
at a time.
IIterate<Thing>ƒexamineThings(IPredicateƒaPredicate)
Examine all the Thing objects that are on the same Intersection as this Robot and
match the given predicate, one at a time.
booleanƒfrontIsClear()
intƒgetAvenue()
DirectionƒgetDirection()
StringƒgetLabel()
ROBOT
doubleƒgetSpeed()
How many moves and/or turns does this Robot complete in one second?
intƒgetStreet()
doubleƒgetTransparency()
booleanƒisBesideThing(IPredicateƒaPredicate)
Determine whether this Robot is on the same Intersection as one or more instances
of the specified kind of Thing.
protectedƒThingƒmakeThing(intƒnOf,ƒintƒtotal)
Make a new Thing to place in this Robot’s backpack. Override this method in a sub-
class to control what kind of Thing is made when a Robot is constructed with Things
in its backpack.
voidƒmove()
Move this Robot from the Intersection it currently occupies to the next
Intersection in the Direction it is currently facing, leaving it facing the same
Direction.
voidƒpickThing()
Attempt to pick up a particular kind of Thing from the Intersection this Robot
currently occupies.
voidƒpickThing(ThingƒtheThing)
Attempt to pick up a particular Thing from the Intersection this Robot currently
occupies.
voidƒputThing()
Take something out of this Robot’s backpack and put it down on the Intersection
this Robot currently occupies.
voidƒputThing(IPredicateƒkindOfThing)
Attempt to take a particular kind of Thing out of this Robot’s backpack and put it
down on the Intersection the Robot currently occupies.
APPENDIX E | SELECTED ROBOT DOCUMENTATION 808
voidƒputThing(ThingƒtheThing)
Attempt to put down a particular Thing on the Intersection this Robot currently
occupies.
protectedƒvoidƒsave(Stringƒindent,ƒjava.io.PrintWriterƒout)
voidƒsetLabel(StringƒtheLabel)
voidƒsetSpeed(doubleƒmovesPerSecond)
voidƒsetTransparency(doubleƒtrans)
voidƒturnLeft()
RobotRC
publicƒclassƒRobotRCƒextendsƒRobot
A remote control robot, RobotRC for short, can be directed from a computer key-
board. The City’s view must have the keyboard focus when the program is running
for the Robot to receive the instructions from the keyboard. When the City’s view has
the focus, it will have a thin black outline. Shift the focus between the speed control
and the start/stop button on the City’s view with the tab key.
Constructor Summary
RobotRC(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue,ƒDirectionƒaDir)
ROBOTSE
Method Summary
protectedƒvoidƒkeyTyped(charƒkey)
This method makes the robot respond to the user’s key presses as shown in Table E-1.
It may be overridden to make the robot respond differently.
r, R turn right
l, L turn left
u, U pick up a Thing
RobotSE
publicƒclassƒRobotSEƒextendsƒRobot
A new kind of Robot with extended capabilities, such as turnAround and turnRight.
Constructor Summary
RobotSE(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue,ƒDirectionƒaDir)
Method Summary
booleanƒisFacingEast()
booleanƒisFacingSouth()
booleanƒisFacingWest()
voidƒmove(intƒhowFar)
voidƒpickAllThings()
Pick up all the Things that can be carried from the current Intersection.
voidƒpickAllThings(PredicateƒkindOfThing)
Pick up all of the specified kind of Things from the current Intersection.
voidƒputAllThings()
Put down all the Things in this Robot’s backpack on the current Intersection.
voidƒputAllThings(PredicateƒkindOfThing)
Put down all of the specified kind of Things from the Robot’s backpack on the current
Intersection.
voidƒturnAround()
voidƒturnLeft(intƒnumTimes)
voidƒturnRight()
voidƒturnRight(intƒnumTimes)
Sim
publicƒabstractƒclassƒSimƒextendsƒjava.lang.Object
Since this class is abstract it cannot be instantiated; only subclasses may be instantiated.
This class exists both to ensure that basic services required for the simulation are
present and to provide common implementations for required several services.
811
STREETLIGHT
Constructor Summary
Sim(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue,ƒDirectionƒorientation,ƒ
ƒƒƒIconƒtheIcon)
Method Summary
IconƒgetIcon()ƒ
Return the icon used to display the visible characteristics of this Sim, based on the
Sim’s current state.
protectedƒabstractƒIntersectionƒgetIntersection()
protectedƒvoidƒkeyTyped(charƒkey)
This method is called when a key is typed and keyboard input is directed to karel’s
world (the map, as opposed to a different window or the controls for karel’s world).
protectedƒvoidƒnotifyObservers()
Notify any observers of this Sim (for instance, the user interface) that it has changed.
protectedƒvoidƒnotifyObservers(java.lang.ObjectƒchangeInfo)ƒ
Notify any observers of this Sim (for instance, the user interface) that it has changed.
voidƒsetIcon(IconƒtheIcon)
Streetlight
publicƒclassƒStreetlightƒextendsƒLight
A Streetlight is a kind of Light that lights an intersection. Like all Lights, it can
be turned on and off. A Streetlight cannot be moved by a Robot.
Constructor Summary
Streetlight(Cityƒcity,ƒintƒaStreet,ƒintƒanAvenue,ƒDirectionƒcorner)
Streetlight(Cityƒcity,ƒintƒaStreet,ƒintƒanAvenue,ƒDirectionƒcorner,
ƒƒƒbooleanƒisOn)
Method Summary
protectedƒvoidƒsave(Stringƒindent,ƒjava.io.PrintWriterƒout)
voidƒturnOff()
voidƒturnOn()
Thing
publicƒclassƒThingƒextendsƒSim
A Thing is something that can exist on an Intersection. All Things have a loca-
tion (avenue and street). Some Things can be picked up and moved by a Robot
(Flashers) while others cannot (Streetlights, Walls).
In addition to a location, all Things have an orientation, although it is common for the
orientation to always have a default value. Examples where that is not the case include
a Wall where the orientation determines which exit or entry into an Intersection is
blocked, and a Streetlight where the orientation determines which corner of the
Intersection it occupies.
Constructor Summary
Thing(Cityƒcity,ƒintƒaStreet,ƒintƒanAvenue)
Thing(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue,ƒDirectionƒorientation)
Construct a new Thing with a default appearance that can be carried, in the given
orientation.
813
THING
Thing(CityƒaCity,ƒintƒaStreet,ƒintƒanAvenue,ƒDirectionƒorientation,
ƒbooleanƒcanBeMoved,ƒIconƒanIcon)
Thing(RobotƒheldBy)
Method Summary
booleanƒblocksIntersectionEntry(DirectionƒentryDir)
Does this Thing block the entry of this Intersection from the given Direction?
booleanƒblocksIntersectionExit(DirectionƒexitDir)
Does this Thing block the exit of this Intersection in the given Direction?
booleanƒcanBeCarried()
Can this Thing be picked up, carried, and put down by a Robot?
protectedƒIntersectionƒgetIntersection()
protectedƒvoidƒsave(Stringƒindent,ƒjava.io.PrintWriterƒout)
voidƒsetBlocksEntry(booleanƒnorth,ƒbooleanƒsouth,ƒbooleanƒeast,
ƒƒƒbooleanƒwest)
Set whether this Thing blocks a Robot’s entry from the given Directions.
voidƒsetBlocksEntry(DirectionƒaDir,ƒbooleanƒblock)
Set whether this Thing blocks a Robot’s entry from the given Direction.
voidƒsetBlocksExit(booleanƒnorth,ƒbooleanƒsouth,ƒbooleanƒeast,
ƒƒƒbooleanƒwest)
Set whether this Thing blocks a Robot’s exit from the given Directions.
voidƒsetBlocksExit(DirectionƒaDir,ƒbooleanƒblock)
Set whether this Thing blocks a Robot’s exit from the given Direction.
voidƒsetCanBeCarried(booleanƒcanCarry)
Wall
publicƒclassƒWallƒextendsƒThing
A Wall will block the movement of a Robot into or out of the Intersection that con-
tains it, depending on the Robot’s direction of travel and the orientation of the Wall.
Constructor Summary
Wall(Cityƒcity,ƒintƒaStreet,ƒintƒanAvenue,ƒDirectionƒorientation)
Method Summary
protectedƒvoidƒsave(Stringƒindent,ƒjava.io.PrintWriterƒout)ƒ
Index
816
INDEX
817
INDEX
closed for modification, 67 shallow copies versus deep copies, 667–669
collaborating. See collaborating classes using, 666
concrete, 640 closed for modification, 67
developing to specified interface, 378–379 closing files, 461, 464
extending. See extending classes code
identifiers, 81 byte, 29
identifying, 412–413, 589–590, 648–651 commenting out, 83
immutable, 612–614 duplication, putting in helper methods,
implementing accessor methods, 359–361 608–609
implementing command/query pairs, 361–366 packages, 26
inner, 735–737 pseudocode, 138–139
modifying versus extending, 305–306 quality, writing. See writing quality code
multiple, using, 394–398 sample, 744
mutable, 612 self-documenting, 188
names, 80 cohesion, complexity of programs, 615, 618
null values, 398 collaborating classes
objects versus, 7 diagraming, 399–400
open for extension, 67 GUIs, 447–449
passing arguments, 401 collaborators, 591
reimplementing, 396–397 collections, 431–447. See also lists; maps; sets
relationships, 413 foreach loops, 437
returning object references, 401–402 color chooser, 71
setting up relationships between, 493–494 column(s), formatting numbers, 342–343
single, using, 392–494 column-major order, 563
temporary variables, 401 combining Boolean expressions, 231–236
wrapper, 446–447 error common in, 236
writing, 358–366 operator precedence, 234–235
class diagrams, 7 operators, 231
developing, 595 command(s), 4
lists, 438–439 correctness, 86
Robot class, 12–15 Draw a Picture, 105–106
class methods, 368–374 meaning, 86
Character class, 372–373 preconditions, 86
main method, 374 software objects, 4, 6
Math class, 369–372 specification, 86
Test class, 373–374 testing, 330–332
class relationships, 649–651 command interpreter(s), 486, 486–495
class variables, 366, 366–368 implementing, 487–492
assigning unique ID numbers, 368 separating user interface from model, 492–495
guidelines, 368 Command Interpreter pattern, 510–511
initialization, 791 Command Invocation pattern, 42
client(s), 4, 400 command/query pairs, implementing, 361–366
client class, 640 comment(s), 81, 81–84
clone method, 665–669 documentation, 83–84
implementing, 666–667
Index C5743 40143.ps 11/30/06 1:32 PM Page 818
818
INDEX
819
INDEX
discrete structures, 765 equal sign (=)
Display a Frame pattern, 44 equal operator, 176
displaying images from files, 506–507 statements, 19
documentation, 606–607 equals method, overriding, 663–665
API, 743 Equals pattern, 688
becker library, 243n, 797–814 equivalence, 410
external, 84–85 testing for, 410–411
Robot class, 26–29 Equivalence Test pattern, 450–451
documentation comments, 83, 84–85 error(s)
dot (.), messages, 14 avoiding with stepwise refinement, 134–135
double method, 470 checking user input, 481–484
double type, 338 compile-time, 30–32
do-while loops, 242–243 debugging. See debugging
Draw a Picture command, 105–106 intent (logic), 30, 33–34
drawing using loops, 251–257 run-time, 30, 32–33
loop counter, 252–253 testing for, with stepwise refinement, 135
nesting selection and repetition, 253–257 user, GUI forgiveness of, 628
dynamic arrays, 551–558 error messages, 11
combining approaches, 557–558 error tolerance of GUIs, 626
partially filled. See partially filled arrays Error-Checked Input pattern, 509–510
resizing, 554–557 escape sequences, 346, 346–347, 796
evaluating expressions, 175, 175–176
evaluation diagrams, 233, 233–234
E event(s), 716, 716–717
event objects, 716, 737–738
E method, 436
example programs, 15–25
easy to learn GUIs, 626
multiple objects, 24–25
Eclipse project, 311
program listings, 17–18
Edison, Thomas, 34
sending messages, 20
effectiveness of GUIs, 625
setting up initial situation, 19–20
efficiency of GUIs, 625
situations, 15–16
Eiffel programming language, 623n
tracing programs, 20–22
Either This or That pattern, 202–203
exception(s), 424, 424–431
elements
checked, 426, 427–428
of arrays, 521
defensive programming, 621–622
of collections, 431
handling, 426–428
else-clause, 183
propagating, 428–429
encapsulation, 25, 615
reading a stack trace, 425–426
complexity of programs, 615, 616–617
throwing, 424–425
encoding, 793
unchecked, 426
Unicode, 794–796
Exception class, 424–425
engaging interfaces, GUIs, 625
exponents, 338
enumeration(s) (enumerated types), 355–358, 356
expressions, 175
Enumeration pattern, 382–383
evaluating, 175–176
Index C5743 40143.ps 11/30/06 1:32 PM Page 820
820
INDEX
821
INDEX
displaying images from files, 503, 506–507 hashing, 441
drawing using loops, 251–257 helper classes, delegating work to, 614–615
extending, 92–102 helper method(s), 120
file choosers, 503, 504–506 declaring parameters, 153
frames, 35–37 GUIs, 151–155
helper methods, 151–155 making private, 608
identifying listeners for components, 741–743 nesting statements, 227
implementing, 377–378 putting duplicated code in, 608–609
informing user interface of changes, 379–380 using parameters, 153–155
invoking methods, 100 Helper Method pattern, 155–156
iterative design, 625–626 high-fidelity prototypes, 625
Java, 374–380 host name, 460
laying out components, 711–713 human-computer interaction, 767
layout managers, 680–686
learning to use components, 740–747
libraries of components, 448 I
making graphical components interactive,
icons
750–756
changing size, 75
models, views, and controllers, 698–700
transparency, 75–76
overriding methods, 97–99
identifiers, 79, 79–81
painting components, 747–749
capitalization, 81
patterns, 700
if statements, 169–171
quality, 624–628
flowcharts, 169
repainting, 312–318
general from, 173
scaling images, 196–200
nesting statements, 225–226
sequence diagrams, 733–735
semantics, 174
setting up model and view, 700–705
syntax, 174
specifying methods, 375–377
then-clause, 173
steps for building, 700
while statements compared, 168–174
views. See views, GUI
if-else statements, 183–186
graphics, 767
else-clause, 183
GregorianCalendar class, 394
example using, 184–186
GridBagLayout strategy, 683
then-clause, 183
GridLayout strategy, 681–682
images from files, displaying, 506–507
GUIs. See graphical user interfaces (GUIs)
immutable classes, 612, 612–614
implementing
constructors, 70
H objects, extending classes, 69–73
handling things, 12 services, 70–71
hanging, 213 implicit parameters, 63–64, 64
harvestIntersection method, 179–181 indenting programs, 79
Has-a (Composition) pattern, 449–450 IndexOf method, 355
has-a relationships, 400
Index C5743 40143.ps 11/30/06 1:32 PM Page 822
822
INDEX
823
INDEX
J Light class, 77–78
Light method, 804–805
.jar files, 499–500 Linear Search pattern, 573–574
Java Archive, 499, 499–500 Liskov, Barbara, 645
Java library, sorting, 539–540, 673 lists, 431, 432–439
Java Program pattern, 40–41 adding elements, 433–434
Java Tutorial, 744 class diagrams, 438–439
javadoc tool, 84–85 construction, 432–433
JFileChooser, 503, 504–506 foreach loops, 437
JFrame object, 36 getting, setting, and removing elements,
JPanel object, components, 37–39 434–435
justification, columnar number format, 343 processing elements, 436–438
local variables. See temporary variable(s)
logic errors, 30, 33–34
K logical negation operator, 175
key(s), 431, 530 logical operators, 231–232
maps, 431 precedence, 234–235
multiple, sorting using, 676–677 long integer type, 338
searching using, 530 long method, File class, 480
keyboard focus, 721 loop(s), 174. See also while loops
keywords, 79, 80 assignment statements, 191–193
count-down, 192–193
do-while, 242–243
L drawing. See drawing using loops
foreach, 437, 528
layout(s), 680 guidelines on use, 246
layout managers, 680, 680–686 infinite, 213–214
BorderLayout strategy, 683 nested, 253
FlowLayout strategy, 680–681 repetition, 253–257
GridBagLayout strategy, 683 when statements, 174
GridLayout strategy, 681–682 while-true, 243–246
nesting layout strategies, 684–686 loop counter, 252–253
SpringLayout strategy, 683 Loop-and-a-Half pattern, 257–258
learning, ease of, of GUIs, 626 loop-and-a-half problem, 213, 246
left justification, 343 low-fidelity prototypes, 625
less than operator (<), 176
less than or equal operator (<=), 176
lexicographic order, 351, 351–352 M
libraries, 495–500
compiling without an IDE, 495–496 main method, 704–705
creating and using a package, 497–499 class methods, 374
.jar files, 499–500 multiple, 335–337
Java, sorting, 539–540, 673 maintainable programs, 586
lifetime of instance variables, 276 maintaining instance variables, 303
mantissa, 338
Index C5743 40143.ps 11/30/06 1:32 PM Page 824
824
INDEX
825
INDEX
numeric types, 337–344 precedence, 234–235, 787–792
converting between, 340–341 primitive and reference types, 351
floating-point, 338–340 or operator, 231
formatting numbers, 341–343 organization, 766
integer, 337–338 origin, 9
shortcuts, 344 output streams, 500
numerical methods, 768 overloading methods, 298
overriding methods, 97–99, 298, 411
Object class, 662–669
O toString method, 349
object(s)
classes versus, 7
event, 737–738
P
identifying, 412–413, 589–590, 648–651 package(s), 26, 495
initializing, 74 package statement, 497–499
instantiation, 6–7 paintComponent method, 312–318
multiple, 24–25 painting
representing records as, 472–477 GUI components, 747–749
software. See software objects repainting, 312–318
object diagrams, 5 @param tag, 83
object equality, 410 parameter(s), 14, 189–196
testing for, 410–411 assignment statements, 191–193
object identity, 410 declaring, 153
Object Instantiation pattern, 41–42 stepwise refinement, 193–196
object references, returning, 401–402 using, 153–155
object-oriented design methodology, 412–424, while statements with, 190
588–595, 647 parameter variables, 296–300
identifying objects and classes, 412–413, final keyword, 300
589–590, 648–651 initializing, 792
identifying services, 414–423, 652–655 initializing instance variables using, 298–300
solving the problem, 423–424, 655–661 instance variables compared, 289, 306–307
object-oriented programming languages, 4 name conflicts, 300
Once or Not at All pattern, 201 temporary variables compared, 296–298,
Open File for Input pattern, 508 306–307
Open File for Output pattern, 508 Parameterized Method pattern, 158–159
open for extension, 67 Parameterless Command pattern, 105
opening files, 461, 462–463 partially filled arrays, 551, 551–554
operands, 232 deleting elements, 553
operating systems, 766 problems, 554
operators, 231 sorted, inserting into, 552–553
Boolean, 231–232 Pascal, Blaise, 568
comparison, 176–177 Pascal’s Triangle, 568
logical. See logical operators
Index C5743 40143.ps 11/30/06 1:32 PM Page 826
826
INDEX
827
INDEX
primitive type arrays, 558–561 pseudocode, 138, 138–139
double, 558–559 public keyword, 63–64, 148–150
indices, 560–561 putThing method, 178–179
printing expressions, 310–312
System.out object, 310–311
private keyword, 148–150 Q
problem solving, 116
quality code, writing. See writing quality code
Process All Elements pattern, 452–453, 573
quality software
Process File pattern, 508–509
programmer’s perspective, 585–586
processing files, 463
user’s perspective, 584
processing streams, 501
queries, 4, 330–337
professional issues, 768
built-in, 174–175
program(s), 4
integer, testing, 176–177
compiling, 29–32
multiple main methods, 335–337
complexity. See complexity of programs
non-Boolean, predicates, 188
correct, 584
return statements, 223
evaluating with users, 598
side effects, 224
example. See example programs
size, 198–199
form, 25–26
software objects, 4–5
hanging, 213
storing results, 222–223
identifiers, 79–81
String object, 350–352
keywords (reserved words), 79, 80
testing commands, 330–332
maintainable, 586
testing queries, 332–335
modifying with stepwise refinement, 136–138
writing, 223–224
quality software, 584–586
Query pattern, 259–260
reliability, 584
running, 32–34
special symbols, 79
style, 78–85
R
testable, 586 random access, 541
tracing. See tracing programs ranges, integers, 338
understandability, 585 reading, 461
usability, 584 from console, 480–481
program fragments, 169n files, 461–462
programming defensively. See defensive records as objects, 472–475
programming records, 460
programming fundamentals, 765 representing as objects, 472–477
programming languages, 767 recursion, infinite, 88
Eiffel, 623n refactoring, 586, 597
object-oriented, 4 reference variables, 403–411, 404
prompt, 85, 481 aliases, 406–409
Prompt class, 485 garbage collection, 409
prototyping, 625 memory, 404–406
provider streams, 501 testing for equality, 410–411
Index C5743 40143.ps 11/30/06 1:32 PM Page 828
828
INDEX
829
INDEX
single-line comments, 81, 81–82 identifying required services, 118–119
sink, 500 parameters, 193–196
situations, 15–16 style, 247
final, 16 testing and debugging, 135
initial. See initial situation understandability of programs, 133–134
size of icons, 75 Strategy pattern, 674, 674–679, 687
size queries, 198–199 anonymous classes, 677–678
social issues, 768 applications of strategy objects, 678–679
software engineering, 768 Comparator interface, 674–676
software objects, 4 sorting with multiple keys, 676–677
attributes, 4, 5–6, 13 streams, 500, 500–503
class diagrams, 7 byte, 501, 503
classes, 6–7 character input, 501–502
commands, 4, 6 character output, 502–503
modeling robots using, 12–15 processing, 501
queries, 4–5 provider, 501
Sojourner, 8–9 street(s), 9
solving problems, 116 Streetlight method, 811–812
sorting arrays, 534–540 StreetLight subclass, 76
coding Selection Sort, 536–538 String method
without helper methods, 538–539 Scanner class, 470
Java library, 539–540 File class, 479
Selection Sort overview, 535–536 String type, 347–355
sorting using Java library, 539–540, 673 Java support, 347–348
sound, 429–431 overriding toString method, 349
source, 500 querying strings, 350–352
source code, 17, 17–25 transforming strings, 352–353
spaghetti code, 243 stroke, 200
special symbols, 79 structure of files, 466–472
specification, structured programming, 243
programs, 587 stubs, 124
commands, 86 style, 246–251
defining in development process, 587–588 positively stated simple expressions, 247–249
SpringLayout strategy, 683 stepwise refinement, 247
stack traces, 425–426, 426 visual structure of code, 250–251
state, 6 styles of fonts, 722
state change diagrams, 6 subclasses, 58
statement(s), 20. See also specific statements subject, 589
static keyword, instance variables, 285 substitution principle, 645, 645–646
static variables. See class variables superclasses, 58, 59
stepwise refinement, 117, 117–138 swapping array elements, 525–526
error avoidance, 134–135 Swing, 92, 92–93
future modifications, 136–138 syntax, 174
helper methods, 120
Index C5743 40143.ps 11/30/06 1:32 PM Page 830
830
INDEX
831
INDEX
identifiers, 81 while loops, 212–218
instance. See instance variable(s) avoiding common errors, 212–214
names, 80 four-step process for constructing, 214–218
non-numeric types. See boolean type; char while statements, 171–173
type; String type flowcharts, 169
numeric types. See numeric types general form, 173–174
reference. See reference variables if statements compared, 168–174
selecting, rules of thumb for, 307 nesting statements, 225–226
temporary (local). See temporary variable(s) using with parameters, 190
variable declarations, 19, 276, 276–277 while-true loops, 243–246
verification, 56 example, 245–246
views, 449 form, 244
views, GUIs, 698–700 structured programming, 243
building, 709–726 white space, programs, 78–79
infrastructure, 703–704 whitespace, 466
integrating with controllers, 738–739 working directory, 463
making graphical components interactive, working incrementally, 744–747
750–756 wrapper classes, 446, 446–447
multiple views, 726–735 writing files, 464, 464–466
painting components, 747–749 writing quality code, 606–615
refining, 721–725 avoiding nested loops, 607
setting up, 700–705 delegating work to helper classes, 614–615
updating, 713–715 document classes and methods, 606–607
view patterns, 725–726 keeping data and processing together, 611–612
visual computing, 767 keeping methods shore, 607–608
visualizing arrays, 521–522 making helper methods private, 608
void keyword, 63–64 making instance variables private, 609–610
void method, 436, 440, 444 putting duplicated code in helper methods,
608–609
writing immutable classes, 612–614
W writing powerful constructors, 610–611
walk-throughs, 592
wall(s), 10
Wall method, 814
Z
waterfall model, 598 Zero or More Times pattern, 202
when statements, 174
Index C5743 40143.ps 11/30/06 1:32 PM Page 832
Index C5743 40143.ps 11/30/06 1:32 PM Page 833
Index C5743 40143.ps 11/30/06 1:32 PM Page 834
Index C5743 40143.ps 11/30/06 1:32 PM Page 835
Index C5743 40143.ps 11/30/06 1:32 PM Page 836
Index C5743 40143.ps 11/30/06 1:32 PM Page 837
Index C5743 40143.ps 11/30/06 1:32 PM Page 838
Index C5743 40143.ps 11/30/06 1:32 PM Page 839
Sun Microsystems, Inc. Binary Code License Agreement for the
JAVA 2 PLATFORM STANDARD EDITION RUNTIME ENVIRONMENT 5.0
SUN MICROSYSTEMS, INC. (“SUN”) IS WILLING TO LICENSE THE SOFTWARE IDENTIFIED BELOW TO YOU ONLY UPON THE CONDITION THAT YOU
ACCEPT ALL OF THE TERMS CONTAINED IN THIS BINARY CODE LICENSE AGREEMENT AND SUPPLEMENTAL LICENSE TERMS (COLLECTIVELY
“AGREEMENT”). PLEASE READ THE AGREEMENT CAREFULLY. BY DOWNLOADING OR INSTALLING THIS SOFTWARE, YOU ACCEPT THE TERMS OF
THE AGREEMENT. INDICATE ACCEPTANCE BY SELECTING THE “ACCEPT” BUTTON AT THE BOTTOM OF THE AGREEMENT. IF YOU ARE NOT
WILLING TO BE BOUND BY ALL THE TERMS, SELECT THE “DECLINE” BUTTON AT THE BOTTOM OF THE AGREEMENT AND THE DOWNLOAD OR
INSTALL PROCESS WILL NOT CONTINUE.
1. DEFINITIONS. “Software” means the identified above in binary form, any other machine readable materials (including, but not limited to, libraries, source files,
header files, and data files), any updates or error corrections provided by Sun, and any user manuals, programming guides and other documentation provided to you
by Sun under this Agreement. “Programs” mean Java applets and applications intended to run on the Java 2 Platform Standard Edition (J2SE platform) platform on
Java-enabled general purpose desktop computers and servers.
2. LICENSE TO USE. Subject to the terms and conditions of this Agreement, including, but not limited to the Java Technology Restrictions of the Supplemental
License Terms, Sun grants you a non-exclusive, non-transferable, limited license without license fees to reproduce and use internally Software complete and unmodi-
fied for the sole purpose of running Programs. Additional licenses for developers and/or publishers are granted in the Supplemental License Terms.
3. RESTRICTIONS. Software is confidential and copyrighted. Title to Software and all associated intellectual property rights is retained by Sun and/or its licensors.
Unless enforcement is prohibited by applicable law, you may not modify, decompile, or reverse engineer Software. You acknowledge that Licensed Software is not
designed or intended for use in the design, construction, operation or maintenance of any nuclear facility. Sun Microsystems, Inc. disclaims any express or implied
warranty of fitness for such uses. No right, title or interest in or to any trademark, service mark, logo or trade name of Sun or its licensors is granted under this
Agreement. Additional restrictions for developers and/or publishers licenses are set forth in the Supplemental License Terms.
4. LIMITED WARRANTY. Sun warrants to you that for a period of ninety (90) days from the date of purchase, as evidenced by a copy of the receipt, the media on
which Software is furnished (if any) will be free of defects in materials and workmanship under normal use. Except for the foregoing, Software is provided “AS IS”.
Your exclusive remedy and Sun’s entire liability under this limited warranty will be at Sun’s option to replace Software media or refund the fee paid for Software.
Any implied warranties on the Software are limited to 90 days. Some states do not allow limitations on duration of an implied warranty, so the above may not apply
to you. This limited warranty gives you specific legal rights. You may have others, which vary from state to state.
5. DISCLAIMER OF WARRANTY. UNLESS SPECIFIED IN THIS AGREEMENT, ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT
ARE DISCLAIMED, EXCEPT TO THE EXTENT THAT THESE DISCLAIMERS ARE HELD TO BE LEGALLY INVALID.
6. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
REVENUE, PROFIT OR DATA, OR FOR SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED
REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF OR RELATED TO THE USE OF OR INABILITY TO USE SOFTWARE, EVEN IF SUN
HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. In no event will Sun’s liability to you, whether in contract, tort (including negligence), or oth-
erwise, exceed the amount paid by you for Software under this Agreement. The foregoing limitations will apply even if the above stated warranty fails of its essential
purpose. Some states do not allow the exclusion of incidental or consequential damages, so some of the terms above may not be applicable to you.
7. TERMINATION. This Agreement is effective until terminated. You may terminate this Agreement at any time by destroying all copies of Software. This Agreement
will terminate immediately without notice from Sun if you fail to comply with any provision of this Agreement. Either party may terminate this Agreement immedi-
ately should any Software become, or in either party’s opinion be likely to become, the subject of a claim of infringement of any intellectual property right. Upon
Termination, you must destroy all copies of Software.
8. EXPORT REGULATIONS. All Software and technical data dealivered under this Agreement are subject to US export control laws and may be subject to export or
import regulations in other countries. You agree to comply strictly with all such laws and regulations and acknowledge that you have the responsibility to obtain
such licenses to export, re-export, or import as may be required after delivery to you.
9. TRADEMARKS AND LOGOS. You acknowledge and agree as between you and Sun that Sun owns the SUN, SOLARIS, JAVA, JINI, FORTE, and iPLANET
trademarks and all SUN, SOLARIS, JAVA, JINI, FORTE, and iPLANET-related trademarks, service marks, logos and other brand designations (“Sun Marks”), and
you agree to comply with the Sun Trademark and Logo Usage Requirements currently located at http://www.sun.com/policies/trademarks. Any use you make of the
Sun Marks inures to Sun’s benefit.
10. U.S. GOVERNMENT RESTRICTED RIGHTS. If Software is being acquired by or on behalf of the U.S. Government or by a U.S. Government prime contractor or
subcontractor (at any tier), then the Government’s rights in Software and accompanying documentation will be only as set forth in this Agreement; this is in accor-
dance with 48 CFR 227.7201 through 227.7202-4 (for Department of Defense (DOD) acquisitions) and with 48 CFR 2.101 and 12.212 (for non-DOD acquisitions).
11. GOVERNING LAW. Any action related to this Agreement will be governed by California law and controlling U.S. federal law. No choice of law rules of any
jurisdiction will apply.
12. SEVERABILITY. If any provision of this Agreement is held to be unenforceable, this Agreement will remain in effect with the provision omitted, unless omission
would frustrate the intent of the parties, in which case this Agreement will immediately terminate.
13. INTEGRATION. This Agreement is the entire agreement between you and Sun relating to its subject matter. It supersedes all prior or contemporaneous oral or
written communications, proposals, representations and warranties and prevails over any conflicting or additional terms of any quote, order, acknowledgment, or
other communication between the parties relating to its subject matter during the term of this Agreement. No modification of this Agreement will be binding, unless
in writing and signed by an authorized representative of each party.
SUPPLEMENTAL LICENSE TERMS
These Supplemental License Terms add to or modify the terms of the Binary Code License Agreement. Capitalized terms not defined in these Supplemental Terms shall
have the same meanings ascribed to them in the Binary Code License Agreement . These Supplemental Terms shall supersede any inconsistent or conflicting terms in the
Binary Code License Agreement, or in any license contained within the Software.
A. Software Internal Use and Development License Grant. Subject to the terms and conditions of this Agreement and restrictions and exceptions set forth in the Software
“README” file, including, but not limited to the Java Technology Restrictions of these Supplemental Terms, Sun grants you a non-exclusive, non-transferable, limited
license without fees to reproduce internally and use internally the Software complete and unmodified for the purpose of designing, developing, and testing your
Programs.
B. License to Distribute Software. Subject to the terms and conditions of this Agreement and restrictions and exceptions set forth in the Software README file, includ-
ing, but not limited to the Java Technology Restrictions of these Supplemental Terms, Sun grants you a non-exclusive, non-transferable, limited license without fees
to reproduce and distribute the Software, provided that (i) you distribute the Software complete and unmodified and only bundled as part of, and for the sole pur-
pose of running, your Programs, (ii) the Programs add significant and primary functionality to the Software, (iii) you do not distribute additional software intended
to replace any component(s) of the Software, (iv) you do not remove or alter any proprietary legends or notices contained in the Software, (v) you only distribute the
Software subject to a license agreement that protects Sun’s interests consistent with the terms contained in this Agreement, and (vi) you agree to defend and indem-
nify Sun and its licensors from and against any damages, costs, liabilities, settlement amounts and/or expenses (including attorneys’ fees) incurred in connection with
any claim, lawsuit or action by any third party that arises or results from the use or distribution of any and all Programs and/or Software.
C. Java Technology Restrictions. You may not create, modify, or change the behavior of, or authorize your licensees to create, modify, or change the behavior of, classes,
interfaces, or subpackages that are in any way identified as “java”, “javax”, “sun” or similar convention as specified by Sun in any naming convention designation.
D. Source Code. Software may contain source code that, unless expressly licensed for other purposes, is provided solely for reference purposes pursuant to the terms of
this Agreement. Source code may not be redistributed unless expressly provided for in this Agreement.
E. Third Party Code. Third Party Code. Additional copyright notices and license terms applicable to portions of the Software are set forth in
the THIRDPARTYLICENSEREADME.txt file. In addition to any terms and conditions of any third party opensource/freeware license identified in the
THIRDPARTYLICENSEREADME.txt file, the disclaimer of warranty and limitation of liability provisions in paragraphs 5 and 6 of the Binary Code License Agreement shall
apply to all Software in this distribution.
For inquiries please contact: Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A.
(LFI#141623/Form ID#011801)