0% found this document useful (0 votes)
460 views

Timothy Budd - Understanding Object-Oriented Programming With Java

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
460 views

Timothy Budd - Understanding Object-Oriented Programming With Java

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 373

Understanding Object-Oriented Programming with Java

Timothy A. Budd
Oregon State University
Corvallis, Oregon
USA All rights reserved.
No part of this publication may be reproduced,
stored in a retrieval system, or transmitted,
in any form or by any means, electronic, mechanical,
photocopying, recording, or otherwise,
without the prior written permission of the Author.
Copyright 1997 by Timothy A. Budd
July 23, 1997
0

This is not a blank page.


Preface and Comments to
Editor and Reviewers
This is the rst complete draft. Much has changed, there is still much to do.
There are many books on Java that will teach you how to use the language, but few
books teach you why the language works in the way it does.
Many books will help you learn the mechanics of Java programming, few books will teach
you the deeper issues that lie behind the programming syntax. That is what I'm trying to
do with this book. My goal is to give the student a better and deeper understanding of the
philosophy behind the language, not just the mechanics of the langauge.
I've tried to illustrate these with extensive examples from the Java standard library.
Here students can learn, for example, the many design patterns that are found in the AWT,
or the multitude of purposes for which inheritance in used in the standard classes, or why
there are 22 dierent types of input/output le streams. Here they can discover how the lack
of an ordered container class in the standard library is not a simple omission, but re ects a
fundamental and deep property of the Java language. So on and so on.
The book is structured in several major parts. Part 1 is the general, language inde-
pendent introduction. The rst major OO concept, that of classes, encapsulation, and
responsibilities, will be introduced in this part and reinforced in part 2. The second chapter
of part 1 is just background history on the development of Java. Part 2 introduces Java by
several graduated example programs (paradigms, in the original sense of the world). Part 3
discusses inheritance, the next major OO concept the student needs to master after learning
about classes and objects. Part 4 discusses polymorphism, which is generally a more subtle
concept for the student to understand than inhertiance. Polymorphism is manifest in Java
in many ways, as shown by the extensive examples found in this part of the book. Part 5
discusses features of the Java world that are important for the student to understand, but
not particularly notable for their OO features.
As you (or the reviewers) read the book I would be very interested in your answers to a
couple simple questions:
1. Do you think this approach is a good idea?
1
2

2. I've tried (not always successfully) to avoid sounding like a language reference man-
ual. In fact, many items of syntax or features of the library that are not particulary
notworthy are not explained in detail at all. Will instructors see this as a serious
disadvantage?
3. There are three chapters in part 1. The history chapter is probably expected, but
is somewhat unrelated to the other two. This makes deciding the appropriate order
somewhat dicult.
4. Part 5 of the book is somewhat problematic. It doesn't t well with the rest of the
book, but I've not found a way to t it in with the structure of the earlier sections.
The material is not necessarily so advanced that it should be left this late { in fact,
I can imagine that many instructors would want to reference some of the material
earlier in the term.
5. Should more emphasis be placed on applets and the web? I see this as a rather
tangental issue, but I suspect others might disagree.
6. In addition to study questions and exercises, a feature I started adding to some of the
later chapters is a short section near the end entitied cross references. This would be a
short section where I point out where in the book I discuss in more details ideas that
might have been only glossed over in the current chapter. Since so many concepts are
so entertwined, it is not possible to discuss everything at once. This would help the
reader trying to track down a specic idea, or trying to nd an example that illustrates
the use of a given construct.
7. Are there other topics you think I should address that are not described here either
in the text or the table of contents?
A potential source of problems is the fact that Java is not yet a stable language. Ev-
erything I describe here is version 1.1 (which may already be one version more recent than
some of the reviewers). However, some of the hints I have heard indicate that there is likely
another round of language revisions coming soon, particularly the introduction of generics,
which, if true, should certainly be mentioned in part 4 of the book.
Contents
I Understanding the Object-Oriented World View 11
1 Object-Oriented Thinking 13
1.1 A Way of Viewing the World . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.1.1 Agents and Communities . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.1.2 Responsibility, Messages, and Methods . . . . . . . . . . . . . . . . . . 15
1.1.3 Responsibilities . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.1.4 Classes and Instances . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.1.5 Class Hierarchies{Inheritance . . . . . . . . . . . . . . . . . . . . . . . 16
1.1.6 Method Binding, Overriding, and Exceptions . . . . . . . . . . . . . . 19
1.1.7 Summary of Object-Oriented Concepts . . . . . . . . . . . . . . . . . 19
1.2 Computation as Simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
1.2.1 The Power of Metaphor . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.3 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2 A Brief History of Java 27
2.1 Client Side Computing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.1.1 Bytecode Interpreters and Just In Time Compilers . . . . . . . . . . . 29
2.1.2 Security Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.1.3 Specialization of Interfaces . . . . . . . . . . . . . . . . . . . . . . . . 30
2.2 The White Paper Description . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.2.1 Java is Simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.2.2 Java is Object-Oriented . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.2.3 Java is Network Savvy . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.2.4 Java is Interpreted . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.2.5 Java is Robust . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.2.6 Java is Secure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.2.7 Java is Architecture Neutral . . . . . . . . . . . . . . . . . . . . . . . . 33
2.2.8 Java is Portable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.2.9 Java is High-performance . . . . . . . . . . . . . . . . . . . . . . . . . 34
2.2.10 Java is Multithreaded . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3
4 CONTENTS

2.2.11 Java is Dynamic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34


2.3 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3 Object-Oriented Design 37
3.1 Responsibility Implies Noninterference . . . . . . . . . . . . . . . . . . . . . . 37
3.2 Programming in the Small and in the Large . . . . . . . . . . . . . . . . . . . 38
3.3 Why Begin with Behavior? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.4 A Case Study in RDD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
3.4.1 The Interactive Intelligent Kitchen Helper . . . . . . . . . . . . . . . . 39
3.4.2 Working through Scenarios . . . . . . . . . . . . . . . . . . . . . . . . 41
3.4.3 Identication of Components . . . . . . . . . . . . . . . . . . . . . . . 41
3.5 CRC Cards{Recording Responsibility . . . . . . . . . . . . . . . . . . . . . . 41
3.5.1 Give Components a Physical Representation . . . . . . . . . . . . . . 42
3.5.2 The What/Who Cycle . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.5.3 Documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.6 Components and Behavior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.6.1 Postponing Decisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.6.2 Preparing for Change . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
3.6.3 Continuing the Scenario . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.6.4 Interaction Diagrams . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.7 Software Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.7.1 Behavior and State . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.7.2 Instances and Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.7.3 Coupling and Cohesion . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.7.4 Interface and Implementation{Parnas's Principles . . . . . . . . . . . . 50
3.8 Formalize the Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.8.1 Coming up with Names . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3.9 Designing the Representation . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.10 Implementing Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
3.11 Integration of Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.12 Maintenance and Evolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.13 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56

II Understanding Paradigms 59
4 A Paradigm 61
4.1 Program Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.2 The Connection to the Java world . . . . . . . . . . . . . . . . . . . . . . . . 64
4.3 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.4 Access Modiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.5 Lifetime Modiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
CONTENTS 5

4.6 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69


5 Ball Worlds 73
5.1 Data Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.2 Constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.3 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.4 The Java Graphics Model . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.5 The class Ball . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.6 Multiple Objects of the Same Class . . . . . . . . . . . . . . . . . . . . . . . . 83
5.7 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
6 A Cannon Game 89
6.1 The Simple Cannon Ball Game . . . . . . . . . . . . . . . . . . . . . . . . . . 90
6.1.1 Balls that Respond to Gravity . . . . . . . . . . . . . . . . . . . . . . 93
6.1.2 Integers and ints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
6.2 Adding User Interaction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
6.2.1 Inner classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.2.2 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
6.2.3 The Java Event Model . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
6.2.4 Window layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
6.3 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6.4 Cross References . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
7 Pin Ball Game Construction Kit 105
7.1 First Version of Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
7.1.1 Collection Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
7.1.2 Mouse Listeners . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
7.1.3 Multiple Threads of Execution . . . . . . . . . . . . . . . . . . . . . . 110
7.1.4 Exception Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
7.2 Adding Targets: Inheritance and Interfaces . . . . . . . . . . . . . . . . . . . 112
7.2.1 The Pin Ball Target Interface . . . . . . . . . . . . . . . . . . . . . . . 112
7.2.2 Adding a Label to our Pin Ball Game . . . . . . . . . . . . . . . . . . 117
7.3 Pin Ball Game Construction: Mouse Events Reconsidered . . . . . . . . . . . 121
7.4 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123

III Understanding Inheritance 129


8 Understanding Inheritance 131
8.1 An Intuitive Description of Inheritance . . . . . . . . . . . . . . . . . . . . . . 131
8.2 The base class Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
8.3 Subclass, Subtype, and Substitutability . . . . . . . . . . . . . . . . . . . . . 133
6 CONTENTS

8.4 Forms of Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134


8.4.1 Inheritance for Specialization . . . . . . . . . . . . . . . . . . . . . . . 135
8.4.2 Inheritance for Specication . . . . . . . . . . . . . . . . . . . . . . . . 135
8.4.3 Inheritance for Construction . . . . . . . . . . . . . . . . . . . . . . . 137
8.4.4 Inheritance for Extension . . . . . . . . . . . . . . . . . . . . . . . . . 138
8.4.5 Inheritance for Limitation . . . . . . . . . . . . . . . . . . . . . . . . . 139
8.4.6 Inheritance for Combination . . . . . . . . . . . . . . . . . . . . . . . . 140
8.4.7 Summary of the Forms of Inheritance . . . . . . . . . . . . . . . . . . 141
8.5 Modiers and Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
8.6 Programming as a Multi Person Activity . . . . . . . . . . . . . . . . . . . . . 142
8.7 The Benets of Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
8.7.1 Software Reusability . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
8.7.2 Increased Reliability . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
8.7.3 Code Sharing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
8.7.4 Consistency of Interface . . . . . . . . . . . . . . . . . . . . . . . . . . 144
8.7.5 Software Components . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
8.7.6 Rapid Prototyping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
8.7.7 Polymorphism and Frameworks . . . . . . . . . . . . . . . . . . . . . . 144
8.7.8 Information Hiding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
8.8 The Costs of Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
8.8.1 Execution Speed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
8.8.2 Program Size . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
8.8.3 Message-Passing Overhead . . . . . . . . . . . . . . . . . . . . . . . . 146
8.8.4 Program Complexity . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
8.9 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
9 A Case Study: Solitaire 149
9.1 The Class Card . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
9.2 The Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
9.3 Card Piles{Inheritance in Action . . . . . . . . . . . . . . . . . . . . . . . . . 154
9.3.1 The Suit Piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
9.3.2 The Deck Pile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
9.3.3 The Discard Pile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
9.3.4 The Tableau Piles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
9.4 The Application Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
9.5 Playing the Polymorphic Game . . . . . . . . . . . . . . . . . . . . . . . . . . 165
9.6 Building a More Complete Game . . . . . . . . . . . . . . . . . . . . . . . . . 167
9.7 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
CONTENTS 7

10 Mechanisms for Software Reuse 171


10.1 Substitutability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
10.1.1 The Is-a Rule and the Has-a Rule . . . . . . . . . . . . . . . . . . . . 172
10.1.2 Inheritance of Code and Inheritance of Behavior . . . . . . . . . . . . 173
10.2 Composition and Inheritance Described . . . . . . . . . . . . . . . . . . . . . 174
10.2.1 Using Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
10.2.2 Using Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
10.3 Composition and Inheritance Contrasted . . . . . . . . . . . . . . . . . . . . . 178
10.4 Combining Inheritance and Composition . . . . . . . . . . . . . . . . . . . . . 180
10.5 Novel Forms of Software Reuse . . . . . . . . . . . . . . . . . . . . . . . . . . 181
10.5.1 Dynamic Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
10.5.2 Inheritance of Inner Classes . . . . . . . . . . . . . . . . . . . . . . . . 182
10.5.3 Unnamed Classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
10.6 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
11 Implications of Inheritance 187
11.1 The Polymorphic Variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
11.2 Memory Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
11.2.1 An Alternative Technique . . . . . . . . . . . . . . . . . . . . . . . . . 191
11.3 Assignment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
11.3.1 Clones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
11.3.2 Parameters are a form of Assignment . . . . . . . . . . . . . . . . . . 195
11.4 Equality Test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
11.5 Garbage Collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
11.6 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200

IV Understanding Polymorphism 203


12 Polymorphism 205
12.1 Varieties of Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
12.2 Polymorphic Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
12.3 Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
12.3.1 Overloading Names in Real Life . . . . . . . . . . . . . . . . . . . . . . 207
12.3.2 Overloading and Coercion . . . . . . . . . . . . . . . . . . . . . . . . . 207
12.3.3 Overloading from Separate Classes . . . . . . . . . . . . . . . . . . . . 208
12.3.4 Parameteric Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . 209
12.4 Overriding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
12.4.1 Replacement and Renement . . . . . . . . . . . . . . . . . . . . . . . 210
12.5 Abstract Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
12.6 Pure Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
12.7 Eciency and Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
8 CONTENTS

12.8 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213


13 The AWT 215
13.1 The AWT Class Hierarchy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
13.2 The Layout Manager . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
13.2.1 Layout Manager Types . . . . . . . . . . . . . . . . . . . . . . . . . . 219
13.3 User Interface Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
13.3.1 Labels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
13.3.2 Button . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
13.3.3 Canvas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
13.3.4 Scroll Bars . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
13.3.5 Text Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
13.3.6 Checkbox . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
13.3.7 Checkbox Groups, Choices and Lists . . . . . . . . . . . . . . . . . . . 226
13.4 Panels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
13.4.1 ScrollPane . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
13.5 Case Study: A Color Display . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
13.6 Dialogs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
13.6.1 Example Program for Dialogs . . . . . . . . . . . . . . . . . . . . . . . 234
13.7 The Menu Bar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
13.7.1 A Quit Menu Facility . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
14 Input and Output Streams 241
14.1 Input Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
14.1.1 Physical Input Streams . . . . . . . . . . . . . . . . . . . . . . . . . . 242
14.1.2 Virtual Input Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
14.1.3 Stream Tokenizer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
14.2 Output Streams . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
14.3 Piped Input and Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 248
14.4 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
15 Design Patterns 255
15.1 Adapter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 255
15.2 Composition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
15.3 Strategy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 258
15.4 Observer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259
15.5 Flyweight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
15.6 Abstract Factory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
15.7 Factory Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
15.8 Iterator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
15.9 Decorator (Filter or Wrapper) . . . . . . . . . . . . . . . . . . . . . . . . . . . 263
15.10Proxy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
CONTENTS 9

15.11Bridge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
15.12Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265

V Understanding the Java World 267


16 Exception Handling 269
16.1 Information Transmitted to the Catch Block . . . . . . . . . . . . . . . . . . . 271
16.2 Catching Multiple Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
16.3 Exceptions Thrown in the Standard Library . . . . . . . . . . . . . . . . . . . 271
16.4 Throwing Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
16.5 Passing On Exceptions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
16.6 Chapter Summary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
17 Utility Classes 275
17.1 Point . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
17.2 Dimension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
17.3 Date . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
17.3.1 After the Epoch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277
17.4 Math . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278
17.5 Random . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
17.6 Toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
17.7 System . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
18 Strings and related classes 283
18.1 Operations on Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
18.2 String Buers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
18.3 String Tokenizers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
18.4 Parsing String Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289
19 Understanding Graphics 291
19.1 Color . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
19.2 Rectangles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
19.2.1 Rectangle Sample Program . . . . . . . . . . . . . . . . . . . . . . . . 293
19.3 Fonts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
19.3.1 Font Metrics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297
19.3.2 Font Example Program . . . . . . . . . . . . . . . . . . . . . . . . . . 297
19.4 Images . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
19.4.1 Animation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
19.5 Graphics Contexts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
19.6 A Simple Painting Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
10 CONTENTS

20 Collection Classes 311


20.1 Elements Types and primitive value Wrappers . . . . . . . . . . . . . . . . . 311
20.2 Enumerators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
20.3 The Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 314
20.4 The Vector collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
20.4.1 Using a Vector as an array . . . . . . . . . . . . . . . . . . . . . . . . . 315
20.4.2 Using a Vector as a stack . . . . . . . . . . . . . . . . . . . . . . . . . 317
20.4.3 Using a Vector as a queue . . . . . . . . . . . . . . . . . . . . . . . . . 317
20.4.4 Using a Vector as a set . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
20.4.5 Using a Vector as a list . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
20.5 The Stack collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
20.6 The BitSet collection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
20.6.1 Example Program: Prime Sieve . . . . . . . . . . . . . . . . . . . . . . 321
20.7 The Dictionary interface and the Hashtable collection . . . . . . . . . . . . . . 321
20.7.1 Example Program: A Concordance . . . . . . . . . . . . . . . . . . . . 323
20.7.2 Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
20.8 Why are there no ordered collections? . . . . . . . . . . . . . . . . . . . . . . 326
20.9 Building your Own Containers . . . . . . . . . . . . . . . . . . . . . . . . . . 328
21 Multiple Threads of Execution 333
21.1 Creating Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
21.1.1 Synchronizing Threads . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
21.2 Case Study: A Tetris Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
21.2.1 The Tetris Game Class . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
21.2.2 The PieceMover Thread . . . . . . . . . . . . . . . . . . . . . . . . . . 342
21.2.3 The Game Piece class . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
22 Applets and Web Programming 351
22.1 Applets and HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351
22.2 Security Issues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
22.3 Applets and Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
22.4 Obtaining Resources using an Applet . . . . . . . . . . . . . . . . . . . . . . . 353
22.4.1 Universal Resource Locators . . . . . . . . . . . . . . . . . . . . . . . . 355
22.4.2 Loading a New Web Page . . . . . . . . . . . . . . . . . . . . . . . . . 356
22.5 Combining Applications and Applets . . . . . . . . . . . . . . . . . . . . . . . 356
Glossary 359
Part I

Understanding the
Object-Oriented World View

11
Chapter 1

Object-Oriented Thinking
This is a book about object-oriented programming. In particular, this is a book that explores
the principle ideas of object-oriented programming in the context of the Java programming
language. Object-oriented programming has been a hot topic for the past decade, and more
recently Java has become the commonly perceived embodiment of object-oriented ideas.
This book will help you understand Java. It makes no pretensions to being a language
reference manual there are many other books that fall into that category. But knowing the
syntax for a language should not be confused with an understanding of why the language
has been developed in the way it has, why certain things are done the way they are, or why
Java programs look the way they do. This book explores this issue of why.
Object-oriented programming is frequently referred to as a new programming paradigm.
The word paradigm originally meant example, or model. For example, a paradigm sentence
would help you remember how to conjugate a word in a foreign language. More generally,
a model is an example that helps you understand how the world works. For example, the
Newtonian model of physics explains why apples fall to the ground. In computer science, a
paradigm explains how the elements that go into making a computer program are organized
and how they interact with each other. For this reason the rst step in understanding Java
is appreciating the object-oriented world view.

1.1 A Way of Viewing the World


To illustrate the major ideas in object-oriented programming, let us consider how we might
go about handling a real-world situation and then ask how we could make the computer
more closely model the techniques employed.
Suppose I wish to send owers to a friend who lives in a city many miles away. Let
me call my friend Sally. Because of the distance, there is no possibility of my picking the
owers and carrying them to her door myself. Nevertheless, sending her the owers is an
13
14 CHAPTER 1. OBJECT-ORIENTED THINKING

Sally Delivery Person Gardeners


E C
E C
E Grower
E Flower Arranger
ME Flora E ; 

@ E ;  Wholesaler
@ 
Sally's Florist

Figure 1.1: The Community of Agents Helping Me

easy enough task I merely go down to my local orist (who happens to be named Flora),
tell her the variety and quantity of owers I wish to send and give her Sally's address, and
I can be assured the owers will be delivered expediently and automatically.

1.1.1 Agents and Communities


At the risk of belaboring a point, let me emphasize that the mechanism I used to solve my
problem was to nd an appropriate agent (namely, Flora) and to pass to her a message
containing my request. It is the responsibility of Flora to satisfy my request. There is some
method{some algorithm or set of operations{used by Flora to do this. I do not need to know
the particular method she will use to satisfy my request indeed, often I do not want to
know the details. This information is usually hidden from my inspection.
If I investigated however, I might discover that Flora delivers a slightly dierent message
to another orist in my friend's city. That orist, in turn, perhaps has a subordinate who
makes the ower arrangement. The orist then passes the owers, along with yet another
message, to a delivery person, and so on. Earlier, the orist in Sally's city had obtained
her owers from a ower wholesaler who, in turn, had interactions with the ower growers,
each of whom had to manage a team of gardeners.
So, our rst observation of object-oriented problem solving is that the solution to my
problem required the help of many other individuals (Figure 1.1). Without their help, my
problem could not be easily solved. We phrase this in a general fashion as the following:

An object oriented program is structured as a community of interacting agents.


Each agent has a role to play. Each agent provides a service, or performs an
action, that is used by other members of the community.
1.1. A WAY OF VIEWING THE WORLD 15

1.1.2 Responsibility, Messages, and Methods


The chain reaction that ultimately resulted in the solution to my program began with my
request to Flora. This request lead to other requests, which lead to still more requests,
until my owers ultimately reached my friend. We see, therefore, that members of this
community interact with each other by making requests. So, our next principle of object-
oriented problem solving is the vehicle by which activities are initiated:
Action is initiated in object-oriented programming by the transmission of a mes-
sage to an agent (an object) responsible for the action. The message encodes the
request for an action and is accompanied by any additional information (argu-
ments) needed to carry out the request. The receiver is the agent to whom the
message is sent. If the receiver accepts the message, it accepts the responsibility
to carry out the indicated action. In response to a message, the receiver will
perform some method to satisfy the request.
We have noted the important principle of information hiding in regard to message
passing{that is, the client sending the request need not know the actual means by which the
request will be honored. There is another principle, all too human, that we see is implicit in
message passing. If there is a task to perform, the rst thought of the client is to nd some-
body else he or she can ask to do the work. This second reaction often becomes atrophied
in many programmers with extensive experience in conventional techniques. Frequently,
a dicult hurdle to overcome is the idea in the programmer's mind that he or she must
write everything and not use the services of others. An important part of object-oriented
programming is the development of reusable components, and an important rst step in the
use of reusable components is a willingness to trust software written by others.
Information hiding is also an important aspect of programming in conventional lan-
guages. In what sense is a message dierent from, say, a procedure call? In both cases,
there is a set of well-dened steps that will be initiated following the request. But, there
are two important distinctions.
The rst is that in a message there is a designated receiver for that message the receiver
is some agent to which the message is sent. In a procedure call, there is no designated
receiver.
The second is that the interpretation of the message (that is, the method used to respond
to the message) is dependent on the receiver and can vary with dierent receivers. I can give
a message to my wife Elizabeth, for example, and she will understand it and a satisfactory
outcome will be produced (that is, owers will be delivered to my friend). However, the
method Elizabeth uses to satisfy the request (in all likelihood, simply passing the request on
to Flora) will be dierent from that used by Flora in response to the same request. If I ask
Kenneth, my dentist, to send owers to my friend, he may not have a method for solving
that problem. If he understands the request at all, he will probably issue an appropriate
error diagnostic.
16 CHAPTER 1. OBJECT-ORIENTED THINKING

Let us move our discussion back to the level of computers and programs. There, the
distinction between message passing and procedure calling is that, in message passing, there
is a designated receiver, and the interpretation{the selection of a method to execute in
response to the message{may vary with dierent receivers. Usually, the specic receiver
for any given message will not be known until run time, so the determination of which
method to invoke cannot be made until then. Thus, we say there is late binding between
the message (function or procedure name) and the code fragment (method) used to respond
to the message. This situation is in contrast to the very early (compile-time or link-time)
binding of name to code fragment in conventional procedure calls.
1.1.3 Responsibilities
A fundamental concept in object-oriented programming is to describe behavior in terms of
responsibilities. My request for action indicates only the desired outcome ( owers for my
friend). Flora is free to pursue any technique that achieves the desired objective and is not
hampered by interference on my part.
By discussing a problem in terms of responsibilities we increase the level of abstraction.
This permits greater independence between agents, a critical factor in solving complex prob-
lems. The entire collection of responsibilities associated with an object is often described
by the term protocol.
The dierence between viewing software in traditional, structured terms and viewing it
from an object-oriented perspective can be summarized by a twist on a well-known quote:
Ask not what you can do to your data structures,
but rather ask what your data structures can do for you.
1.1.4 Classes and Instances
Although I have only dealt with Flora a few times, I have a rough idea of the behavior I can
expect when I go into her shop and present her with my request. I am able to make certain
assumptions because I have information about orists in general, and I expect that Flora,
being an instance of this category, will t the general pattern. We can use the term Florist
to represent the category (or class) of all orists. Let us incorporate these notions into our
next principle of object-oriented programming:
All objects are instances of a class. The method invoked by an object in response
to a message is determined by the class of the receiver. All objects of a given
class use the same method in response to similar messages.

1.1.5 Class Hierarchies{Inheritance


I have more information about Flora{not necessarily because she is a orist but because
she is a shopkeeper. I know, for example, that I probably will be asked for money as part
1.1. A WAY OF VIEWING THE WORLD 17

' $
Material Object
' $
Animal
' Mammal
$
' $
'Human
$
Shopkeeper
' $
Florist
Flora
&
& %%
&
& %
%
& %
& %
Figure 1.2: { The categories surrounding Flora.

of the transaction, and that in return for payment I will be given a receipt. These actions
are true of grocers, stationers, and other shopkeepers. Since the category Florist is a more
specialized form of the category Shopkeeper, any knowledge I have of Shopkeepers is also
true of Florists and hence of Flora.
One way to think about how I have organized my knowledge of Flora is in terms of a
hierarchy of categories (see Figure 1.2). Flora is a Florist, but Florist is a specialized form
of Shopkeeper. Furthermore, a Shopkeeper is also a Human so I know, for example, that
Flora is probably bipedal. A Human is a Mammal (therefore they nurse their young and
have hair), and a Mammal is an Animal (therefore it breathes oxygen), and an Animal is a
Material Object (therefore it has mass and weight). Thus, quite a lot of knowledge that I
have that is applicable to Flora is not directly associated with her, or even with her category
Florist.
The principle that knowledge of a more general category is also applicable to a more
18 CHAPTER 1. OBJECT-ORIENTED THINKING

Material Objects
HH
  HH
 HH
  HH
H


HH
H
Animal Plant

Mammal
X
Flower
;@XXXXX
; @ XXX
XXX
;
; @
@ XX
Dog Human Platypus
;H
B HH
; B HH
;
; B
B HH
Shopkeeper Artist Dentist

Florist Potter

Flash Flora Elizabeth Kenneth Phyl owers

Figure 1.3: { A class hierarchy for various material objects.

specic category is called inheritance. We say that the class Florist will inherit attributes of
the class (or category) Shopkeeper.
There is an alternative graphical technique often used to illustrate this relationship,
particularly when there are many individuals with diering lineage's. This technique shows
classes listed in a hierarchical tree-like structure, with more abstract classes (such as Material
Object or Animal) listed near the top of the tree, and more specic classes, and nally
individuals, are listed near the bottom. Figure 1.3 shows this class hierarchy for Flora. This
same hierarchy also includes Elizabeth, my dog Flash, Phyl the platypus who lives at the
zoo, and the owers I am sending to my friend.
Information that I possess about Flora because she is an instance of class Human is also
applicable to my wife Elizabeth, for example. Information that I have about her because
1.1. A WAY OF VIEWING THE WORLD 19

she is a Mammal is applicable to Flash as well. Information about all members of Material
Object is equally applicable to Flora and to her owers. We capture this in the idea of
inheritance:
Classes can be organized into a hierarchical inheritance structure. A child class
(or subclass) will inherit attributes from a parent class higher in the tree. An
abstract parent class is a class (such as Mammal) for which there are no direct
instances it is used only to create subclasses.

1.1.6 Method Binding, Overriding, and Exceptions


Phyl the platypus presents a problem for our simple organizing structure. I know that
mammals give birth to live children, and Phyl is certainly a Mammal, yet Phyl (or rather
his mate Phyllis) lays eggs. To accommodate this, we need to nd a technique to encode
exceptions to a general rule.
We do this by decreeing that information contained in a subclass can override information
inherited from a parent class. Most often, implementations of this approach takes the form
of a method in a subclass having the same name as a method in the parent class, combined
with a rule for how the search for a method to match a specic message is conducted:
The search for a method to invoke in response to a given message begins with the
class of the receiver. If no appropriate method is found, the search is conducted
in the parent class of this class. The search continues up the parent class chain
until either a method is found or the parent class chain is exhausted. In the
former case the method is executed in the latter case, an error message is issued.
If methods with the same name can be found higher in the class hierarchy, the
method executed is said to override the inherited behavior.
Even if the compiler cannot determine which method will be invoked at run time, in
many object-oriented languages, such as Java, it can determine whether there will be an
appropriate method and issue an error message as a compile-time error diagnostic rather
than as a run-time message.
That my wife Elizabeth and my orist Flora will respond to my message by dierent
methods is an example of one form of polymorphism. We will discuss this important part
of object-oriented programming in Chapter 12. As explained, that I do not, and need not,
know exactly what method Flora will use to honor my message is an example of information
hiding.

1.1.7 Summary of Object-Oriented Concepts


Alan Kay, considered by some to be the father of object-oriented programming, identied
the following characteristics as fundamental to OOP Kay 1993]:
20 CHAPTER 1. OBJECT-ORIENTED THINKING

1. Everything is an object.
2. Computation is performed by objects communicating with each other, requesting that
other objects perform actions. Objects communicate by sending and receiving mes-
sages. A message is a request for action bundled with whatever arguments may be
necessary to complete the task.
3. Each object has its own memory, which consists of other objects.
4. Every object is an instance of a class. A class simply represents a grouping of similar
objects, such as integers or lists.
5. The class is the repository for behavior associated with an object. That is, all objects
that are instances of the same class can perform the same actions.
6. Classes are organized into a singly rooted tree structure, called the inheritance hier-
archy. Memory and behavior associated with instances of a class are automatically
available to any class associated with a descendant in this tree structure.

1.2 Computation as Simulation


The view of programming represented by the example of sending owers to my friend is very
dierent from the conventional conception of a computer. The traditional model describing
the behavior of a computer executing a program is a process-state or pigeon-hole model. In
this view, the computer is a data manager, following some pattern of instructions, wandering
through memory, pulling values out of various slots (memory addresses), transforming them
in some manner, and pushing the results back into other slots (see Figure 1.4). By examining
the values in the slots, we can determine the state of the machine or the results produced by
a computation. Although this model may be a more or less accurate picture of what takes
place inside a computer, it does little to help us understand how to solve problems using the
computer, and it is certainly not the way most people (pigeon handlers and postal workers
excepted) go about solving problems.
In contrast, in the object-oriented framework we never mention memory addresses, vari-
ables, assignments, or any of the conventional programming terms. Instead, we speak of
objects, messages, and responsibility for some action. In Dan Ingalls's memorable phrase:
Instead of a bit-grinding processor...plundering data structures, we have a uni-
verse of well-behaved objects that courteously ask each other to carry out their
various desires Ingalls 1981].
Another author has described object-oriented programming as \animistic": a process of
creating a host of helpers that form a community and assist the programmer in the solution
of a problem Actor 1987].
1.2. COMPUTATION AS SIMULATION 21

i: j:
2 3
#
# x:
47
"!
"! a1]: a2]: a3]: a4]:
4 6 2 4

Figure 1.4: { Visualization of imperative programming.

This view of programming as creating a \universe" is in many ways similar to a style


of computer simulation called \discrete event-driven simulation." In brief, in a discrete
event-driven simulation the user creates computer models of the various elements of the
simulation, describes how they will interact with one another, and sets them moving. This
is almost identical to the average object-oriented program, in which the user describes what
the various entities in the universe for the program are, and how they will interact with one
another, and nally sets them in motion. Thus, in object-oriented programming, we have
the view that computation is simulation Kay 1977].

1.2.1 The Power of Metaphor


An easily overlooked benet to the use of object-oriented techniques is the power of metaphor.
When programmers think about problems in terms of behaviors and responsibilities of ob-
jects, they bring with them a wealth of intuition, ideas, and understanding from their ev-
eryday experience. When envisioned as pigeon holes, mailboxes, or slots containing values,
there is little in the programmer's background to provide insight into how problems should
be structured.
Although anthropomorphic descriptions such as the quote by Ingalls may strike some
people as odd, in fact they are a re ection of the great expositive power of metaphor.
Journalists make use of metaphor every day, as in the following description of object-oriented
programming from Newsweek:
Unlike the usual programming method{writing software one line at a time{
NeXT's \object-oriented" system oers larger building blocks that developers
22 CHAPTER 1. OBJECT-ORIENTED THINKING

can quickly assemble the way a kid builds faces on Mr. Potato Head.
Possibly this feature, more than any other, is responsible for the frequent observation
that it is often easier to teach object-oriented programming concepts to computer novices
than to computer professionals. Novice users quickly adapt the metaphors with which they
are already comfortable from their everyday life, whereas seasoned computer professionals
are blinded by an adherence to more traditional ways of viewing computation.
As you start to examine the Java programs presented in the book, as well as creating
your own Java programs, you may nd it useful to envision the process of programming as
similar to the task of \training" a universe of agents to interact smoothly with each other,
each providing a certain small and well dened service to the others, each contributing to
the eective execution of the whole.

1.3 Chapter Summary


 Object-oriented programming is not simply a few new features added to programming
languages. Rather, it is a new way of thinking about the process of decomposing
problems and developing programming solutions.
 Object-oriented programming views a program as a collection of loosely connected
agents, termed objects. Each object is responsible for specic tasks. It is by the
interaction of objects that computation proceeds. In a certain sense, therefore, pro-
gramming is nothing more or less than the simulation of a model universe.
 An object is an encapsulation of state (data values) and behavior (operations). Thus,
an object is in many ways similar to a module or an abstract data type.
 The behavior of objects is dictated by the object class. Every object is an instance of
some class. All instances of the same class will behave in a similar fashion (that is,
invoke the same method) in response to a similar request.
 An object will exhibit its behavior by invoking a method (similar to executing a
procedure) in response to a message. The interpretation of the message (that is, the
specic method used) is decided by the object and may dier from one class of objects
to another.
 Objects and classes extend the concept of abstract data types by adding the notion
of inheritance. Classes can be organized into a hierarchical inheritance tree. Data
and behavior associated with classes higher in the tree can also be accessed and used
by classes lower in the tree. Such classes are said to inherit their behavior from the
parent classes.
 By reducing the interdependency among software components, object-oriented pro-
gramming permits the development of reusable software systems. Such components
Further Reading 23

can be created and tested as independent units, in isolation from other portions of a
software application.
 Reusable software components permit the programmer to deal with problems on a
higher level of abstraction. We can dene and manipulate objects simply in terms of
the messages they understand and a description of the tasks they perform, ignoring
implementation details.

Further Reading
I said at the beginning of the chapter that this is not a reference manual. The reference
manual written by the developers of the language is Gosling 96]. But perhaps even more
useful for most programmers is the annotated description of the Java class library pre-
sented by Chan 96]. Information on the internal workings of the Java system is presented
by Lindholm 97].
I noted earlier that many consider Alan Kay to be the father of object-oriented pro-
gramming. Like most simple assertions, this one is only somewhat supportable. Kay
himself Kay 1993] traces much of the in uence on his development of Smalltalk to the
earlier computer programming language Simula, developed in Scandinavia in the early
1960s Dahl 1966]. A more accurate history would be that most of the principles of object-
oriented programming were fully worked out by the developers of Simula, but that these
would have been largely ignored by the profession had they not been rediscovered by Kay
in the creation of the Smalltalk programming language. A widely read 1981 issue of Byte
magazine, in which the quote by Ingalls given earlier in this chapter appears, did much to
popularize the concepts developed by Kay and his team at Xerox PARC.
Like most terms that have found their way into the popular jargon, object-oriented is used
more often than it is dened. Thus, the question What is object-oriented programming?
is surprisingly dicult to answer. Bjarne Stroustrup has quipped that many arguments
appear to boil down to the following syllogism:
 X is good.
 Object-oriented is good.
 Ergo, X is object-oriented Stroustrup 1988].
Roger King argued Kim 1989], that his cat is object-oriented. After all, a cat exhibits char-
acteristic behavior, responds to messages, is heir to a long tradition of inherited responses,
and manages its own quite independent internal state.
Many authors have tried to provide a precise description of the properties a program-
ming language must possess to be called object-oriented. I myself have written an earlier
book (Budd 97]) that tries to explain object-oriented concepts in a language-indepent fash-
ion. See also, for example, the analysis by Josephine Micallef Micallef 1988], or Peter
24 CHAPTER 1. OBJECT-ORIENTED THINKING

Wegner Wegner 1986]. Wegner, distinguishes object-based languages, which support only
abstraction (such as Ada), from object-oriented languages, which must also support inheri-
tance.
Other authors{notably Brad Cox Cox 1990]{dene the term much more broadly. To
Cox, object-oriented programming represents the objective of programming by assembling
solutions from collections of o-the-shelf subcomponents, rather than any particular tech-
nology we may use to achieve this objective. Rather than drawing lines that are divisive, we
should embrace any and all means that show promise in leading to a new software Industrial
Revolution. Cox's book on OOP Cox 1986], although written early in the development of
object-oriented programming and now somewhat dated in details, is nevertheless one of the
most readable manifestos of the object-oriented movement.

Study Questions
1. What is the original meaning of the word paradigm?
2. How do objects interact with each other?
3. How are messages dierent from procedure calls?
4. What is the name applied to describe an algorithm an object uses to respond to a
request?
5. Why does the object-oriented approach naturally imply a high degree of information
hiding?
6. What is a class? How are classes linked to behavior?
7. What is a class inheritance hierarchy? How is it linked to classes and behavior?
8. What does it mean for one method to override another method from a parent class?
9. What are the basic elements of the process-state model of computation?
10. How does the object-oriented model of computation dier from the process-state
model?

Exercises
1. In an object-oriented inheritance hierarchy, each level is a more specialized form of
the preceding level. Give an example of a hierarchy found in everyday life that has
this property. Some types of hierarchy found in everyday life are not inheritance
hierarchies. Give an example of a noninheritance hierarchy.
Further Reading 25

2. Look up the denition of paradigm in at least three dictionaries. Relate these deni-
tions to computer programming languages.
3. Take a real-world problem, such as the task of sending owers described earlier, and
describe its solution in terms of agents (objects) and responsibilities.
4. If you are familiar with two or more distinct computer programming languages, give
an example of a problem showing how one language would direct the programmer to
one type of solution, and a dierent language would encourage an alternative solution.
5. Argue either for or against the position that computing is basically simulation. (You
may want to read the article by Alan Kay in Scientic American Kay 1977].)
26 CHAPTER 1. OBJECT-ORIENTED THINKING
Chapter 2

A Brief History of Java


The language we now call Java was originally named Oak, and was developed in 1991 by
a computer scientist at Sun Microsystems named James Gosling. The intended purpose of
Oak was as a language for use in embedded consumer electronic applications, such as VCRs.
Although this intended use might at rst seem to be only a bit of historical computer trivia,
in fact it was important in determining the characteristics of the language we see today.
The model imagined by Gosling in designing Oak envisioned a world where many elec-
tronic devices, such as your telephone, your VCR, your television, your computer, would
all be connected together over a vast computer network. Such applications would generally
possess embedded computer processors, which would control the essential running of the
component. (Although we have not yet reached the point where telephones are routinely
connected to the internet, the part about almost all electronic devices having embedded
processors is now almost universally true).
There are several characteristics of embedded systems that make then dierent from
the average general purpose computer. Two of the most important features are size and
reliability. Generally, the processors that run in embedded systems are very small, and
possess only meager amounts of memory. Thus, a programming language designed for an
embedded system must be able to be translated into a very concise encoding. An even more
important aspect is reliability. When a program fails on a typical general purpose computer
the user is annoyed, but even in the worst case the user can typically recover by rebooting
the computer. The annoyance level is much higher if, for example, the software controlling
a telephone fails. For this reason embedded systems should almost never fail, and should
respond as gracefully as possible to exceptional and erroneous conditions.
Many features of Java re ect this original mindset. The language itself is small and
simple, and can be translated into a very compact internal representation. Programming
constructs, such as pointers or the goto statement, which experience had shown to be a
source of many programming errors, were simply eliminated from the language. A powerful
concept called exception handling was borrowed from earlier languages, but greatly extended
27
28 CHAPTER 2. A BRIEF HISTORY OF JAVA

and intimately tied into the other aspects of the language. This exception handing facility
meant that any programmer writing in Java would be forced to deal with the possibilities of
how programs could fail in unpredictable ways, and create code to handle the unexpected
in a (hopefully) graceful fashion.
For a number of reasons, Java (or Oak) as a language for embedded consumer electronics
did not materialize. But as interest in embedded systems at Sun was starting to wane, the
phenomena known as the World-Wide-Web was just beginning. The Web was originally
developed in the late 1980s by a small group of scientists at a research lab in Switzerland as
a means of quickly communicating research results to a physically far ung set of colleagues.
It was quickly realized, however, that the framework provided by the Web was applicable
to a wide range of information. First scientists in all disciplines started using the Web, and
eventually the ideas found their way into the mainstream. Now, almost every organization,
large or small, must have a web page. Similarly, almost every advertisement in print or
television contains an obligatory web address, or URL.
To understand how Java ts into the world-wide-web, and the importance of internet
computing, one must rst understand a little about the concept of clients and servers, and
the dierence between server side computing and client side computing.

2.1 Client Side Computing


Although Java is a general purpose programming language that can be used to create almost
any type of computer program, much of the excitement surrounding Java has been generated
by its employment as a language for creating programs intended for execution across the
internet. To understand the process of programming for the web, one must rst understand
a few basic characteristics of the internet in general. The internet is a classic example of
a client/server system. A person using the internet works at their own computer, which
runs an internet aware application, such as a web browser (Figure 2.1). This is called
the client system. The client application communicates over the internet with another
computer, perhaps one physically very far away. For example, a web browser might request
the information on a web page stored on a distant computer. The second computer, the
server computer, transmits the contents of the web page to the client application. The client
computer then determines how to display this information to the user.
From the beginning of the web, it has been possible to add dynamic behavior to web
pages by executing programs. However, in the past these programs executed on the server
computer. The client transmitted a request, and the server responded by executing a pro-
gram, and transmitting the result. Many web-based forms are still implemented in this
fashion. Such an arrangement is sometimes termed CGI{bin processing, after the directory
where executable programs are conventionally located on the server computer.
There are several problems with this arrangement. For one, transmission times are often
slow, causing a noticeable delay in time between the point the client asks that a program
be executed, and the time the results are returned. In addition, server programs often deal
2.1. CLIENT SIDE COMPUTING 29


 @
XXX @
@


client side server side

Figure 2.1: Client and Server Side Computing

with many clients at once (perhaps hundreds or thousands), causing further reduction in
performance. In contrast, the client machines are often lightly loaded personal machines.
Frequently the client machine is executing little more than the single internet application.
The key idea of client-side computing is that rather than executing the program on the
server side and transmitting the result, the server will transmit the program to the client.
The client will then execute the program locally. Not only is the program then run on a
less heavily loaded system, but the only delay is the time to transmit the program. Once
the program starts executing, any interactions between the user and the program take place
locally, and do not need to cross the internet.

2.1.1 Bytecode Interpreters and Just In Time Compilers


Of course, there are many diculties that must be overcome for this process to be successful.
The rst is that the client computer must be able to execute the program. Often the server
and client machines will be dierent types of computers. Indeed, the client may not even
know what type of machine the server is using. Thus, the traditional concept of computer
programs being translated into machine code for a specic machine will not work in this
environment machine code that executes ne on the sever computer may not work at all
for the client computer.
Instead, Java is translated into a device independent bytecode. This bytecode (so called
because most instructions are one or two bytes long), is like a machine language for an
imaginary machine, a Java-specic machine, or virtual machine. Each computer that runs
Java programs then processes these bytecodes into a form that works correctly on the current
system. There are several ways this can be done. The easiest scheme is to have an interpreter
that reads and executes bytecodes one by one as they are needed. Better performance can
be obtained by using a just-in-time compiler, or JIT. This system takes the Java bytecodes
30 CHAPTER 2. A BRIEF HISTORY OF JAVA

and translates them into the native machine code for the client system. These programs
then run as fast as any compiled program created specically for the client computer.

2.1.2 Security Issues


Another problem that must be overcome for client side computing to be widely accepted
is the issue of security. A program running on a server machine can do very little damage
to a client machine the server simply does not have access to memory or to les to which
damage could be done. But a program running on the client side could, in theory, have full
access to the client computer resources. There is great potential for such a program to do
signicant damage, such as erasing les from a hard drive.
Java programs get around this problem by using a security manager. The security
manager is provided by the client, and limits the actions that can be performed by the Java
program provided by the server. For example, most security managers will not allow a Java
program to access the le system, or transmit information across the internet to machines
other than the client or server processors. Thus, the potential damage that a Java program
can cause will be very limited.

2.1.3 Specialization of Interfaces


Yet another issue arises due to the fact that the client and server systems can be entirely
dierent types of computers. As anybody who has tried to write a graphical program in
another language for multiple platforms has discovered, the sequences of commands needed
to perform graphical operations, such as placing a window on the display, varies greatly from
one machine to another. As we will describe in Section 15.11, the solution to this problem
requires a careful coordination between the client and server computers, with portions of a
Java program originating on one machine, and other parts coming from the second.
The server program is structured in terms of generic classes, such as Window and Button.
These classes are the same regardless of the type of system on which the Java program is run.
But when executed, the rst task these components perform is to create a peer component.
The peer component originates on the client system, and is not part of the server program.
Thus, a button running on a PC will create a PC-Button peer, while the same program
running on a Macintosh will create a MAC-Button peer. All the device specic aspects of
drawing the image on the local computer system are held in the peer class, and not in the
generic button class.

2.2 The White Paper Description


In one of the rst papers published by Sun Microsystems that dealt with Java, the language
was described in the following fashion:
2.2. THE WHITE PAPER DESCRIPTION 31

\Java: A simple, object-oriented, network-savvy, interpreted, robust, secure,


architecture neutral, portable, high-performance, multithreaded, dynamic lan-
guage."
The tongue-in-cheek description is intentionally reminiscent of the hyperbole laden and
buzzword heavy descriptions characteristic of advertising copy. Nevertheless, each phrase
had been carefully selected, and in total they accurately sum up the language.

2.2.1 Java is Simple


Although much of the syntax of Java is based on the earlier object-oriented language C++,
the Java language is considerably simpler than C++. Many keywords have been eliminated,
there is no preprocessor, there are far fewer special cases, and the language is augmented
with a much larger library of high level development tools. Operator overloading as been
eliminated, as have independent functions, global variables, the goto statement, structures
and pointers.
One of the more notable omissions from Java is the latter, the concept of the pointer. In
many other languages there is a distinction between a value and a pointer to a value. Values
are static, xed size entities. Pointers are dynamic quantities that are lled at run-time.
As we will discuss in Chapter 11, there important reasons why an object-oriented language
should make heavy use of pointers. Java does, but hides this fact from the programmer.
As far as the programmer is concerned, there are no pointers, although in fact this is only
possible because almost everything is internally a pointer. However, the elimination of this
construct removes an entire class of common programming errors, making it greatly easier
to construct reliable and correct programs.

2.2.2 Java is Object-Oriented


The language Java is founded upon the object-oriented principles described in Chapter 1.
The only unit of programming is the class description. Unlike other languages, in Java
there are no functions and no variables that can exist outside of class boundaries. Thus,
all Java programs must be built out of objects. Other languages, notably C++ and Object
Pascal, have tried to combine object-oriented features on top of an existing, non-object-
oriented language. The unfortunate consequence of such a design is that programmers can
continue working in their old, non-object-oriented fashion. By forcing all programs into
an object-oriented structure, the many benets of object-oriented design (an emphasis on
encapsulation, an orientation towards reusability) are much more easily realized.

2.2.3 Java is Network Savvy


From the start, Java was designed with the internet in mind. Although it is possible to
construct Java programs that do not deal with the internet (indeed, most of the programs
32 CHAPTER 2. A BRIEF HISTORY OF JAVA

in this book will not), the language provides a rich set of tools for programming across
a network. The Java standard library provides a plethora of classes for describing URLs
(Universal Resource Locators), for making connections between client and server computers
(See Chapter 22), and for execution in controlled environments such as a world-wide-web
browser.

2.2.4 Java is Interpreted


Java was designed for a multicomputer execution environment. From the rst, it was in-
tended that the computer a program was developed on might not be the same as the com-
puter on which it is stored, which might again be dierent from the computer on which it is
nally executed. Thus, the traditional model where a program is translated by a compiler
into the machine language for a particular machine will not work for Java the machine
language for the system on which the program is developed will probably not work for the
machine on which the program is eventually executed.
Java systems initially got around this problem by using an interpreter. Java programs
were compiled into an assembly language for an imaginary machine, called the virtual ma-
chine. These assembly language instructions, called bytecodes, could be stored on any type
of machine. Any machine that supported Java program would provide a simulator, an in-
terpreter, that would read the bytecode values and execute them. In this fashion, any type
of computer could be used as a Java virtual machine.
However, interpreters have one serious disadvantage over conventional systems. They
are generally much slower in execution. Recent innovations in the Java world have advanced
upon this idea of interpreters, and largely eliminated this performance penalty. A Just In
Time Compiler, or JIT, is a system that reads the machine independent bytecode repre-
sentation of a Java program, and immediately prior to execution translates the bytecode
representation into actual machine instructions for the system on which the Java program
is being run. Because Java programs then execute as machine instructions, they can be al-
most as fast as programs compiled in more conventional languages for the specic hardware
platform, and still retain the portability of the virtual machine.

2.2.5 Java is Robust


The Java language and associated libraries are designed to be graceful in the presence of
hardware and software errors. An example of this is the extensive use of exception handling.
Statements that can potentially receive an error, such as a le operation that could attempt
to read from a nonexistent source, will throw an error instead of performing an erroneous
operation. The semantics of the language insist that the programmer must deal with this
possibility any time a le operation is intended. Thus, programmers are forced into thinking
about potential sources of error, and their programs are therefore much more robust in the
presence of error producing conditions.
2.2. THE WHITE PAPER DESCRIPTION 33

Another feature that makes Java programs more robust is automatic memory manage-
ment, or garbage collection. Programmers writing in languages that use manual memory
management, for example C++, frequently forget to release memory resources once they
are nished with them. Long running programs therefore slowly increase their memory
requirements, until they catastrophically fail. The Java run-time system instead automat-
ically detects and recovers memory that is no longer being used by the currently running
program. This both simplies the programmers task, and makes programs more reliable.

2.2.6 Java is Secure


By eliminating pointers, the Java language removes what is perhaps the most common
source of programming errors inadvertently overwriting memory locations that are being
addressed by pointers with improperly set values. The Java language also insists that array
index values are checked for validity before they are referenced, and that all variables must
be assigned a value before used.
But the Java language is just the rst layer in a multilevel security system. Bytecodes
themselves (which may or may not have been produced by a Java compiler) are examined
before they executed by the Java interpreter. This check determines that bytecodes are
free of a number of common errors, for example that they do not access classes incorrectly,
over ow or under ow the operand stack, or use illegal data conversions.
Finally, as we will discuss in Chapter 22, many of the applications envisioned for Java
involve programs that are stored on one computer, but executed on another. Typically,
the computer on which the Java program will execute is a users personal computer. Few
users would trust Java if it were possible that programs brought over a network could
possibly cause damage, such as erasing a hard drive or removing a le. For this reason,
the designers of Java purposed created a programming environment where programs are
severely restricted in the type of operations they can perform. Because of these restrictions,
users can be assured that when they execute a program brought over the network, their
local computer is safe from tampering.

2.2.7 Java is Architecture Neutral


Because Java bytecodes do not correspond to any particular machine, they work with all
machines. A Java program is the same whether it runs on a PC, on a Macintosh, or on
a Unix system. This is very dierent from conventional languages. Although C++ is a
standard language, and therefore should be the same on all machines, the libraries needed
to perform activities such as placing a window on a display, or responding to a button press,
dier considerably from one platform to another. This is why it is very dicult to, for
example, move programs designed for the PC onto a Macintosh, or vice versa. But Java
hides these application specic details under a layer of abstraction in the standard Java
library. Thus, from the programmers point of view, all machines look the same.
34 CHAPTER 2. A BRIEF HISTORY OF JAVA

2.2.8 Java is Portable


Because the Java library hides architecture specic concepts, and because bytecodes are
the same regardless of the machine on which they are generated, Java programs possess an
unparalleled degree of portability. Indeed, the exact same program can be compiled on one
system, then executed on many dierent types of systems.

2.2.9 Java is High-performance


Although the initial implementations of Java bytecode interpreters exacted a heavy perfor-
mance penalty, the technology of Java execution has rapidly changed since the language
was introduced. Systems such as Just-in-Time compilers now allow platform independent
Java programs to be executed with nearly the same run-time performance as conventional
compiled languages.

2.2.10 Java is Multithreaded


Java is one of the rst languages to explicitly be designed purposely imagining the possibility
of multiple threads of execution running in one program. As we will describe in Chapter 21,
not only is it easy to set up such multitasking, but the coordination of these parallel processes
is also relatively simple.

2.2.11 Java is Dynamic


Finally, because Java programs move across the internet and execute on the users local
computer, they permit a degree of dynamic behavior that is impossible in older style systems.

2.3 Chapter Summary


Although much of the excitement of Java stems from its use in developing web-based ap-
plication programs, or applets, the Java language itself is a general purpose programming
language suitable for any task that can be solved using a computer. For the majority of the
rest of this book we will be dealing with more general application programs. We will return
to a discussion of web-based programming and applets in Chapter 22.
The intent of this book is to discuss the principles of object-oriented programming, and
in particular the way that object-oriented concepts are manifest in the Java programming
language. Nevertheless, an understanding of Java is not possible without an appreciation
of the history and intent of the language. In this chapter we have described how the Java
language was developed, and the original purpose for the language. The characteristics
required for this original purpose, namely a small language with a high degree of reliability,
turn out to also be desirable characteristics for any programming language. Thus, the Java
language was potential uses that far exceed the original designers intent.
2.3. CHAPTER SUMMARY 35

Study Questions
1. What was the original name given to the Java language?
2. What was the original intended use of Java programs?
3. What are some characteristics of embedded systems?
4. What is the dierence between server-side computing (also known as cgi-bin process-
ing) and client-side computing?
5. What is a just-in-time compiler?
36 CHAPTER 2. A BRIEF HISTORY OF JAVA
Chapter 3

Object-Oriented Design
A supercial description of the dierences between an object-oriented language, such as Java,
and a conventional programming language, such as Pascal, might concentrate on syntactic
dierences. In this area discussion would center on topics such as classes, inheritance,
message passing, and methods. But such an analysis miss the most important point of
object-oriented programming, which has nothing to do with syntax.
Working in an object-oriented language (that is, one that supports inheritance, message
passing, and classes) is neither a necessary nor sucient condition for doing object-oriented
programming. As we emphasized in Chapter 1, the most important aspect of OOP is a design
technique driven by the determination and delegation of responsibilities among members of a
community of agents working together in the solution of a common problem. This technique
has been called responsibility-driven design Wirfs-Brock 1989b, Wirfs-Brock 1990].

3.1 Responsibility Implies Noninterference


As anyone can attest who can remember being a child, or who has raised children, responsi-
bility is a sword that cuts both ways. When you make an object (be it a child or a software
system) responsible for specic actions, you expect a certain behavior, at least when the
rules are observed. But just as important, responsibility implies a degree of independence
or noninterference. If you tell a child that she is responsible for cleaning her room, you do
not normally stand over her and watch while that task is being performed{that is not the
nature of responsibility. Instead, you expect that, having issued a directive in the correct
fashion, the desired outcome will be produced.
Similarly, in the owers example from Chapter 1, I give the request to deliver owers to
my orist without stopping to think about how my request will be serviced. Flora, having
taken on the responsibility for this service, is free to operate without interference on my
part.
37
38 CHAPTER 3. OBJECT-ORIENTED DESIGN

The dierence between conventional programming and object-oriented programming is


in many ways the dierence between actively supervising a child while she performs a task,
and delegating to the child responsibility for that performance. Conventional programming
proceeds largely by doing something to something else{modifying a record or updating an
array, for example. Thus, one portion of code in a software system is often intimately tied,
by control and data connections, to many other sections of the system. Such dependencies
can come about through the use of global variables, through use of pointer values, or simply
through inappropriate use of and dependence on implementation details of other portions
of code. A responsibility-driven design attempts to cut these links, or at least make them
as unobtrusive as possible.
This notion might at rst seem no more subtle than the notions of information hiding
and modularity, which are important to programming even in conventional languages. But
responsibility-driven design elevates information hiding from a technique to an art. This
principle of information hiding becomes vitally important when one moves from program-
ming in the small to programming in the large.
One of the major benets of object-oriented programming occurs when software subsys-
tems are reused from one project to the next. For example, a simulation system might work
for both a simulation of balls on a billiards table and a simulation of sh in a sh tank.
This ability to reuse code implies that the software can have almost no domain-specic com-
ponents it must totally delegate responsibility for domain-specic behavior to application-
specic portions of the system. The ability to create such reusable code is not one that is
easily learned{it requires experience, careful examination of case studies (paradigms, in the
original sense of the word), and use of a programming language in which such delegation is
natural and easy to express. In subsequent chapters, we will present several such examples.

3.2 Programming in the Small and in the Large


The dierence between the development of individual projects and of more sizable software
systems is often described as programming in the small versus programming in the large.
Programming in the small characterizes projects with the following attributes:
 Code is developed by a single programmer, or perhaps by a very small collection of
programmers. A single individual can understand all aspects of a project, from top to
bottom, beginning to end.
 The major problem in the software development process is the design and development
of algorithms for dealing with the problem at hand.
Programming in the large, on the other hand, characterizes software projects with fea-
tures such as the following:
 The software system is developed by a large team of programmers. Individuals involved
in the specication or design of the system may dier from those involved in the coding
3.3. WHY BEGIN WITH BEHAVIOR? 39

of individual components, who may dier as well from those involved in the integration
of various components in the nal product. No single individual can be considered
responsible for the entire project, or even necessarily understands all aspects of the
project.
 The major problem in the software development process is the management of details
and the communication of information between diverse portions of the project.
While the beginning student will usually be acquainted with programming in the small,
aspects of many object-oriented languages are best understood as responses to the problems
encountered while programming in the large. Thus, some appreciation of the diculties
involved in developing large systems is a helpful prerequisite to understanding OOP.

3.3 Why Begin with Behavior?


Why begin the design process with an analysis of behavior? The simple answer is that the
behavior of a system is usually understood long before any other aspect.
Earlier software development techniques concentrated on ideas such as characterizing
the basic data structures or the overall structure of function calls, often within the creation
of a formal specication of the desired application. But structural elements of the appli-
cation can be identied only after a considerable amount of problem analysis. Similarly, a
formal specication often ended up as a document understood by neither programmer nor
client. But behavior is something that can be described almost from the moment an idea
is conceived, and (often unlike a formal specication) can be described in terms meaningful
to both the programmers and the client.
We will illustrate the application of Responsibility-Driven Design (RDD) with a case
study.

3.4 A Case Study in RDD


Imagine you are the chief software architect in a major computer rm. One day your boss
walks into your oce with an idea that, it is hoped, will be the next major success in
your product line. Your assignment is to develop the Interactive Intelligent Kitchen Helper
(Figure 3.1).
The task given to your software team is stated in very few words (written on what
appears to be the back of a slightly-used dinner napkin, in handwriting that appears to be
your boss's).
3.4.1 The Interactive Intelligent Kitchen Helper
Brie y, the Interactive Intelligent Kitchen Helper (IIKH) is a PC-based application that will
replace the index-card system of recipes found in the average kitchen. But more than simply
40 CHAPTER 3. OBJECT-ORIENTED DESIGN

' $
P
PP
H
 H PP
H HH PH
HH H H HH Welcome
H HPPH to the
  H
HH H  PHPH IIKH
 
HHHH HH HP PHP the
HH HHHH HHH P
H HHHHPP Interactive
HHHHH H HH H P  Intelligent
HHH HH HHHHHH P Kitchen
HH H HHHHH  Helper
HHHH 
HH
HH Press Return
to begin
HH
HH 


& %
H 

 
 22222222 
 222222222 
 
 2222222 


Figure 3.1: { View of the Interactive Intelligent Kitchen Helper.

maintaining a database of recipes, the kitchen helper assists in the planning of meals for an
extended period, say a week. The user of the IIKH can sit down at a terminal, browse the
database of recipes, and interactively create a series of menus. The IIKH will automatically
scale the recipes to any number of servings and will print out menus for the entire week, for
a particular day, or for a particular meal. And it will print an integrated grocery list of all
the items needed for the recipes for the entire period.
As is usually true with the initial descriptions of most software systems, the specication
for the IIKH is highly ambiguous on a number of important points. It is also true that, in
all likelihood, the eventual design and development of the software system to support the
IIKH will require the eorts of several programmers working together. Thus, the initial goal
of the software team must be to clarify the ambiguities in the description and to outline how
the project can be divided into components to be assigned for development to individual
team members.
The fundamental cornerstone of object-oriented programming is to characterize software
in terms of behavior that is, actions to be performed. We will see this repeated on many
levels in the development of the IIKH. Initially, the team will try to characterize, at a
3.5. CRC CARDS{RECORDING RESPONSIBILITY 41

very high level of abstraction, the behavior of the entire application. This then leads to
a description of the behavior of various software subsystems. Only when all behavior has
been identied and described will the software design team proceed to the coding step. In
the next several sections we will trace the tasks the software design team will perform in
producing this application.

3.4.2 Working through Scenarios


The rst task is to rene the specication. As we have already noted, initial specications are
almost always ambiguous and unclear on anything except the most general points. There are
several goals for this step. One objective is to get a better handle on the \look and feel" of the
eventual product. This information can then be carried back to the client (in this case, your
boss) to see if it is in agreement with the original conception. It is likely, perhaps inevitable,
that the specications for the nal application will change during the creation of the software
system, and it is important that the design be developed to easily accommodate change
and that potential changes be noted as early as possible. (See Section 3.6.2 \Preparing for
Change.") Equally important, at this point very high level decisions can be made concerning
the structure of the eventual software system. In particular, the activities to be performed
can be mapped onto components.

3.4.3 Identication of Components


The engineering of a complex physical system, such as a building or an automobile engine,
is simplied by dividing the design into smaller units. So, too, the engineering of software
is simplied by the identication and development of software components. A component
is simply an abstract entity that can perform tasks{that is, fulll some responsibilities. At
this point, it is not necessary to know exactly the eventual representation for a component
or how a component will perform a task. A component may ultimately be turned into a
function, a structure or class, or a collection of other components (a pattern). At this level
of development there are just two important characteristics:
 A component must have a small well-dened set of responsibilities.

 A component should interact with other components to the minimal extent possible.
We will shortly discuss the reasoning behind the second characteristic. For the moment we
are simply concerned with the identication of component responsibilities.

3.5 CRC Cards{Recording Responsibility


In order to discover components and their responsibilities, the programming team walks
through scenarios. That is, the team acts out the running of the application just as if it
42 CHAPTER 3. OBJECT-ORIENTED DESIGN

already possessed a working system. Every activity that must take place is identied and
assigned to some component as a responsibility.

Component Name Collaborators


List of
Description of the
other components
responsibilities assigned
to this component

As part of this process, it is often useful to represent components using small index
cards. Written on the face of the card is the name of the software component, the respon-
sibilities of the component, and the names of other components with which the component
must interact. Such cards are sometimes known as CRC (Component, Responsibility, Collab-
orator) cards Beck 1989, Bellin 97], and are associated with each software component. As
responsibilities for the component are discovered, they are recorded on the face of the CRC
card.
3.5.1 Give Components a Physical Representation
While working through scenarios, it is useful to assign CRC cards to dierent members
of the design team. The member holding the card representing a component records the
responsibilities of the associated software component, and acts as the \surrogate" for the
software during the scenario simulation. He or she describes the activities of the software
system, passing \control" to another member when the software system requires the services
of another component.
An advantage of CRC cards is that they are widely available, inexpensive, and erasable.
This encourages experimentation, since alternative designs can be tried, explored, or aban-
doned with little investment. The physical separation of the cards encourages an intuitive
understanding of the importance of the logical separation of the various components, helping
to emphasize the cohesion and coupling (which we will describe shortly). The constraints
of an index card are also a good measure of approximate complexity{a component that is
3.5. CRC CARDS{RECORDING RESPONSIBILITY 43

expected to perform more tasks than can t easily in this space is probably too complex, and
the team should nd a simpler solution, perhaps by moving some responsibilities elsewhere
to divide a task between two or more new components.

3.5.2 The What/Who Cycle


As we noted at the beginning of this discussion, the identication of components takes place
during the process of imagining the execution of a working system. Often this proceeds
as a cycle of what/who questions. First, the programming team identies what activity
needs to be performed next. This is immediately followed by answering the question of who
performs the action. In this manner, designing a software system is much like organizing a
collection of people, such as a club. Any activity that is to be performed must be assigned
as a responsibility to some component.
A popular bumper sticker states that phenomena can and will spontaneously occur. (The
bumper sticker uses a slightly shorter phrase.) We know, however, that in real life this is
seldom true. If any action is to take place, there must be an agent assigned to perform
it. Just as in the running of a club any action to be performed must be assigned to some
individual, in organizing an object-oriented program all actions must be the responsibility
of some component. The secret to good object-oriented design is to rst establish an agent
for each action.

3.5.3 Documentation
At this point the development of documentation should begin. Two documents should be
essential parts of any software system: the user manual and the system design documen-
tation. Work on both of these can commence even before the rst line of code has been
written.
The user manual describes the interaction with the system from the user's point of
view it is an excellent means of verifying that the development team's conception of the
application matches the client's. Since the decisions made in creating the scenarios will
closely match the decisions the user will be required to make in the eventual application,
the development of the user manual naturally dovetails with the process of walking through
scenarios.
Before any actual code has been written, the mindset of the software team is most similar
to that of the eventual users. Thus, it is at this point that the developers can most easily
anticipate the sort of questions to which a novice user will need answers.
The second essential document is the design documentation. The design documentation
records the major decisions made during software design, and should thus be produced when
these decisions are fresh in the minds of the creators, and not after the fact when many of
the relevant details will have been forgotten. It is often far easier to write a general global
description of the software system early in the development. Too soon, the focus will move
to the level of individual components or modules. While it is also important to document
44 CHAPTER 3. OBJECT-ORIENTED DESIGN

the module level, too much concern with the details of each module will make it dicult for
subsequent software maintainers to form an initial picture of the larger structure.
CRC cards are one aspect of the design documentation, but many other important
decisions are not re ected in them. Arguments for and against any major design alternatives
should be recorded, as well as factors that in uenced the nal decisions. A log or diary of
the project schedule should be maintained. Both the user manual and the design documents
are rened and evolve over time in exactly the same way the software is rened and evolves.

3.6 Components and Behavior


To return to the IIKH, the team decides that when the system begins, the user will be
presented with an attractive informative window (see Figure 3.1). The responsibility for
displaying this window is assigned to a component called the Greeter. In some as yet
unspecied manner (perhaps by pull-down menus, button or key presses, or use of a pressure-
sensitive screen), the user can select one of several actions. Initially, the team identies just
ve actions:
1. Casually browse the database of existing recipes, but without reference to any partic-
ular meal plan.
2. Add a new recipe to the database.
3. Edit or annotate an existing recipe.
4. Review an existing plan for several meals.
5. Create a new plan of meals.
These activities seem to divide themselves naturally into two groups. The rst three are
associated with the recipe database the latter two are associated with menu plans. As a re-
sult, the team next decides to create components corresponding to these two responsibilities.
Continuing with the scenario, the team elects to ignore the meal plan management for the
moment and move on to rene the activities of the Recipe Database component. Figure 3.2
shows the initial CRC card representation of the Greeter.
Broadly speaking, the responsibility of the recipe database component is simply to main-
tain a collection of recipes. We have already identied three elements of this task: The
recipe component database must facilitate browsing the library of existing recipes, editing
the recipes, and including new recipes in the database.

3.6.1 Postponing Decisions


There are a number of decisions that must eventually be made concerning how best to let
the user browse the database. For example, should the user rst be presented with a list of
3.6. COMPONENTS AND BEHAVIOR 45

Greeter Collaborators
Database Manager
Display Informative Initial Message
Plan Manager
Oer User Choice of Options
Pass Control to either
Recipe Database Manager
Plan Manager for processing

Figure 3.2: { CRC card for the Greeter.

categories, such as \soups," \salads," \main meals," and \desserts"? Alternatively, should
the user be able to describe keywords to narrow a search, perhaps by providing a list of
ingredients, and then see all the recipes that contain those items (\Almonds, Strawberries,
Cheese"), or a list of previously inserted keywords (\Bob's favorite cake")? Should scroll
bars be used or simulated thumb holes in a virtual book? These are fun to think about,
but the important point is that such decisions do not need to be made at this point (see
Section 3.6.2, \Preparing for Change"). Since they aect only a single component, and do
not aect the functioning of any other system, all that is necessary to continue the scenario
is to assert that by some means the user can select a specic recipe.

3.6.2 Preparing for Change


It has been said that all that is constant in life is the inevitability of uncertainty and
change. The same is true of software. No matter how carefully one tries to develop the
initial specication and design of a software system, it is almost certain that changes in the
user's needs or requirements will, sometime during the life of the system, force changes to
be made in the software. Programmers and software designers need to anticipate this and
plan accordingly.
46 CHAPTER 3. OBJECT-ORIENTED DESIGN

 The primary objective is that changes should aect as few components as possible.
Even major changes in the appearance or functioning of an application should be
possible with alterations to only one or two sections of code.
 Try to predict the most likely sources of change and isolate the eects of such changes
to as few software components as possible. The most likely sources of change are
interfaces, communication formats, and output formats.
 Try to isolate and reduce the dependency of software on hardware. For example, the
interface for recipe browsing in our application may depend in part on the hardware
on which the system is running. Future releases may be ported to dierent platforms.
A good design will anticipate this change.
 Reducing coupling between software components will reduce the dependence of one
upon another, and increase the likelihood that one can be changed with minimal eect
on the other.
 In the design documentation maintain careful records of the design process and the
discussions surrounding all major decisions. It is almost certain that the individuals
responsible for maintaining the software and designing future releases will be at least
partially dierent from the team producing the initial release. The design documen-
tation will allow future teams to know the important factors behind a decision and
help them avoid spending time discussing issues that have already been resolved.

3.6.3 Continuing the Scenario


Each recipe will be identied with a specic recipe component. Once a recipe is selected,
control is passed to the associated recipe object. A recipe must contain certain information.
Basically, it consists of a list of ingredients and the steps needed to transform the ingredients
into the nal product. In our scenario, the recipe component must also perform other
activities. For example, it will display the recipe interactively on the terminal screen. The
user may be given the ability to annotate or change either the list of ingredients or the
instruction portion. Alternatively, the user may request a printed copy of the recipe. All
of these actions are the responsibility of the Recipe component. (For the moment, we will
continue to describe the Recipe in singular form. During design we can think of this as a
prototypical recipe that stands in place of a multitude of actual recipes. We will later return
to a discussion of singular versus multiple components.)
Having outlined the actions that must take place to permit the user to browse the
database, we return to the recipe database manager and pretend the user has indicated
a desire to add a new recipe. The database manager somehow decides in which category
to place the new recipe (again, the details of how this is done are unimportant for our
development at this point), requests the name of the new recipe, and then creates a new
recipe component, permitting the user to edit this new blank entry. Thus, the responsibilities
3.6. COMPONENTS AND BEHAVIOR 47

of performing this new task are a subset of those we already identied in permitting users
to edit existing recipes.
Having explored the browsing and creation of new recipes, we return to the Greeter and
investigate the development of daily menu plans, which is the Plan Manager's task. In some
way (again, the details are unimportant here) the user can save existing plans. Thus, the
Plan Manager can either be started by retrieving an already developed plan or by creating
a new plan. In the latter case, the user is prompted for a list of dates for the plan. Each
date is associated with a separate Date component. The user can select a specic date for
further investigation, in which case control is passed to the corresponding Date component.
Another activity of the Plan Manager is printing out the recipes for the planning period.
Finally, the user can instruct the Plan Manager to produce a grocery list for the period.
The Date component maintains a collection of meals as well as any other annotations
provided by the user (birthday celebrations, anniversaries, reminders, and so on). It prints
information on the display concerning the specied date. By some means (again unspecied),
the user can indicate a desire to print all the information concerning a specic date or choose
to explore in more detail a specic meal. In the latter case, control is passed to a Meal
component.
The Meal component maintains a collection of augmented recipes, where the augmenta-
tion refers to the user's desire to double, triple, or otherwise increase a recipe. The Meal
component displays information about the meal. The user can add or remove recipes from
the meal, or can instruct that information about the meal be printed. In order to discover
new recipes, the user must be permitted at this point to browse the recipe database. Thus,
the Meal component must interact with the recipe database component. The design team
will continue in this fashion, investigating every possible scenario. The major category of
scenarios we have not developed here is exceptional cases. For example, what happens if a
user selects a number of keywords for a recipe and no matching recipe is found? How can
the user cancel an activity, such as entering a new recipe, if he or she decides not to con-
tinue? Each possibility must be explored, and the responsibilities for handling the situation
assigned to one or more components.
Having walked through the various scenarios, the software design team eventually decides
that all activities can be adequately handled by six components (Figure 3.3). The Greeter
needs to communicate only with the Plan Manager and the Recipe Database components.
The Plan Manager needs to communicate only with the Date component and the Date
agent, only with the Meal component. The Meal component communicates with the Recipe
Manager and, through this agent, with individual recipes.

3.6.4 Interaction Diagrams


While a description such as that shown in Figure 3.3 may describe the static relationships
between components, it is not very good for describing their dynamic interactions during the
execution of a scenario. A better tool for this purpose is an interaction diagram. Figure 3.4
shows the beginning of an interaction diagram for the interactive kitchen helper. In the
48 CHAPTER 3. OBJECT-ORIENTED DESIGN

 
Greeter
 H
 ; 
;
HH
H 
Plan Manager Recipe Database
 @   Q 
@  Q
Date   Q
Q
Q 
 H Meal
HH
Recipe
  
Figure 3.3: { Communication between the six components in the IIKH.

diagram, time moves forward from the top to the bottom. Each component is represented by
a labeled vertical line. A component sending a message to another component is represented
by a horizontal arrow from one line to another. Similarly, a component returning control
and perhaps a result value back to the caller is represented by an arrow. (Some authors use
two dierent arrow forms, such as a solid line to represent message passing and a dashed
line to represent returning control.) The commentary on the right side of the gure explains
more fully the interaction taking place.
With a time axis, the interaction diagram is able to describe better the sequencing of
events during a scenario. For this reason, interaction diagrams can be a useful documenta-
tion tool for complex software systems.

3.7 Software Components


In this section we will explore a software component in more detail. As is true of all but
the most trivial ideas, there are many aspects to this seemingly simple concept.

3.7.1 Behavior and State


We have already seen how components are characterized by their behavior, that is, by
what they can do. But components may also hold certain information. Let us take as
our prototypical component a Recipe structure from the IIKH. One way to view such a
component is as a pair consisting of behavior and state.
 The behavior of a component is the set of actions it can perform. The complete
description of all the behavior for a component is sometimes called the protocol. For the
3.7. SOFTWARE COMPONENTS 49

Greeter Database Recipe Planner Comment


- Message browse()
- Message display()
Return from display()
Return from browse()
- Message makePlan()

Figure 3.4: { An Example interaction diagram.

Recipe component this includes activities such as editing the preparation instructions,
displaying the recipe on a terminal screen, or printing a copy of the recipe.
 The state of a component represents all the information held within it. For our Recipe
component the state includes the ingredients and preparation instructions. Notice
that the state is not static and can change over time. For example, by editing a recipe
(a behavior) the user can make changes to the preparation instructions (part of the
state).
It is not necessary that all components maintain state information. For example, it
is possible that the Greeter component will not have any state since it does not need to
remember any information during the course of execution. However, most components will
consist of a combination of behavior and state.

3.7.2 Instances and Classes


The separation of state and behavior permits us to clarify a point we avoided in our earlier
discussion. Note that in the real application there will probably be many dierent recipes.
However, all of these recipes will perform in the same manner. That is, the behavior of each
recipe is the same it is only the state{the individual lists of ingredients and instructions
for preparation{that diers between individual recipes. In the early stages of development
our interest is in characterizing the behavior common to all recipes the details particular
to any one recipe are unimportant.
50 CHAPTER 3. OBJECT-ORIENTED DESIGN

The term class is used to describe a set of objects with similar behavior. We will see
in later chapters that a class is also used as a syntactic mechanism in almost all object-
oriented languages. An individual representative of a class is known as an instance. Note
that behavior is associated with a class, not with an individual. That is, all instances of
a class will respond to the same instructions and perform in a similar manner. On the
other hand, state is a property of an individual. We see this in the various instances of the
class Recipe. They can all perform the same actions (editing, displaying, printing) but use
dierent data values.

3.7.3 Coupling and Cohesion


Two important concepts in the design of software components are coupling and cohesion.
Cohesion is the degree to which the responsibilities of a single component form a meaningful
unit. High cohesion is achieved by associating in a single component tasks that are related
in some manner. Probably the most frequent way in which tasks are related is through
the necessity to access a common data area. This is the overriding theme that joins, for
example, the various responsibilities of the Recipe component.
Coupling, on the other hand, describes the relationship between software components.
In general, it is desirable to reduce the amount of coupling as much as possible, since
connections between software components inhibit ease of development, modication, or
reuse.
In particular, coupling is increased when one software component must access data
values{the state{held by another component. Such situations should almost always be
avoided in favor of moving a task into the list of responsibilities of the component that
holds the necessary data. For example, one might conceivably rst assign responsibility
for editing a recipe to the Recipe Database component, since it is while performing tasks
associated with this component that the need to edit a recipe rst occurs. But if we did
so, the Recipe Database agent would need the ability to directly manipulate the state (the
internal data values representing the list of ingredients and the preparation instructions) of
an individual recipe. It is better to avoid this tight connection by moving the responsibility
for editing to the recipe itself.

3.7.4 Interface and Implementation{Parnas's Principles


The emphasis on characterizing a software component by its behavior has one extremely
important consequence. It is possible for one programmer to know how to use a compo-
nent developed by another programmer, without needing to know how the component is
implemented. For example, suppose each of the six components in the IIKH is assigned to
a dierent programmer. The programmer developing the Meal component needs to allow
the IIKH user to browse the database of recipes and select a single recipe for inclusion in
the meal. To do this, the Meal component can simply invoke the browse behavior associated
with the Recipe Database component, which is dened to return an individual Recipe. This
3.8. FORMALIZE THE INTERFACE 51

description is valid regardless of the particular implementation used by the Recipe Database
component to perform the actual browsing action.
The purposeful omission of implementation details behind a simple interface is known
as information hiding. We say the component encapsulates the behavior, showing only how
the component can be used, not the detailed actions it performs. This naturally leads
to two dierent views of a software system. The interface view is the face seen by other
programmers. It describes what a software component can perform. The implementation
view is the face seen by the programmer working on a particular component. It describes
how a component goes about completing a task.
The separation of interface and implementation is perhaps the most important concept in
software engineering. Yet it is dicult for students to understand, or to motivate. Informa-
tion hiding is largely meaningful only in the context of multiperson programming projects.
In such eorts, the limiting factor is often not the amount of coding involved, but the amount
of communication required between the various programmers and between their respective
software systems. As we will describe shortly, software components are often developed in
parallel by dierent programmers, and in isolation from each other.
There is also an increasing emphasis on the reuse of general-purpose software components
in multiple projects. For this to be successful, there must be minimal and well-understood
interconnections between the various portions of the system. These ideas were captured by
computer scientist David Parnas in a pair of rules, known as Parnas's principles:
 The developer of a software component must provide the intended user with all the
information needed to make eective use of the services provided by the component,
and should provide no other information.
 The developer of a software component must be provided with all the information
necessary to carry out the given responsibilities assigned to the component, and should
be provided with no other information.
A consequence of the separation of interface from implementation is that a program-
mer can experiment with several dierent implementations of the same structure without
aecting other software components.

3.8 Formalize the Interface


We continue with the description of the IIKH development. In the next several steps the
descriptions of the components will be rened. The rst step in this process is to formalize
the patterns and channels of communication.
A decision should be made as to the general structure that will be used to implement
each component. A component with only one behavior and no internal state may be made
into a function{for example, a component that simply takes a string of text and translates
all capital letters to lowercase. Components with many tasks are probably more easily
52 CHAPTER 3. OBJECT-ORIENTED DESIGN

implemented as classes. Names are given to each of the responsibilities identied on the CRC
card for each component, and these will eventually be mapped onto function or procedure
names. Along with the names, the types of any arguments to be passed to the function
are identied. Next, the information maintained within the component itself should be
described. All information must be accounted for. If a component requires some data to
perform a specic task, the source of the data, either through argument or global value, or
maintained internally by the component, must be clearly identied.

3.8.1 Coming up with Names


Careful thought should be given to the names associated with various activities. Shake-
speare has Juliet claiming that a name change does not alter the object being described,1
but certainly not all names will conjure up the same mental images in the listener. As
government bureaucrats have long known, obscure and idiomatic names can make even the
simplest operation sound intimidating. The selection of useful names is extremely impor-
tant, as names create the vocabulary with which the eventual design will be formulated.
Names should be internally consistent, meaningful, preferably short, and evocative in the
context of the problem. Often a considerable amount of time is spent nding just the right
set of terms to describe the tasks performed and the objects manipulated. Far from being
a barren and useless exercise, proper naming early in the design process greatly simplies
and facilitates later steps.
The following general guidelines have been suggested Keller 1990]:
 Use pronounceable names. As a rule of thumb, if you cannot read a name out loud, it
is not a good one.
 Use capitalization (or underscores) to mark the beginning of a new word within a name,
such as \CardReader" or \Card reader," rather than the less readable \cardreader."
 Examine abbreviations carefully. An abbreviation that is clear to one person may
be confusing to the next. Is a \TermProcess" a terminal process, something that
terminates processes, or a process associated with a terminal?
 Avoid names with several interpretations. Does the empty function tell whether some-
thing is empty, or empty the values from the object?
 Avoid digits within a name. They are easy to misread as letters (0 as O, 1 as l, 2 as
Z, 5 as S).
 Name functions and variables that yield Boolean values so they describe clearly the
interpretation of a true or false value. For example, \PrinterIsReady" clearly indicates
1 \What's in a name? That which we call a rose, by any other name would smell as sweet So Romeo
would, were he not Romeo call'd, retain that dear perfection which he owes without that title." Romeo and
Juliet, Act II, Scene 2.
3.9. DESIGNING THE REPRESENTATION 53

Date Collaborators
Plan Manager
Maintain information about specic date
Meal
Date(year, month, day){create new date
DisplayAndEdit(){display date information
in window allowing user to edit entries
BuildGroceryList(List &){add items from
all means to grocery list

Figure 3.5: { Revised CRC card for the Date component.

that a true value means the printer is working, whereas \PrinterStatus" is much less
precise.
 Take extra care in the selection of names for operations that are costly and infrequently
used. By doing so, errors caused by using the wrong function can be avoided.
Once names have been developed for each activity, the CRC cards for each component
are redrawn, with the name and formal arguments of the function used to elicit each behavior
identied. An example of a CRC card for the Date is shown in Figure 3.5. What is not yet
specied is how each component will perform the associated tasks.
Once more, scenarios or role playing should be carried out at a more detailed level to
ensure that all activities are accounted for, and that all necessary information is maintained
and made available to the responsible components.

3.9 Designing the Representation


At this point, if not before, the design team can be divided into groups, each responsible
for one or more software components. The task now is to transform the description of a
component into a software system implementation. The major portion of this process is
54 CHAPTER 3. OBJECT-ORIENTED DESIGN

designing the data structures that will be used by each subsystem to maintain the state
information required to fulll the assigned responsibilities.
It is here that the classic data structures of computer science come into play. The
selection of data structures is an important task, central to the software design process. Once
they have been chosen, the code used by a component in the fulllment of a responsibility
is often almost self-evident. But data structures must be carefully matched to the task at
hand. A wrong choice can result in complex and inecient programs, while an intelligent
choice can result in just the opposite.
It is also at this point that descriptions of behavior must be transformed into algorithms.
These descriptions should then be matched against the expectations of each component
listed as a collaborator, to ensure that expectations are fullled and necessary data items
are available to carry out each process.

3.10 Implementing Components


Once the design of each software subsystem is laid out, the next step is to implement
each component's desired behavior. If the previous steps were correctly addressed, each
responsibility or behavior will be characterized by a short description. The task at this step
is to implement the desired activities in a computer language. In a later section we will
describe some of the more common heuristics used in this process.
If they were not determined earlier (say, as part of the specication of the system),
then decisions can now be made on issues that are entirely self-contained within a single
component. An decision we saw in our example problem was how best to let the user browse
the database of recipes.
As multiperson programming projects become the norm, it becomes increasingly rare
that any one programmer will work on all aspects of a system. More often, the skills a
programmer will need to master are understanding how one section of code ts into a larger
framework and working well with other members of a team.
Often, in the implementation of one component it will become clear that certain infor-
mation or actions might be assigned to yet another component that will act \behind the
scene," with little or no visibility to users of the software abstraction. Such components are
sometimes known as facilitators. We will see examples of facilitators in some of the later
case studies.
An important part of analysis and coding at this point is characterizing and documenting
the necessary preconditions a software component requires to complete a task, and verifying
that the software component will perform correctly when presented with legal input values.
This is establishing the correctness aspect of the algorithms used in the implementation of
a component.
3.11. INTEGRATION OF COMPONENTS 55

3.11 Integration of Components


Once software subsystems have been individually designed and tested, they can be inte-
grated into the nal product. This is often not a single step, but part of a larger process.
Starting from a simple base, elements are slowly added to the system and tested, using
stubs{simple dummy routines with no behavior or with very limited behavior{for the as yet
unimplemented parts.
For example, in the development of the IIKH, it would be reasonable to start integration
with the Greeter component. To test the Greeter in isolation, stubs are written for the Recipe
Database manager and the daily Meal Plan manager. These stubs need not do any more
than print an informative message and return. With these, the component development
team can test various aspects of the Greeter system (for example, that button presses elicit
the correct response). Testing of an individual component is often referred to as unit testing.
Next, one or the other of the stubs can be replaced by more complete code. For example,
the team might decide to replace the stub for the Recipe Database component with the actual
system, maintaining the stub for the other portion. Further testing can be performed until it
appears that the system is working as desired. (This is sometimes referred to as integration
testing.)
The application is nally complete when all stubs have been replaced with working com-
ponents. The ability to test components in isolation is greatly facilitated by the conscious
design goal of reducing connections between components, since this reduces the need for
extensive stubbing.
During integration it is not uncommon for an error to be manifested in one software
system, and yet to be caused by a coding mistake in another system. Thus, testing during
integration can involve the discovery of errors, which then results in changes to some of
the components. Following these changes the components should be once again tested in
isolation before an attempt to reintegrate the software, once more, into the larger system.
Reexecuting previously developed test cases following a change to a software component is
sometimes referred to as regression testing.

3.12 Maintenance and Evolution


It is tempting to think that once a working version of an application has been delivered the
task of the software development team is nished. Unfortunately, that is almost never true.
The term software maintenance describes activities subsequent to the delivery of the initial
working version of a software system. A wide variety of activities fall into this category.
 Errors, or bugs, can be discovered in the delivered product. These must be corrected,
either in patches to existing releases or in subsequent releases.
 Requirements may change, perhaps as a result of government regulations or standard-
ization among similar products.
56 CHAPTER 3. OBJECT-ORIENTED DESIGN

Hardware may change. For example, the system may be moved to dierent platforms,

or input devices, such as a pen-based system or a pressure-sensitive touch screen, may
become available. Output technology may change{for example, from a text-based
system to a graphical window-based arrangement.
 User expectations may change. Users may expect greater functionality, lower cost,
and easier use. This can occur as a result of competition with similar products.
 Better documentation may be requested by users.

A good design recognizes the inevitability of changes and plans an accommodation for
them from the very beginning.

3.13 Chapter Summary


Object-oriented programming begins with object-oriented design. Object-oriented design is
characterized by an emphasis on responsibility, rather than on structure. Responsibility and
behavior are attributes that can be discovered for a software system well before any other
features can be identied. By systematically tracing the behavior of a system, the design of
the software elements ows naturally from the general specication.
A key tool in the characterization of behavior is the idea of scenarios. Developers trace
through the execution of an imaginary system, identifying actions that need to be performed,
and more importantly assigning the responsibilities for these actions to individual software
components. A useful tool in this activity is the CRC card, which is an index card that
records the responsibilities of a software system. As design evolves, the descriptions of the
actions of each component can be rewritten in more precise formats.
Developing a working software systems involves many steps, frequently termed the soft-
ware life cycle. Design and implementation are the rst major steps. Implementation can
be broken into the identication of components, development and testing of components in
isolation, integration of components into larger units, and nally testing of the completed
application. The life of a software system does not, however, halt with the rst completed
applications. Errors are uncovered, requirements change, and hardware modications can
all cause changes in the software system. The management of these changes that come after
the rst release is known as software maintenance.

Study Questions
1. What is the key idea driving object-oriented design?
2. How is the idea of responsibility tied to information hiding?
3. What are some of the characteristics of programming in the small?
3.13. CHAPTER SUMMARY 57

4. How does programming in the large dier from programming in the small?
5. Why is information hiding an important aspect of programming in the large?
6. Why should the design of a software system begin with the characterization of behav-
ior?
7. What is a scenario? How does a scenario help the identication of behaviors?
8. What do the three elds of a CRC card represent?
9. What are some of the advantages of using a physical index card to represent a CRC
card?
10. What is the what-who cycle?
11. Why should the user manual be written before actual coding of an application is
begun?
12. What are the most common sources of change in the requirements for an application
over time? How can some of the diculties inherent in change be mitigated?
13. What information is being conveyed by an interaction diagram?
14. Describe in your own words the following aspects of software components
(a) Behavior and state
(b) Instances and Classes
(c) Coupling and Cohesion
(d) Interface and Implementation
15. What are Parnas's principles of information hiding?
16. What are some guidelines to follow in the selection of names for components, argu-
ments, behaviors, and so on?
17. After design, what are the later stages of the software life cycle?
18. What is software maintenance?
58 CHAPTER 3. OBJECT-ORIENTED DESIGN

Exercises
1. Describe the responsibilities of an organization that includes at least six types of
members. Examples of such organizations are a school (students, teachers, principal,
janitor), a business (secretary, president, worker), and a club (president, vice-president,
member). For each member type, describe the responsibilities and the collaborators.
2. Create a scenario for the organization you described in Exercise 1 using an interaction
diagram.
3. For a common game such as solitaire or twenty-one, describe a software system that
will interact with the user as an opposing player. Example components include the
deck and the discard pile.
4. Describe the software system to control an ATM (Automated Teller Machine). Give
interaction diagrams for various scenarios that describe the most common uses of the
machine.
Part II

Understanding Paradigms

59
Chapter 4

A Paradigm
As we noted in Chapter 1, to a medieval scholar a paradigm was an example sentence, one
that could be used as a model or as an aid in learning a language. You are learning a
new language, the programming language Java. This book will introduce Java by means
of many small paradigms, or example code fragments. You as the reader should examine
these programs carefully, paying close attention to those features that are new or dierent in
comparison to earlier programs. Learning to view programs as a form of literature, that is,
learning how to read programs as well as to write them, is a skill well worth an investment
in time.
Our rst paradigm is shown in Figure 4.1. Line numbers have been provided in comments
along the right side. Like all comments, these are mainly intended to help the human reader,
and are not actually part of the program. While exceedingly short and largely lacking in
interesting functionality (the program prints one line of output and exits), this program
nevertheless exhibits characteristics found in all Java programs, and is therefore a good place
to begin our explorations. In the remainder of this chapter we will analyze the features of
this program from several dierent perspectives.

import java.io. // 1
public class FirstProgram f // 2
public static void main ( String  ] args ) f // 3
System.out.println( "Your first Java program!") // 4
g // 5
g // 6
Figure 4.1: A simple Java program
61
62 CHAPTER 4. A PARADIGM

Figure 4.2: Compiling and Executing a Java Program

4.1 Program Structure


The rst step is to understand the structure of a Java program. As we noted in Chapter 1,
the Java universe is a community populated by objects, and by little else (we will have more
to say about the types of values found in the Java world in the next section). Objects are
all instances of classes, and thus the overall structure of a Java program is simply a series of
class descriptions. In our example program there is a single class, named FirstProgram. The
name of the class (given on line number 2) is important in several respects. First, it will be
the handle we use to create instances of the class. Of more pragmatic concern in running
your rst program, it is also the name used to create the le in which the executable version
of the class will be stored. On most systems this le will be named FirstProgram.class. To
execute the program, this would be the le you hand to the Java interpreter.
Figure 4.2 shows the commands used to compile and execute this rst program on a
computer running the Unix operating system. The text of the program is stored in a le
named FirstProgram.java. The command javac, the java compiler, analyzes this program,
creating the le FirstProgram.class to hold the executable version of the program. The
command ls lists the contents of the directory, showing these two les. The command java,
the java bytecode interpeter, is used to execute the program. The output of the program is
shown immediately after this command.
The names of the applications used to compile and execute a Java program, the steps
needed to invoke these applications, and the location of the output are all features that dier
greatly between platforms. We will not discuss these further here, instead concentrating on
the features of the Java programming language, which will be the same on all platforms.
4.1. PROGRAM STRUCTURE 63

A class consists of a class header and a class body. The header is found on line 2, and
provides the name of the class. The keyword class indicates (both to the compiler, and to
you as a program reader) that this is a new class description, and that the text following
the class keyword should be taken to be the class name.
public class FirstProgram f // 2
...
g // 6
The class body begins with the curly brace at the end of line 2, and terminates with
the matching curly brace on line 6. The Java language places few restrictions on the use of
spaces in programs, and the placement of elements such as curly braces relative to the rest
of the line is largely a matter of personal taste, and consequently the subject of a great deal
of heated debate. I personally like to place my starting brace on the same line as the unit it
is grouping, with the closing brace on a line all by itself. I deviate from this only when an
entire statement group can be placed on a line by itself. Others prefer to place both braces
on separate lines, as in the following:
public class FirstProgram
f
public static void main ( String  ] args )
f
System.out.println( "Your first Java program!")
g
g

Find a style that seems comfortable to you personally, and use it consistently.
Within the body of a class are found a series of members. A member can be either a
data eld, or a method. The former characterize the internal data being held by an object,
while the latter dene the behaviors an instance of the class can perform.
In our initial program there are no data elds, and only one method. This method
is named main, which must be the name used in describing the rst method that will be
invoked when execution commences.
A method is a function. Like a class, a function consists of two parts a function header
and a function body. All function headers have the same form, which can be described as a
sequence of zero or more modiers, a return type, a name, and a list of arguments. Thus,
a prototypical function has the following form:
modifiers return-type function-name ( arguments ) f
sequence-of-statements
g
64 CHAPTER 4. A PARADIGM

The function header for main is given on line number 3 of our example program. It in-
cludes two modiers, public and static. The rst, as we indicated earlier, will be subsequently
discussed in Section 4.4, while the latter is described in Section 4.5. The return type for
this procedure is void, which we will introduce in Section 4.3. The remaining parts are the
name of the function (main) and the list of arguments to the function. The initial function
for execution always takes a single argument, which is an array of string values. We will
have more to say about Java types, such as the types shown in the argument declaration,
in Section 4.3.
public static void main ( String  ] args ) f // 3
...
g // 5
The function body begins with the curly brace on line 3, and ends with the corresponding
curly brace on line 5. Function bodies must always be properly nested within a class
description.
Within a function body are a series of statements, that indicate the actions to be executed
when an instance of the given class is asked to perform the indicated method. In our sample
program, there is one action, which is to print a single line of output. We will discuss this
statement in more detail in the next section.

4.2 The Connection to the Java world


A Java program is never entirely self contained, but must instead execute in the \universe"
provided by the Java run-time system. We see this characteristic exhibited in two ways
in our sample program. The rst connection to the Java world is created by the import
statement in line 1.
import java.io. // 1
This statement makes a portion of the Java library visible to the following class descrip-
tion. The Java run-time library is both powerful and exceedingly large. Because of the
size, portions of the Java universe are only made available when the user explicitly requests
them. The import statement requests that a portion of the Java run-time system related to
input and output should be treated as part of this program.
This particular section of the Java run-time system has been selected because it is where
the object System is dened.1 Among the data elds held by System is one named out, that
holds various features associated with output. Among the behaviors provided by out is the
1 Actually, System is a class, but classes can be considered to be objects in certain respects. The distinction
is not here important.
4.3. TYPES 65

function println, which takes as argument a text string, and uses the argument to print a
single line of output on a standard output area (often called the output console).
System.out.println( "Your first Java program!") // 4
System is one of many useful objects that already populate the Java universe when a
program begins execution. Another example is Math, which is an object that can perform
many common mathematical operations. We will encounter several other system objects in
later examples.

4.3 Types
In addition to a variety of useful objects, the initial Java world contains the descriptions for
a large collection of useful types that programmer can employ in their own code.
The most basic types are integers and real (or oating-point) values. Integer variables
are declared using the keyword int, as in the following, which both declares a new integer
variable and initializes it with a value.
int newVar = 42

Such variables could be declared either as data elds within a class or as local variables
within a function. Floating point values are declared using either the types oat or double.
Another basic type is boolean. A boolean represents a true or false value. Booleans are
produced by the relational operators (less-than, written <, less-than-or-equal, written <=
and so on) as well as the logical operators (and, written &, or, written |, and the like). The
most common use for boolean values are in the test portion of an if or while statement.
The keyword void is used as a type mainly to describe procedures that do not, in fact,
return any value. We see this in our example program, as the return type for the procedure
main:
public static void main ( String  ] args ) f // 3
The argument list for this procedure also illustrates another useful data type provided
by the Java language, the type String. A literal value, for example, has the type String.
String name = "Fred Smith"

Notice that the types int, oat, double and boolean all begin with lower case letters,
while the type String, as well as the majority of other types provided by the Java library,
begin with an upper case letter. The reason for this that the more primitive types (int and
the like) are technically not objects, while all other values are objects. That is, there is no
66 CHAPTER 4. A PARADIGM

class denition that corresponds to int, while there is a class description that denes the
characteristics of the String data type. This is a minor distinction that in rare situations is
important, and one we will return to in a later chapter.
An array in Java is rather dierent from arrays in many other languages. One dierence
is that the array type declaration does not specify the number of elements, or extent, of the
array. We see this in our sample program in line 3:
public static void main ( String  ] args ) f // 3
The parameter value for this procedure is named args, and is an array of string values.
The square brackets in the declaration give us the clue that the value is an array, however
they do not specify the size of the array. Instead, methods provided by the class Array can
be used to access information about the array. For example, consider the following program
that is only slightly more complicated than the rst example:
import java.io.

public class SecondProgram f

public static void main ( String  ] args ) f


if (args.length > 0)
System.out.println("Hello " + args0])
else
System.out.println( "Hello everybody!")
g
g

Here the data eld length is being used to determine the number of values held by the
array named args. If the user entered a command line argument, such as the string \Fred",
the output would be \hello Fred".2
If the size of the array args is larger than zero (that is, if there are array elements) then
the subscript operator is used to access the rst element. The set of legal index values for
an array in Java begin with zero, and extend to the value one smaller than the number of
elements in the array.
Finally, this example shows the use of the + operator with a string value. When at least
one argument to the \addition" operator is a String, the other argument is automatically
converted into a string, and the operation of string catenation is performed. The result
will be a new string value in which the left argument is immediately followed by the right
2 Exactly how command line arguments are entered di ers depending upon which platform you are exe-
cuting your Java programs. You should consult a reference manual for further information.
4.4. ACCESS MODIFIERS 67

argument. This feature is often used to format output. For example, suppose x is an integer
variable, we could display the value of x by means of the following statement:
System.out.println("The value of x is " + x)

There are other interesting features of both strings and the array data type in Java that
we will discuss in subsequent chapters.

4.4 Access Modiers


The modier public appears twice in our example program, rst in line 2 and then again in
line 3.
public class FirstProgram f // 2
public static void main ( String  ] args ) f // 3
This modier is one of a trio that are used to control the accessibility of names. The other
possibilities are protected and private. By controlling access we mean that these modiers
control which objects in a program can make use of a name, and in which portions of a
program that name can appear.
Recall once again our intuitive description of an object-oriented program as a community
populated by many agents, or objects, that interact with each other in order to achieve some
desired objective. Each object has a role to play, and that role is dened by the data values
it holds and the services it can provide to the other objects in the universe.
Those features that are public are the aspects of an object that another object can see
the outward appearance of the object. Any feature that another object might want to use
should be declared as public.
The use of the public modier in front of the class keyword in our rst program indicates
that the entire class description is public is visible to the program loader that gets an
application ready for execution.
public class FirstProgram f // 2
Similarly, the keyword used in front of the function named main indicates that the
function is visible outside of the class. This means that the program loader can not only
see the class FirstProgram, but can see the function inside of the class. This is important,
otherwise the program loader would not be able to execute the function.
public static void main ( String  ] args ) f // 3
68 CHAPTER 4. A PARADIGM

class BankAccount f

private int accountBalance = 0

public void deposit (int amount) f


accountBalance = accountBalance + amount
g

public void withdrawal (int amount) f


accountBalance = accountBalance - amount
g

Figure 4.3: A data eld being hidden using the keyword private

There are frequently times when a programmer desires that features of a class, or entire
classes themselves, be \hidden" from other classes. This means that other objects in the
program universe cannot \see" these features, and since they cannot be seen, they cannot
be manipulated.
Data elds are most commonly hidden. An object might want to hold a data value, and
not let the value be seen by other objects. It can do this by using the keyword private. For
example, the class description shown in Figure 4.3 is a simple model of a bank account.
The bank account object holds an \internal" piece of information, which represents the
account balance. Because the variable holding this value is declared private, other objects
in the program universe are not allowed to directly examine or modify the account balance.
There are, however, two functions that are declared as public. These allow deposits or
withdrawals to be made from the bank account object. Thus, other objects in the simulation
can indirectly modify the value of the balance (by performing a deposit or a withdrawal)
even though they cannot directly set the account balance.
Private features (both data elds and functions) can only be accessed within the bounds
of a class description. A third possibility, termed protected, comes into play when inheritance
is used as a technique to create new types of objects out of an existing, older class description.
A protected eld is one that is still inaccessible to other objects, but is accessible within
the bounds of any subclass (or derived class). We will see examples of this in subsequent
chapters.
4.5. LIFETIME MODIFIERS 69

4.5 Lifetime Modiers


Another important keyword in our rst program is the term static, which appears preceding
the function main. Like the accessibility keywords, the keyword static can be applied both
to data elds and to functions.
public static void main ( String  ] args ) f // 3
There are two ways to envision the meaning of the attributes described using this term.
One way is to imagine that static elds are shared by all instances of a class. That is, no
matter how many similar objects are created, only one manifestation of a static eld will be
created. This one example will be shared by all the instances of the class.
Because a static eld is shared by all instances, it is not part of any one instance. In this
sense it can be imagined to be outside of all instances. (Although accessibility modiers
can still be applied a private but static data eld is outside of all objects, but can only be
accessed from inside the class denition). Since a static data eld is outside of the object
denitions, it also exists no matter how many instances of a class have been created. In
particular, a static eld exists even if no instances of a class have yet been created!
It is for this reason that the main function in any program must be declared to be static.
The main function must exist, even before any instances of the class have been created. The
program loader selects this static function (which must also be public) and runs it. If it
were not declared static, it would be necessary to rst create an instance of the class before
executing the function.

4.6 Chapter Summary


The rst paradigm, or example program, we have examined will print a single line of output
and halt. Although trivial in purpose, the structure of this program is similar to every Java
program we will subsequently encounter. Thus, a good appreciation of this simple example
is a necessary prerequisite to understanding the remainder of this book:
import java.io.

public class FirstProgram f

public static void main (String  ] args ) f


System.out.println ( "Your first Java program!" )
g

g
70 CHAPTER 4. A PARADIGM

 The import statement connects this Java program to the initial Java universe it indi-
cates which portions of the Java run-time system will be used by this program.
 A Java program is a sequence of class descriptions.
 Each class description consists of a class heading and a class body.
 The class heading consists of modiers, the keyword class, and a class name.
 A class body is a sequence of members.
 A member is either a data member, or a method.
 A method is a function, and consists of a function heading and a function body.
 A function heading consists of modiers, the function name, and the argument list.
 A function body is a sequence of statements.
 The modier public indicates attributes (entire classes, or members within a class)
that can be accessed and used by other objects.
 The modier static indicates attributes (data members of methods) that are shared
by all instances of a class. Such elds exist even when no instances of the class have
yet been created.
 The function main must be declared as both public and static, since it must be visible
to the program loader and must exist even before instances of the class have been
created.

Cross References
Other, more extensive paradigms will be introduced in the remaining chapters in this part
of the book.
The keyword protected, discussed brie y in Section 4.4, becomes important when inher-
itance is used to create new classes. Inheritance will be investigated more fully beginning
in Chapter 8. The class String and related facilities will be explored in Chapter 18.

Study Questions
1. What is the original meaning of the word paradigm?
2. What is the overall structure of a Java program?
3. What are the two major parts of a class description?
4.6. CHAPTER SUMMARY 71

4. What are the two types of members that can be found within a class body?
5. What are the parts of a function header?
6. What operation is performed by System.out.println?
7. What is the type void mainly used for?
8. What does the dierence in case in the initial letter of the types int and String indicate?
9. How in Java does one determine the number of elements held by an array?
10. What is the meaning of the + operator when one of the arguments is a String?
11. What are the three access modier keywords? What does each of them signify?
12. When applied to a data eld, what does the modier static signify?

Exercises
1. Add a member function named display to the class description shown in Figure 4.3.
When invoked, this function should print the current account balance.
2. The looping statement in Java uses the for keyword, and consists of three parts. The
rst part is an initialization statement, which can also be used to declare the loop
variable. The second part is a test for termination the loop will execute as long as
the expression returns true. The nal part is the increment, which is a statement that
is evaluated to update the loop variable.
Consider the following main program. Describe the eect produced by the program
when it is executed with three command line arguments.
public static void main ( String  ] args ) f
for (int i = 0 i < args.length i = i + 1)
System.out.println(argsi])
g

3. Now consider the following slightly more complex program:


public static void main ( String  ] args ) f
for (int i = 0 i < args.length i = i + 1)
for (int j = 0 j <= i j = j + 1)
System.out.println(argsi])
g
72 CHAPTER 4. A PARADIGM

Describe the pattern of the output when the program is executed with three command
line arguments.
4. Consider the following main program:
public static void main ( String  ] args ) f
String result = ""
for (int i = args.length - 1 i >= 0 i = i - 1)
result = result + " " + argsi]
System.out.println(result)
g

What does this function do? What will be the result printed given the arguments Sam
saw Sarah said Sally ?
5. Another useful function provided by the class String is the substr operation. This takes
an integer argument, and returns the portion of the string that remains following
the given index position. For example, if word is a variable containing the string
\unhappy", then word.substr(2) is the string \happy".
This operation is used in the following program. What will the output be given the
command line argument Sally?
static public void main ( String  ] args ) f
String name = args0]
String shortName = name.substr(1)
System.out.println(name + "," + name + ", bo-B" + shortName)
System.out.println("Banana-fana Fo-F" + shortName)
System.out.println("Fee, Fie, mo-M" + shortName)
System.out.println(name + "!")
g

6. By placing the code shown in the previous question inside a loop, write a program
that will take any number of command line arguments, and write one verse of the
name game for each.
Chapter 5

Ball Worlds
In our intuitive description of object-oriented programming presented in Chapter 1, we
described an object-oriented program as a universe of interacting agents. However, in our
rst example Java program, in Chapter 4, we did not actually create any new objects, but
only used the static procedure named main in the program class.
Our second program is slightly more complex in structure, although hardly more com-
plicated in functionality. It places a graphical window on the user's screen, draws a ball
that bounces around the window for a few moments, and then halts.

Our second example program, or paradigm, is constructed out of two classes. The rst
of these appears in Figure 5.1. Again, we have added line numbers for the purposes of
73
74 CHAPTER 5. BALL WORLDS

reference, however these are not part of the actual program. The reader should compare
this program to the example program described in the previous chapter, noting both the
similarities and dierences. Like the previous program, this program imports (on line 1)
information from the Java library. Like the earlier program, execution will begin in the
procedure named main, (lines 3 through 6), which is declared as static, void and public. Like
all main programs, this procedure must take as argument an array of string values, which
are, in this case, being ignored.
However, this program also incorporates a number of new features. These are summa-
rized by the following list, and will be the subject of more detailed discussion in subsequent
sections.
 The class denes a number of private internal variable elds, some of which are con-
stant, some of which are initialized but not constant, and some of which are not
initialized. These data elds will be described in detail in Section 5.1.
 The main program creates an instance of the class BallWorld. This object is initialized
by means of a constructor. A constructor is a function that automatically ties together
the actions of object creation and object initialization. Constructors will be introduced
in Section 5.2.
 The class is declared as an extension of an existing Java class named Frame. This
technique is called inheritance, and is the principal means in object-oriented languages
for constructing new software abstractions that are variations on existing data types.
Inheritance will be introduced in Section 5.3, and will be more extensively studied
beginning in Chapter 8.
 The output displayed in a window by this program is created using some of the graph-
ics primitives provided by the Java run-time library. These graphics operators are
explained in Section 5.4.

5.1 Data Fields


We have seen in the previous chapter (Section 4.4) how data elds can be declared within a
class and how they can be initialized. The example program here includes features we have
not seen in our previous programs in the four data elds declared on lines 7 to 10:
public static final int FrameWidth = 600 // 7
public static final int FrameHeight = 400 // 8
private Ball aBall // 9
private int counter = 0 // 10
Recall that the keyword public means that the variables being declared can be accessed
(that is, used directly) anywhere in a Java program, while those that are declared as private
5.1. DATA FIELDS 75
import java.awt. // 1
public class BallWorld extends Frame f // 2
public static void main (String  ] args) f // 3
BallWorld world = new BallWorld (Color.red) // 4
world.show () // 5
g // 6
public static final int FrameWidth = 600 // 7
public static final int FrameHeight = 400 // 8
private Ball aBall // 9
private int counter = 0 // 10
private BallWorld (Color ballColor) f// constructor for new window 11
// resize our frame, initialize title 12
setSize (FrameWidth, FrameHeight) // 13
setTitle ("Ball World") // 14

// initialize aBall data eld 15


aBall = new Ball (10, 15, 5) // 16
aBall.setColor (ballColor) // 17
aBall.setMotion (3.0, 6.0) // 18
g // 19
public void paint (Graphics g) f // 20
// rst, draw the ball 21
aBall.paint (g) // 22
// then move it slightly 23
aBall.move() // 24
if ((aBall.x() < 0) jj (aBall.x() > FrameWidth)) // 25
aBall.setMotion (-aBall.xMotion(), aBall.yMotion()) // 26
if ((aBall.y() < 0) jj (aBall.y() > FrameHeight)) // 27
aBall.setMotion (aBall.xMotion(), -aBall.yMotion()) // 28
// nally, redraw the frame 29
counter = counter + 1 // 30
if (counter < 2000) repaint() // 31
else System.exit(0) // 32
g // 33
g // 34

Figure 5.1: Class Description for Ball World


76 CHAPTER 5. BALL WORLDS

can be used only within the bounds of the class description in which the declaration appears.
Recall also that the keyword static means that there is one instance of the data eld, shared
by all instances of the class. The modier keyword nal generally means that this is the last
time when an object is changed. It is here applied to a variable declaration we will in later
chapters see how the modier can also be applied to a function denition. When used with
a variable declaration, the declaration must also include an initialization, as shown here.
Variables that are static and nal are used to create symbolic names for constants, as
they are variables that are guaranteed to exist only in one place, and not change values.
Because they cannot be modied, there is less reason to encapsulate a static nal variable by
declaring it private. Thus, such values are often made public, as shown here. The particular
symbolic values being dened in this program represent the height and width of the window
in which the application will eventually produce its output. Symbolic constants are useful
in programs for a number of dierent reasons:
 By being dened in only one place, they make it easy to subsequently change, should
circumstances require. For example, changing the height and or width of the window
merely requires editing the le to change the values being used to initialize these
symbolic constants, rather than hunting down all locations in the code where the
quantities are used.
 When subsequently used elsewhere in the program, the symbolic name helps document
the purpose of the constant values.
The counter data eld is an integer value, initialized to zero:
private int counter = 0 // 10
Because the eld is declared private we know it can be used only within the bounds of the
class denition. Because it was not declared static, we know that each instance of the class
will hold its own dierent value. Because it was not declared as nal we know that the value
being assigned is simply the initial value the variable will hold, but that it subsequently
could be reassigned. We will see how this variable is used when we discuss the graphical
aspects of the current program.
private Ball aBall // 9
The nal data eld is declared as an instance of class Ball, which is the second class used
in the creation of our example program. A ball is an abstraction that represents a bouncing
ball. It is represented by a colored circle that can move around the display surface. The
class Ball will be described in Section 5.5. How this eld is initialized is described in the
next section.
5.2. CONSTRUCTORS 77

5.2 Constructors
As we noted at the beginning of the chapter, one of the major topics we address in this
chapter is the creation of new objects. This occurs in two places in the program shown in
Figure 5.1. The rst is in the main program, which creates an instance of the class BallWorld.
BallWorld world = new BallWorld (Color.red) // 4
The new operator is always used to create a new instance of a class. In this case, it is
being used to create an instance of BallWorld, which (we will see in the next section) is the
name given to the window in which the program will display its output. The new operator
is followed by a class name, indicating the type of object being created. A parenthesized
list then gives any arguments needed in the initialization of the object.
Object creation and object initialization are intimately tied in concept, and it is impor-
tant that a programming language also bring these concepts together. Without support
from the programming language, two types of errors can easily occur:
 An object is created, but it is used before it is ever initialized.
 An object is created, and initialized multiple times before it is used.

The language Java uses a concept called a constructor to guarantee that objects are
placed into a proper initial state the moment they are created. A constructor bears a strong
resemblance to a function, however the name of the function matches the name of the class in
which it appears, the function does not specify a return type, and the user will never directly
execute the constructor. However, like a function, a constructor can have arguments, and
the body of the constructor consists of a sequence of statements. In our example program
the constructor occurs in lines 11 to 19:
private BallWorld (Color ballColor) f// constructor for new window 11
// resize our frame, initialize title 12
setSize (FrameWidth, FrameHeight) // 13
setTitle ("Ball World") // 14

// initialize aBall data eld 15


aBall = new Ball (10, 15, 5) // 16
aBall.setColor (ballColor) // 17
aBall.setMotion (3.0, 6.0) // 18
g // 19
When an object is created (via the new statement), the rst function invoked using the
newly created object is the constructor function. The arguments passed to the constructor
are the arguments supplied in the new expression.
78 CHAPTER 5. BALL WORLDS

In this particular case, the argument represents a color. The class Color is part of the
Java run-time library. The value red is simply a constant (a value declared both as static
and nal) in the class description of Color.
BallWorld world = new BallWorld (Color.red) // 4
The corresponding parameter value in the constructor function is named ballColor (see
line 11). The constructor function must ensure that the instance of the class BallWorld
is properly initialized. As we noted earlier, the BallWorld represents the window in which
the output will be displayed. The rst two statements in the constructor set some of the
attributes for this window namely, the size and the title.
Line 17 of the constructor again uses the new operator to create and initialize a new
object. In this case the object is an instance of the class Ball. Not only will memory
for this object be created by the new statement, but the arguments will be matched by a
corresponding constructor in the class Ball, which will then be invoked to initialize the newly
created ball:
public class Ball f // a generic round colored object that moves
...
public Ball (int x, int y, int r) f // ball with given center and radius
...
g
g

The complete class description for Ball will be shown in Figure 5.2 (page 82). Not all aspects
of a Ball are set by the constructor. The nal two statements in the constructor for BallWorld
set the color of the ball, and set the direction of motion for the ball. These attributes will
be discussed in more detail in Section 5.5.

5.3 Inheritance
The most important feature of this program is the use of inheritance (sometimes also called
extension). As we noted earlier, the ball world is a rectangular window in which the action
of the program (the bouncing ball) is displayed. The code needed to display and manipulate
a window in a modern graphical user interface is exceedingly complex due in part to the
fact that the user can indicate actions such as moving, resizing, or iconifying the window.
As a consequence, recent languages attempt to provide a means of reusing existing code so
that the programmer need only be concerned with those features of the application that
distinguish the program from other window applications.
The programming language Java uses the class Frame to represent a generic window.
By saying that the class BallWorld extends the class Frame, we indicate that our new class,
5.4. THE JAVA GRAPHICS MODEL 79

the ball world, is a type of frame, but a more specialized type with a single purpose. The
class Frame denes code to perform actions such as resizing the window, arranging for the
window to be displayed on the workstation screen, and so on. By extending the class Frame,
our new class inherits this functionality, which means the abilities are made available to the
new class, and do not need to be rewritten anew.
public class BallWorld extends Frame f // 2
By executing the example program the reader can verify that the window exhibits the
functionality we expect of graphical windows the ability to move, resize, and iconify, even
though the program does not explicitly dene any code to support these behaviors. (The
reader might also note some expected behaviors that are not provided. For example, the
handling of menu items and the close or quit box. In a later chapter we will describe how
these features can be provided.)
We can observe the use of inheritance in the variety of methods that are invoked in our
example program, but are not dened by the class BallWorld. These functions are instead
inherited from the parent class Frame. Two examples are the functions setSize and setTitle
invoked in the BallWorld constructor. These functions set the dimensions (in pixels) and
title value for the window, respectively.
private BallWorld (Color ballColor) f// constructor for new window 11
// resize our frame, initialize title 12
setSize (FrameWidth, FrameHeight) // 13
setTitle ("Ball World") // 14
...
g // 19
Another example is the function show, which is invoked in the static procedure main after
the instance of BallWorld has been created. The show function arranges for the window to
appear on the display surface, and then for the surface of the window to be drawn.
public static void main (String  ] args) f // 3
BallWorld world = new BallWorld (Color.red) // 4
world.show () // 5
g // 6

5.4 The Java Graphics Model


Graphics in Java is provided as part of the AWT, or the Abstract Windowing Toolkit. The
Java AWT is an example of a software framework. The idea of a framework is to provide
80 CHAPTER 5. BALL WORLDS

the structure of a program, but no application specic details. The overall control, the ow
of execution, is provided by the framework, and therefore does not need to be rewritten for
each new program. Thus, the programmer does not \see" the majority of the program code.
This is illustrated by the actions that occur subsequent to the program issuing the show
method that is inherited from the class Frame. The window in which the action will take
place is created, and the image of the window must be rendered. To do so, the show method
invokes a function named paint, passing as argument a graphics object.
The programmer therefore denes the appearance of the window by providing an im-
plementation of the function paint. The graphics object passed as argument provides the
ability to draw a host of items, such as lines and polygons as well as text. In our example
program we use the paint procedure for two purposes. The only image in the window itself
is the bouncing ball. The image of the ball is produced by invoking the paint method in the
class Ball (see Figure 5.2). The second purpose of the paint method is to provide a simple
means of updating the location for the ball. The ball is moved slightly, checking to see if
the resulting new location is outside the bounds of the window. If so, the direction of the
ball is reversed. Finally, invoking the repaint method (also inherited from Frame) indicates
to the framework that the window should be redrawn, and the cycle continues.1 A counter
is used to prevent the program from running indenitely, invoking the function System.exit
after a certain number of iterations. (Later programs will use other techniques to halt the
program).
public void paint (Graphics g) f // 20
// rst, draw the ball 21
aBall.paint (g) // 22
// then move it slightly 23
aBall.move() // 24
if ((aBall.x() < 0) jj (aBall.x() > FrameWidth)) // 25
aBall.setMotion (-aBall.xMotion(), aBall.yMotion()) // 26
if ((aBall.y() < 0) jj (aBall.y() > FrameHeight)) // 27
aBall.setMotion (aBall.xMotion(), -aBall.yMotion()) // 28
// nally, redraw the frame 29
counter = counter + 1 // 30
if (counter < 2000) repaint() // 31
else System.exit(0) // 32
g // 33

1 Some readers might object that the control of the animation has little to do with the rendering of the
image on the window, and thus does not belong in the paint routine. While there is merit to this argument,
this is also the simplest way to make simple animations. In later chapters we will present more robust ways
to control animations.
5.5. THE CLASS BALL 81

Note that the programmer calls the inherited method named repaint, which in turn will
eventually result in the paint method being invoked. The programmer does not directly call
the paint method for the class.
In later examples we will investigate more of the abilities of the graphics objects provided
by the Java library.

5.5 The class Ball


We will use a ball, that is, round colored object that moves, in a number of our subsequent
example programs. It is therefore useful to dene the behavior of a Ball in a general fashion
so that it can be used in a variety of ways. The description of class Ball is placed in its
own le (Ball.java) and is linked together with the BallWorld class to create the executable
program.
A Ball (Figure 5.2) maintains four data elds. The location of the ball is represented
by a Rectangle, a general purpose class provided in the Java run-time library. Two oating
point values represent the horizontal and vertical components of the direction of motion for
the ball. Finally, the color of the ball is represented by an instance of class Color, a Java
library class we have previously encountered.
These four data elds are declared as protected. This allows any subsequent child classes
we might create to have access to the data elds, without exposing the data to modication
by other objects. It is good practice to declare data elds protected, rather than private,
even if you do not anticipate extending the class to make new classes.
The constructor for the class Ball records the location by creating a new instance of class
Rectangle. Note that the three integer arguments passed as arguments to the constructor
represent the center location of the ball and the radius: a simple calculation is used to
convert these to the corner of the rectangle and the extent. The constructor also provides
default values for color (blue) and motion. As we have seen in our example program, these
can be redened by invoking the functions setColor and setMotion.
A number of functions are used to access some of the attributes of a ball. Attributes that
can be obtained in this fashion include the radius, the x and y coordinate of the center of the
ball, the horizontal and vertical directions of motion, and the region occupied by the ball.
Functions that allow access to a data eld in a class are termed accessor functions. The use of
accessor functions is strongly encouraged in preference to making the data elds themselves
public, as an accessor function only permits the value to be read, and not modied. This
ensures that any modication to a data eld will be mediated by the proper function, such
as through the function setMotion or moveTo.
Some of the functions use operations provided by the class Rectangle. A rectangle can
provide a width (used in function radius), the location of the upper corner (used in functions
x and y), can move to a new position (used in function moveTo), and can transliterate on
the 2-dimensional surface (used in the function move).
Finally, the function paint uses two operations that are provided by the class Graphics
82 CHAPTER 5. BALL WORLDS
public class Ball f // a generic round colored object that moves
protected Rectangle location // position on graphic surface
protected double dx, dy // x and y components of motion vector
protected Color color // color of ball

public Ball (int x, int y, int r) f // ball with given center and radius
location = new Rectangle(x-r, y-r, 2r, 2r)
dx = 0 dy = 0 // initially no motion
color = Color.blue
g

// functions that set attributes


public void setColor (Color newColor) f color = newColor g

public void setMotion (double ndx, double ndy) f dx = ndx dy = ndy g

// functions that access attributes of ball


public int radius () f return location.width / 2 g

public int x () f return location.x + radius() g

public int y () f return location.y + radius() g

public double xMotion () f return dx g

public double yMotion () f return dy g

public Rectangle region () f return location g

// functions that change attributes of ball


public void moveTo (int x, int y) f location.setLocation (x, y) g

public void move () f location.translate ((int) dx, (int) dy) g

public void paint (Graphics g) f


g.setColor (color)
g.fillOval
(location.x, location.y, location.width, location.height)
g
g

Figure 5.2: Implementation of the class Ball


5.6. MULTIPLE OBJECTS OF THE SAME CLASS 83

in the Java library. These are the ability to set the current color for rendering graphics
(setColor) and to display a painted oval at a given location on the window (llOval).

5.6 Multiple Objects of the Same Class


Every instance of a class maintains its own internal data elds. We can illustrate this
by making variations on our sample program. The simplest change is to modify the main
routine to create two independent windows. Each window will have a dierent ball, each
window can be independently moved or resized.
public static void main (String  ] args) f
// create rst window with red ball
BallWorld world = new BallWorld (Color.red)
world.show()
// now create a second window with yellow ball
BallWorld world2 = new BallWorld (Color.yellow)
world2.show()
g

The reader should try making this change, and observe the result. Note how one window
is bouncing a red ball, and the second is bouncing a yellow ball. This indicates that each
instance of class BallWorld must be maintaining its own Ball value, as a ball cannot be both
red and yellow at the same time.
A second variation illustrates even more dramatically the independence of dierent ob-
jects, even when they derive from the same class. The class MultiBallWorld (Figure 5.3) is
similar to our initial program, only it creates a collection of balls, rather than just a single
ball. Only the lines that have changed are included, and those that are elided are the same
as the earlier program. The new program declares an array of Balls, rather than just a
single ball. Note the syntax used to declare an array. As we noted in the previous chapter,
arrays in Java are dierent from arrays in most other languages. Even though the array is
declared, space is still not set aside for the array elements. Instead, the array itself must be
created (again with a new command):
ballArray = new Ball  BallArraySize ]

Note how the size of the array is specied by a symbolic constant, dened earlier in the
program. Even then, however, the array elements cannot be accessed. Instead, each array
element must be individually created, once more using a new operation:
for (int i = 0 i < BallArraySize i++) f
ballArrayi] = new Ball(10, 15, 5)
84 CHAPTER 5. BALL WORLDS

public class MultiBallWorld extends Frame f

...
private Ball  ] ballArray
private static final int BallArraySize = 10

private MultiBallWorld (Color ballColor) f


...
// initialize object data eld
ballArray = new Ball  BallArraySize ]
for (int i = 0 i < BallArraySize i++) f
ballArrayi] = new Ball(10, 15, 5)
ballArrayi].setColor (ballColor)
ballArrayi].setMotion (3.0+i, 6.0-i)
g
g

public void paint (Graphics g) f


for (int i = 0 i < BallArraySize i++) f
ballArrayi].paint (g)
// then move it slightly
ballArrayi].move()
if ((ballArrayi].x() < 0) jj
(ballArrayi].x() > FrameWidth))
ballArrayi].setMotion
(-ballArrayi].xMotion(), ballArrayi].yMotion())
if ((ballArrayi].y() < 0) jj
(ballArrayi].y() > FrameHeight))
ballArrayi].setMotion
(ballArrayi].xMotion(), -ballArrayi].yMotion())
...
g
g
g

Figure 5.3: Class description for Multiple Ball World


5.7. CHAPTER SUMMARY 85

ballArrayi].setColor (ballColor)
ballArrayi].setMotion (3.0+i, 6.0-i)
g

Each ball is created, and initialized with the given color, and set in motion. We have
used the loop index variable to change the direction of motion slightly, so that each ball will
initially move in a dierent direction.
When executed, ten dierent balls will be created. Each ball will maintain its own
location and direction. As each ball is asked to paint it will display its value on the window.
Each ball will then move, independently of all other balls.

5.7 Chapter Summary


The two major themes introduced in this chapter have been the creation of new objects
using the operator new, and the denition of new classes using inheritance to extend an
existing class. Topics discussed in this chapter include the following:
 Data elds which are declared nal cannot be subsequently redened. A static and
nal value is the technique normally used to create a symbolic constant.
 New objects are always created using the operator new.
 When a new object is created, the constructor for the class of the object is automat-
ically invoked as part of the creation process. The constructor should guarantee the
object is properly initialized.
 A constructor is a function that has the same name as the class in which it is dened.
 Any arguments used by the constructor must appear in the new statement that creates
the corresponding object.
 Classes can be dened using inheritance. Such classes extend the functionality of an
existing class. Any public or protected data elds or functions dened in the parent
class become part of the new class.
 The class Frame can be used to create simple Java windows. This class can be extended
to dene application-specic windows.
 The framework provided by the Java AWT displays a frame (a window) when the
frame object is given the message show. To create the image shown in the window the
message paint is used. The programmer can dene this method to produce application-
specic pictures.
 The paint method is given as argument an instance of the library class Graphics. This
object can be used to create a variety of graphical images.
86 CHAPTER 5. BALL WORLDS

 The class Rectangle (used in our class Ball) is a library class that represents a rect-
angular region on the two-dimensional window surface. The class provides a large
amount of useful functionality.
 Multiple instances of the same class each maintain their own separate data elds.
This was illustrated by creating multiple independent Ball objects, which move inde-
pendently of each other.

Cross References
We will use the Ball class in case studies in Chapters 6, 7, 8 and 21. The topic of inheritance
is simple to explain, but has many subtle points that can easily trap the unwary. We will
example inheritance in detail in Chapters 8 through 11. The AWT will be examined in more
detail in Chapter 13.

Study Questions
1. How would you change the color of the ball in our example application to yellow?
2. How would you change the size of the application window to 500 by 300 pixels?
3. What does the modier keyword nal mean when applied in a data eld declaration?
4. Why do symbolic constants make it easier to read and maintain programs?
5. What two actions are tied together by the concept of a constructor?
6. What types of errors does the use of constructors prevent?
7. What does it mean to say that a new class inherits from an existing class?
8. What methods inherited from class Frame are used in our example application?
9. What methods provided by our example program are invoked by the code inherited
from class Frame?
10. What abstraction does the Java library class Rectangle represent?
11. What are some reasons that data elds should be declared as private or protected, and
access provided only through member functions?
12. In Java, what are the steps involved in creating an array of objects?
5.7. CHAPTER SUMMARY 87

Exercises
1. The function Math.random returns a random oating point value between 0 and 1.0.
Using this function, modify the example program shown in Figure 5.1 so that the ball
will initially move in a random direction.
2. Modify the MultiBallWorld so that the colors of the various balls created are selected
randomly from the values red, blue and yellow. (Hint: call Math.random() and test
the resulting value for various ranges, selecting red if the value is in one range, blue if
it is in another, and so on).
3. Modify the MultiBallWorld so that it will produce balls of dierent radiuses, as well as
dierent colors.
4. Rather than testing whether or not a ball has hit the wall in our main program,
we could have used inheritance to provide a specialized form of Ball. Create a class
BoundedBall that inherits from class Ball. The constructor for this class should provide
the height and width of the window, which should subsequently be maintained as data
elds in the class. Rewrite the move function so that if the ball moves outside the
bounds, it automatically reverses direction. Finally, rewrite the BallWorld class to use
an instance of BoundedBall, rather than an ordinary Ball, and eliminate the bounds
test in the main program.
5. Our Ball abstraction is not as simple as it could have been. Separate the Ball class into
two separate classes. The rst, the new class Ball, knows only a location, its size, and
how to paint itself. The second class, MovableBall, extends the class Ball and adds all
behavior related to motion, such as the data elds dx and dy, the functions setMotion
and move, and so on. Rewrite the MultiBallWorld to use instances of class MovableBall.
88 CHAPTER 5. BALL WORLDS
Chapter 6

A Cannon Game
In this chapter we will examine an implementation of a classic \shooting cannon" game. In
this simple game there is a cannon on the left portion of the users window, and a target
on the right portion. The user can control the angle of elevation for the cannon, and re a
cannon ball. The objective of the game is, of course, to hit the target.

As with all the case studies, our objective is not so much the cannon application itself,
which is only moderately interesting, but the use of a simple program to illustrate a number
of dierent features of the Java language. In particular, we will develop two variations on
this game:
 The rst version is the simplest. The user enters the angle of the cannon from the
command argument line, the cannon res once, and the program halts.
 In the second version, we improve user interaction, by providing both a slider with
which the angle of the cannon can be changed, and a button to re the cannon. Thus,
89
90 CHAPTER 6. A CANNON GAME

multiple attempts to hit the target can be made during one execution of the program.

6.1 The Simple Cannon Ball Game


The principle class for our cannon application is shown in Figure 6.1. The main method
is similar to those described in the previous chapters. The universe for our application is
termed CannonGame. An instance of this class is created, then passed the message show.
The message show causes the window in which the application is played to be displayed.
In our rst version of the cannon game the angle for the cannon is read from the command
line argument. The rst string in the command line argument list is assumed to be an integer
value, representing the angle (in degrees) for the cannon to be set prior to launch of the
cannon ball. This value is parsed by a method from the Java library class Integer, and
passed as argument to the constructor for the cannon world. There, the method intValue()
is used to convert an Integer into an int. We will return to a discussion of the relationship
between these two data types in Section 6.1.2.
The cannon world is declared to be a type of Frame, which you will recall from the last
chapter is how Java declares a new type of window. The class description denes two public
constant values (declared as static nal, see Section 5.1) that describe the height and width
of the window that represents the cannon application. In addition, three new data elds are
declared. These are the angle of the cannon, a message that will be displayed in the middle
of the playing window, and a cannon ball. The latter is an instance of class CannonBall,
which we will describe shortly.
The constructor for the class CannonGame resizes the window frame to the declared
bounds, and sets the window title. The argument value is converted from the type Integer
to the built-in type int using the method intValue. The string representing the message is
set to a value that will, when printed, indicate the current angle of the cannon. Finally, the
cannon ball is created using the angle to determine the initial direction of movement. There
are two other methods dened in this class. The method dy is used in the display function,
and will be described shortly. The remaining method paints the window area.
Rendering pictures is complicated by the fact that Java, like almost all windowing sys-
tems, uses a coordinate system that is \upside down". The upper left corner of a window
is the 0,0 coordinate, with x values increasing as they move right, and y values increasing
as they move down.
6.1. THE SIMPLE CANNON BALL GAME 91

class CannonGame extends Frame f

public static void main (String  ] args) f


CannonGame world = new CannonGame (Integer.valueOf(args0]))
world.show ()
g

public static final int FrameWidth = 600


public static final int FrameHeight = 400

// data elds
private int angle = 45
private String message = ""
private CannonBall cannonBall = null

// constructor
public CannonGame (Integer theta) f

setSize (FrameWidth, FrameHeight)


setTitle ("Cannon Game")

// set the local variables


angle = theta.intValue()
message = "Angle = " + angle

// create the cannon ball


double radianAngle = angle  Math.PI / 180.0
double sinAngle = Math.sin(radianAngle)
double cosAngle = Math.cos(radianAngle)
cannonBall = new CannonBall (
20 + (int) (30  cosAngle), dy(5+(int) (30  sinAngle)),
5, 12  cosAngle, -12  sinAngle)
g

public int dy (int y) f return FrameHeight - y g

public void paint (Graphics g) f ... g


g

Figure 6.1: Description of the Cannon World


92 CHAPTER 6. A CANNON GAME

(0, 0) (500, 0)

(0, 300) (500, 300)

However, most people prefer to think in a coordinate system where the 0,0 location is
the bottom left corner, and y values increase as they move up.

(0, 300) (500, 300)

(0, 0) (500, 0)

One way to handle this is to perform a coordinate transformation as the very last opera-
tion before any drawing command. This is the task of the function dy, shown in Figure 6.1.
By subtracting a value from the maximum window size, we can take a value in the more
natural coordinate system, and convert it into a value in the upside down coordinate sys-
tem. An advantage of the function dy is that it is self-inverting applying the function twice
returns the original value. We will use this observation by expressing all our drawing oper-
ations in the zero-centered form, while allowing the ball itself to work in the Java window
coordinates.
6.1. THE SIMPLE CANNON BALL GAME 93

The method to draw the elements in the cannon world is shown in Figure 6.2. Recall
from the previous chapter that drawing is performed by the method paint, which will be
invoked implicitly when the application is shown, and can be requested explicitly by invok-
ing the method repaint. Also recall that drawing commands are provided by the library
class Graphics, an instance of which is supplied to the paint method. The cannon itself
is represented by a rectangle, ten pixels by thirty, with a circular wheel. A bit of simple
math is used to tilt the cannon to the appropriate angle. One complicating factor is that
the application is representing angles in degrees using an integer value, while the sine and
cosine functions in the Math library want a double precision oating point value in radians.
A simple algorithm is used to convert between the two. The target is represented by a red
rectangle. If a cannon ball exists, it is asked to move itself, paint itself at the transformed
location, and if not at the nal target, schedule the picture for another repainting.1 Finally,
the message is printed in the middle of the playing area.

6.1.1 Balls that Respond to Gravity


The class Ball was introduced in Chapter 5. A Ball, you will recall, possessed a radius, a
location, a color, and a direction. The latter was represented by a pair of values, representing
the extent of motion in the x coordinate and the extent of motion in the y coordinate.
A CannonBall is built using inheritance as an extension of class Ball. This means that
CannonBalls have all the properties of a Ball, and also include new properties or alter existing
properties. In this case, a CannonBall changes the move method, simulating the eect of
gravity by reducing the change in the vertical direction by a small amount each update
cycle.
public class CannonBall extends Ball f

public CannonBall (int sx, int sy, int r, double dx, double dy) f
super (sx, sy, r)
setMotion (dx, dy)
g

public void move () f


dy = dy + 0.3 // eect of gravity
super.move() // update the ball position
g
g

1 We remind the reader once more that we are using the paint procedure in this manner merely as a simple
means of animation. Later chapters will demonstrate how to move the control over the animation out of
the paint routine.
94 CHAPTER 6. A CANNON GAME

class CannonGame f
public void paint (Graphics g) f
int x = 20 // location of cannon
int y = 5
double radianAngle = angle  Math.PI / 180.0
int lv = (int) (30  Math.sin(radianAngle))
int lh = (int) (30  Math.cos(radianAngle))
int sv = (int) (10  Math.sin(radianAngle + Math.PI/2))
int sh = (int) (10  Math.cos(radianAngle + Math.PI/2))
// draw cannon
g.setColor(Color.green)
g.drawLine(x, dy(y), x+lh, dy(y+lv))
g.drawLine(x+ln, dy(y+lv), x+lh+sh, dy(y+lv+sv))
g.drawLine(x+lh+sh, dy(y+lv+sv), x+sh, dy(y+sv))
g.drawLine(x+sh, dy(y+sv), x, dy(y))
g.drawOval(x-8, dy(y+10), 12, 12)
// draw target
g.setColor(Color.red)
g.fillRoundRect(FrameWidth-100, dy(12), 50, 10, 6, 6)
// draw cannon ball
if (cannonBall != null) f
cannonBall.move()
cannonBall.paint(g)
if (dy(cannonBall.y()) > 0)
repaint()
else f
int targetX = FrameWidth - 100
if ((cannonBall.x() > targetX) &&
(cannonBall.x() < (targetX + 50)))
message = "You Hit It!"
else
message = "Missed!"
cannonBall = null
g
g
// draw message
g.drawString(message, FrameWidth/2, FrameHeight/2)
g
g

Figure 6.2: Class description for Cannon Game application


6.2. ADDING USER INTERACTION 95

We have not yet encountered the pseudo-variable super, shown here in both functions.
When a method overrides (that is, replaces) a similarly named function in the parent class,
a technique must be provided to indicate that the function should invoke the procedure
inherited from the parent class. The function cannot simply be named, as the name in the
child class and the parent class are the same. Thus, for example, if one was to try to invoke
the move function in class Ball by simply executing the function move, the result would be
an innite series of recursive function calls which is not the outcome we wish.
The pseudo-variable super is used to represent the receiver, but viewed as an instance of
the parent class, not the current class. Thus, by invoking super.move(), the procedure move
is asking that the move function from the parent class be executed, and not the version
overridden by the class CannonBall.
Similarly, the use of the name super as a function in a constructor is used to indicate
that the constructor for the parent class should be invoked, using the arguments shown.
Note how the class CannonBall is invoked with a slightly dierent set of arguments than
were used for the class Ball.

6.1.2 Integers and ints


As we noted in Section 4.3 of Chapter 4, integer values are not actually objects, in the
technical sense of the world. Thus, integers do not have classes, there are no methods
associated with integers, and so on. For each of the non-class primitive types, the Java
library denes a \wrapper" class that can be used as an object. For integers this wrapper
class is named Integer. The class Integer provides a number of useful operations. The one we
utilize in our example program is the ability to take a String (the command line argument),
and convert it into a numeric value. To do this, a string is passed as argument to the
message valueOf:
CannonGame world = new CannonGame (Integer.valueOf(args0]))

The result produced by the message valueOf is a new instance of class Integer, initialized
to the indicated value. The conversion from Integer to int is performed using the message
intValue:
angle = theta.intValue()

6.2 Adding User Interaction


The user interaction in our rst application was very primitive. The user could select
one angle, run the program, and see the result. In our second variation, we improve user
96 CHAPTER 6. A CANNON GAME

interaction by providing the ability to dynamically set the angle of the cannon, and re
several times during one execution.
The conventional way to set a varying quantity, such as the angle of the cannon, is with a
scrollbar. The normal way to indicate that an action should commence, such as the cannon
should be red, is with a button. In our revised game we will incorporate both these items,
placing the button on the top of the screen, and the scroll bar to the right of the playing
area:

The revised game, now named CannonWorld, is shown in Figure 6.3. One feature to
note is that we must now import the Java library java.awt.Event.*, in order to include the
denitions of the Event-handling routines for the Java system. This is in addition to the
java.awt.* library we have been including from the start. Although in code length the amount
of change from the rst version of our program is relatively small, there are a number of
important features that distinguish this program. We will explore these in the following
sections:

6.2.1 Inner classes


One of the more notable features of the program in Figure 6.3 is the declaration of two
new classes within the application class itself. A class declared in such a fashion is known
as an inner class. Modiers used in declaring an inner class match the meanings we have
previously described for data elds and methods thus, an inner class that is declared as
private (such as shown here) can only be used within the outer class in which it is dened.
class CannonWorld extends Frame f
...

private class FireButtonListener implements ActionListener f


...
6.2. ADDING USER INTERACTION 97
import java.awt.Event. // import the event interface
class CannonWorld extends Frame f
...
private String message = "Angle: " + angle
private CannonBall cannonBall = null
private Scrollbar slider

private class FireButtonListener implements ActionListener f


public void actionPerformed (ActionEvent e) f
double radianAngle = angle  Math.PI / 180.0
double sinAngle = Math.sin(radianAngle)
double cosAngle = Math.cos(radianAngle)
// create the cannon ball
cannonBall = new CannonBall (
20 + (int) (30  cosAngle), dy(5+(int) (30  sinAngle)),
5, 12  cosAngle, -12  sinAngle)
repaint()
g
g

private class ScrollBarListener implements AdjustmentListener f


public void adjustmentValueChanged (AdjustmentEvent e) f
angle = slider.getValue()
message = "Angle: " + angle
repaint()
g
g

public CannonWorld () f
setSize (FrameWidth, FrameHeight)
setTitle ("Cannon Game")
// add the scroll bar and button
Button fire = new Button("fire")
fire.addActionListener(new FireButtonListener())
add ("North", fire)
slider = new Scrollbar(Scrollbar.VERTICAL, angle, 5, 0, 90)
slider.addAdjustmentListener(new ScrollBarListener())
add ("East", slider)
g
g

Figure 6.3: Revised Cannon World with Button and Slider


98 CHAPTER 6. A CANNON GAME

private class ScrollBarListener implements AdjustmentListener f


...
g

...
g

Inner classes are allowed to access their surrounding environment. That is, methods
in an inner class can use data elds declared in the surrounding outer class, as well as
invoke methods from the surrounding context. We see this in the method dened in class
ScrollBarListener, which modies both the data elds angle and message.

6.2.2 Interfaces
Both the inner classes created in this example use the keyword implements in their header.
The implementation of an interface is yet another program structuring mechanism, one
that can be understood by comparison to the technique of inheritance we have been using
previously.
An interface in Java is a description of behavior. It is written in a fashion that appears
similar to a class denition, however there are no implementations (function bodies) associ-
ated with methods, and the only data elds permitted must be declared static. An interface
for ActionListener, used in our sample program, might be written as follows:
interface ActionListener f
public void actionPerformed (ActionEvent)
g

The Java library, particularly in those sections that relate to the handling of events (such
as pressing buttons or moving sliders), makes extensive use of interfaces. When a class is
declared as implementing an interface, it is a guarantee (one that is checked by the compiler)
that the class must provide a certain behavior. In this case, an assertion that a class is an
ActionListener means that the class must provide a function named actionPerformed.
The reader should consider carefully the dierence between this and inheritance. Using
inheritance, functions and data elds that are declared in the parent class may be used in
the child class. Thus, the child class inherits the structure of the parent, as well as being
able to mimic the parent behavior. Using an interface, on the other hand, there is no
implementation of the functions in the \parent interface" at all. Instead, the parent simply
denes the names of the functions, which must then be dened in the child class.
Despite the fact that they have no implementation, an interface can be used as a type
name in an argument declared in a function header. The matching parameter value must
6.2. ADDING USER INTERACTION 99

then be an instance of a class that implements the interface. In our example program,
the addActionListener method in class Button expects an argument that implements the
ActionListener interface, and the addAdjustmentListener method in class ScrollBar expects an
argument that implements the AdjustmentListener interface.

6.2.3 The Java Event Model


Modern graphical user interfaces are based around the concept of events. An event is an
action, such as the user clicking the mouse on a button, selecting a menu item, pressing
a key, or inserting a disk into a drive. The program responds to an event by performing
certain actions. Thus, such interfaces are said to be event-driven.
The Java event model is based on the concept of listeners. A listener is an object whose
sole purpose is to sit and wait for an event to occur. When the event does occur, the listener
goes into action and performs the appropriate behavior.
There are two listeners used in our sample program. The rst is waiting for the button
to be pressed. The class Button is one of many graphical elements provided by the Java
library. The string argument passed to the constructor for the class is the text that will
appear on the face of the button. A listener for a button must implement the interface
ActionListener. The listener in our sample program is declared as an instance of the inner
class FireButtonListener, and is attached to a newly created button by the following code:
Button fire = new Button("Fire")
fire.addActionListener(new FireButtonListener())

When the button is pressed, the message actionPerformed will be passed to the listener.
In this case, this message will be handled by the one method in the class body:
private class FireButtonListener implements ActionListener f
public void actionPerformed (ActionEvent e) f
double radianAngle = angle  Math.PI / 180.0
double sinAngle = Math.sin(radianAngle)
double cosAngle = Math.cos(radianAngle)
// create the cannon ball
cannonBall = new CannonBall (
20 + (int) (30  cosAngle), dy(5+(int) (30  sinAngle)),
5, 12  cosAngle, -12  sinAngle)
repaint()
g
g

The argument of type ActionEvent which is passed to the procedure actionPerformed


describes details of the event in more detail. However, the value is ignored, since in this case
100 CHAPTER 6. A CANNON GAME

there is only one event a button can perform that is of any interest, namely the event that
occurs when it is pressed. The action when this occurs is to convert the angle of elevation
for the cannon into radians, create a new cannon ball for ring, and schedule the window
for repainting.
The slider that is used to control the elevation of the cannon is created in a similar
fashion. The constructor for class ScrollBar takes as argument an indication whether the
slidebar is horizontal or vertical, an initial value, and the range of accepted values. The
constructor for our application creates both a new slider and a new listener:
slider = new Scrollbar(Scrollbar.VERTICAL, angle, 5, 0, 90)
slider.addAdjustmentListener(new ScrollBarListener())

The listener must implement the AdjustmentListener interface. When the slider is moved,
the listener is given the message adjustmentValueChanged. Note that, unlike the situation
involving the button, the slide bar must be made available to the listener, so that the current
value of the slide bar can be determined (using the message getValue). It is for this reason
that the slide bar is saved in a data eld, but the button need not be. Once the value of
the slide bar has been determined, the angle and message can be changed, and the window
scheduled for repainting.
private class ScrollBarListener implements AdjustmentListener f
public void adjustmentValueChanged (AdjustmentEvent e) f
angle = slider.getValue()
message = "Angle: " + angle
repaint()
g
g

6.2.4 Window layout


Part of every Java program is a layout manager. The layout manager controls the place-
ment of graphical items in a Java program. By using sophisticated layout managers the
programmer can have a great deal of control over the appearance of a Java window. For
simple programs, however, we can use the default layout manager, which permits values to
be placed on the four sides of the screen. These four portions of the screen are identied as
North (the top) East (the right), West (the left) and South (the bottom). The constructor
for our application places the button on the top of the window, and the slider on the right
hand side.
public CannonWorld () f
...
6.3. CHAPTER SUMMARY 101

// add the scroll bar


Button fire = new Button("fire")
fire.addActionListener(new FireButtonListener())
add ("North", fire)
slider = new ScrollBar(Scrollbar.VERTICAL, angle, 5, 0, 90)
slider.addAdjustmentListener(new ScrollBarListener())
add ("East", slider)
g

In later chapters we will explore a variety of other layout managers.

6.3 Chapter Summary


This chapter has once again made use of a relatively simple application as a vehicle to
introduce a number of new concepts. The following summarize some of the ideas introduced
in this chapter:
 The class Integer, which is a wrapper class that can hold an integer value. Instances of
this class are objects, unlike normal integer values, which are not objects. The class
Integer provides some useful functionality, such as the ability to parse a string value
that holds the textual representation of an integer quantity.
 The use of inheritance in the construction of the class CannonBall, so that the majority
of the behavior for the ball is inherited from the parent class Ball.
 The pseudo-variable super, which when used as a value inside a method refers to the
parent class from which the current class inherits.
 The idea of an inner class, which is a class denition nested within another class de-
nition. Inner classes are allowed full access to the data values and functions provided
by surrounding classes.
 The idea of an interface, which is a means to ensure that classes satisfy certain behav-
ior. An interface denes the names and arguments for member functions, but does not
provide an implementation. A class that declares itself as implementing an interface
must then provide an implementation for these operations. A function can insist that
an argument implement certain functionality, by declaring the argument using the
interface as a type.
 The Java event model, in which listener objects are attached to event producing
objects, such as buttons. When an event occurs, the listener is notied, and performs
whatever action is appropriate.
102 CHAPTER 6. A CANNON GAME

 The graphical component classes Button and Slider, which simplify the creation of
graphical features in a user interface.
 The idea of a window layout manager. In our application program we used the default
layout manager, which is an instance of the class BorderLayout.
Note that as our application has become more complex, we have moved closer to the
idea that an object oriented programming is a \community" of agents that interact with
each other to produce the desired behavior. Instances of following categories of objects are
all used in this example program:
 The class CannonWorld, which inherits from the class Frame provided by the Java
library.
 The class CannonBall, built as an extension of the earlier class Ball developed in Chap-
ter 5.
 The class Integer, used here for its ability to translate a number in text into a numeric
quantity.
 The graphical component classes Button and ScrollBar, and their listener classes Fire-
ButtonListener and ScrollBarListener, the latter two constructed as inner classes within
our application class.
 Instances of the class ActionEvent and AdjustmentEvent, which are created when an
event occurs and carry information to the event listener.
 The layout manager, an instance of class BorderLayout.

6.4 Cross References


Wrapper classes, such as Integer and Double, will be explained in more detail in Chapter 20.
The distinction between inheritance and implementation, and the uses of each, will be a
topic addressed in Chapter 10. Window layouts, layout managers, graphical components,
and other features of the AWT will be examined in more detail in Chapter 13.

Study Questions
1. What is the parent class for class CannonGame?
2. In the rst cannon game, how is the angle of the cannon determined?
3. What transformation is performed by the member function dy?
6.4. CROSS REFERENCES 103

4. What is the dierence in behavior between a Ball and a CannonBall?


5. How is the pseudo-variable super used in a method? What eect does it have in a
message expression?
6. What is the dierence between the types Integer and int?
7. What is an inner class?
8. What is an interface?
9. What would an interface for the class CannonBall look like?
10. What does it mean to say that a class implements an interface?
11. What does it mean to say that a program is event-driven?
12. What is an event listener?
13. What is a window layout manager?

Exercises
1. Change the CannonGame so that the message being displayed provides not only the
angle of the cannon, but also the current position of the cannon ball. This value should
change as the cannon ball moves through the air.
2. Modify the class CannonBall so that the ball is colored blue when the ball is moving
upwards, and colored red when the ball is descending. Will this change have any
impact on the rest of the program?
3. Modify the CannonWorld program so that it maintains both a count of the number of
balls red and the number of times the target was hit. Display both of these values
in the message area, as well as the angle of the cannon.
4. On some platforms it may be dicult to halt the cannon application once it has
nished. Add a button labeled \Quit" to the bottom (south) part of the application
window. When pressed, this button should execute the method System.exit(0).
5. Create a simple program that draws a window with a colored ball in the center. Place
a button at the top of the window. When the user presses the button, the color of the
ball will change from red to yellow, or from yellow to green, or from green to red.
6. Create a simple program that draws a window with a colored ball in the center. Place
a slider on the right side of the window. As the user moves the slider, move the ball
up and down the window.
104 CHAPTER 6. A CANNON GAME

7. The constructor for class Color can also take three integer arguments, which represent
the saturation values for red, blue and green. The arguments must be integers between
0 and 255. Create a simple program that draws a window with a colored ball in the
center. Place sliders on three sides. As the user moves the sliders, the saturation
values change for one of these arguments, and the ball changes color.
Chapter 7

Pin Ball Game Construction Kit


In this chapter we will expand on the techniques introduced in the cannon game of Chap-
ter 6, creating an entirely dierent type of interactive application. Along the way, we will
use the development of this program to discuss standard data structures, event handling,
inheritance, exceptions, and interfaces.

The application we will develop simulates a pin ball game. Users can re a ball from the
small square in the right corner of the bottom of the screen. Balls rise, then encounter a
105
106 CHAPTER 7. PIN BALL GAME CONSTRUCTION KIT

variety of dierent types of targets as they fall back to the ground. The user scores points
that depend upon the type and number of targets encountered as the ball descends. The
objective of the game is to score as many points as possible.
As we did with the cannon ball application in Chapter 6, we will develop this program in
a sequence of stages, each stage emphasizing a small number of new programming concepts.
Our intent here is to simply introduce these concepts in the context of a relatively simple
and easy to understand program. Many of the major ideas we introduce will subsequently
be discussed in more detail in later chapters.

7.1 First Version of Game


Our rst version (Figure 7.1) is in many ways the same as the cannon ball application from
Chapter 6, but with a number of new features. Because we will later be creating objects that
will need to communicate with the window object, we have declared the variable world as a
public static value, rather than as a local variable to the main procedure. We have moved
the \cannon" to the bottom right corner of the window, and hidden it behind a square box
labeled with a red circle. A notable dierence between this version and the cannon ball
application of Chapter 6 is the mechanism for placing a new ball into motion. Whereas in
the earlier program ring was tied to a button, in this version we will trap mouse activities
in a more general routine. Two other notable changes are that the pin ball game allows
multiple balls to be in the air at once (the cannon game only red a single ball), and we
will nally move the control over the animation out of the paint routine, by introducing the
concept of multiple threads of execution. Each of these ideas will be explored in more detail
in the following sections.

7.1.1 Collection Classes


Unlike the cannon game described in the previous chapter, the pin ball game allows multiple
balls to be moving at one time. Every time the user clicks on the \re" button a new ball
is placed in motion, even if earlier balls have not yet ceased moving.
To manage this, we need a data structure that can hold a collection of values. The one
container data structure we have seen up to now is the array (see Section 5.6). However,
the array is limited by the fact that when we allocate a new array object we must state
the number of elements the array will hold. In the present case, we cannot make any such
estimate since we do not know how many times the user will hit the \re" button.
Fortunately, the Java library provides a number of other data structures we can employ.
One of the simplest ones is a Vector. A vector is, like an array, an indexed data structure
meaning that each element has a position in the collection, and elements are accessed by
requesting the value at a given position. However, unlike an array, a vector can dynamically
grow as new values are inserted into the collection.
7.1. FIRST VERSION OF GAME 107
import java.awt.
import java.awt.event.
import java.util.Vector

public class PinBallGame extends Frame f

public static void main (String  ] args) f


world = new PinBallGame()
world.show()
g

public static final int FrameWidth = 400


public static final int FrameHeight = 400
public static PinBallGame world
private Vector balls

public PinBallGame () f
setTitle ("Pin Ball Construction Kit")
setSize (FrameWidth, FrameHeight)

balls = new Vector()


addMouseListener (new MouseKeeper())
g

private class MouseKeeper extends MouseAdapter f ... g

private class PinBallThread extends Thread f ... g

public void paint (Graphics g) f


g.setColor (Color.white)
g.fillRect (FrameWidth-40, FrameHeight-40, 30, 30)
g.setColor (Color.red)
g.fillOval (FrameWidth-40, FrameHeight-40, 30, 30)
// draw balls
for (int i = 0 i < balls.size() i++) f
Ball aBall = (Ball) balls.elementAt(i)
aBall.paint(g)
g
g
g

Figure 7.1: First version of PinBallGame class


108 CHAPTER 7. PIN BALL GAME CONSTRUCTION KIT

To use a vector, the programmer must rst import the vector class denition from the
standard library:
import java.util.Vector

The vector is declared by simply providing a name:


private Vector balls

Note, in particular, that unlike an array it is not necessary to state the type of values that a
Vector will hold. For technical reasons having to do with their internal structure, a vector is
restricted to holding only objects. Thus, for example, one cannot create a vector of integer
values (ints) but one can create a vector of instances of class Integer. This is one reason for
the existence of \wrapper" classes, such as Integer and Float.
Just as an array in Java separates the declaration of the array name and the allocation
of space for the array, the space for a Vector must be similarly created and assigned. In
our example program this occurs in the constructor for the class PinBallGame. Note that
no xed limit is set for the space:
balls = new Vector()

Although we have not seen it yet, a new element will be inserted into the vector by the
method newElement:
balls.addElement (newBall)

The number of values stored in a Vector can be determined by invoking the method size.
for (int i = 0 i < balls.size() i++)

Finally, values are accessed in a vector using the function elementAt. Like an array, the
set of legal index values range from zero to one less than the number of elements in the
collection. The compiler only knows that the accessed element is a value of type object it
must be cast to the appropriate type before it can be used. Here we cast the value into the
type Ball. A run-time check is performed to ensure that the conversion is actually valid:
Ball aBall = (Ball) balls.elementAt (i)

The reader should note how the paint procedure in Figure 7.1 cycles over the collection
of balls, asking each to paint itself.
7.1. FIRST VERSION OF GAME 109

7.1.2 Mouse Listeners


As we noted in Chapter 6, the Java event model is based around the concept of listeners
objects that wait and \listen" for an event to take place, and then respond appropriately. In
our earlier examples we showed how to create a listener by dening a class that implements
the corresponding interface for the event in question.
Mouse events are treated in a similar fashion, however there are ve dierent mouse-
related events that could potentially be of interest. Thus, the interface for a MouseListener
has the following structure:
public interface MouseListener f
public void mouseClicked (MouseEvent e)
public void mouseEntered (MouseEvent e)
public void mouseExited (MouseEvent e)
public void mousePressed (MouseEvent e)
public void mouseReleased (MouseEvent e)
g

Often a programmer is interested in only one or two of these ve. However, to im-
plement an interface the Java language insists that the programmer provide a denition
for all operations. To simplify such cases, the Java library provides a simple class named
MouseAdapter. The class MouseAdapter implements the MouseListener interface, but uses
an empty procedure for each member function. That is, a MouseAdapter does nothing in
response to any mouse event. However, the programmer can write a new class that inherits
from MouseAdapter, and overrides (or redenes) the procedures of interest.
That is what we do in our example program. An inner class denes a MouseListener by
extending MouseAdapter. An instance of this class is created, and passed as argument to
the method addMouseListener, which is inherited from class Frame.
addMouseListener (new MouseKeeper())

The class MouseKeeper inherits all ve methods dened by MouseAdapter, and redenes
only one. The other four messages will be handled by the methods inherited from the parent
class, which will do nothing.
private class MouseKeeper extends MouseAdapter f

public void mousePressed (MouseEvent e) f


int x = e.getX()
int y = e.getY()
// only handle mouse event in the re region
if ((x > FrameWidth - 40) && (y > FrameHeight-40)) f
110 CHAPTER 7. PIN BALL GAME CONSTRUCTION KIT

PinBall newBall = new PinBall(x, y)


balls.addElement (newBall)
Thread newThread = new PinBallThread (newBall)
newThread.start()
g
g
g

The argument passed to each procedure in the MouseListener interface is a value of type
MouseEvent. The mouse event encodes certain information relating to the type of event
that occurred. If our case, the most important information is the position (or coordinate)
of the mouse at the moment the button was pressed. This information can be derived from
the mouse event object using the methods getX and getY. With these values, a new ball is
created, added to the list of balls, and placed into motion.

7.1.3 Multiple Threads of Execution


The structure of our cannon game application (Figure 6.3) was clouded by the fact that
we used the paint routine to control the animation. During each painting cycle the balls
would be moved a little, and the window would be scheduled for another repainting. From
a logical point of view, the animation task has little to do with repainting the screen image.
Thus combining these two tasks makes the structure of the program unnecessarily dicult
to understand.
In every Java program there must be one principal process that is listening for mouse
clicks, repainting the window, and so on. In the cannon game, we abused this process by
slipping in control of the animation as tasks to be performed while the process was also
performing its regular duties. A more straightforward solution is to separate these two
tasks, leaving the listening for mouse clicks task to one process, and creating a separate
process for the control and movement of the pin balls.
A thread represents a separate task being performed by the computer. One can think
of a computer processor as being similar to a person reading the text of a Java program,
and performing the associated actions one statement at a time. With this image in mind, a
program with two threads is like having two people reading the same program, but working
independently of each other. Each may be operating in a dierent part of the program,
executing dierent instructions, despite the fact that they are working at the same time.
Occasionally the two may meet in the same area, or at least exchange information with each
other but in other respects, they are independent.
In our pin ball program we will create a separate thread for each ball. The thread will be
responsible for controlling the motion of the ball, and the interaction with other balls (and
eventually, with targets). In Java a thread is created by making an class that is a subclass
7.1. FIRST VERSION OF GAME 111

of the class Thread.1 The inner class PinBallThread is shown below:


public class PinBallGame f
...

private class PinBallThread extends Thread f


private Ball theBall

public PinBallThread (Ball aBall)


f theBall = aBall g

public void run () f


while (theBall.y() < FrameHeight) f
theBall.move ()
// more actions here later
repaint ()
try f
sleep(10)
g catch (InterruptedException e) f System.exit(0) g
g
g
g
...
g

The constructor for the thread is given the ball it will be controlling. The actions for
a new thread are those provided by the method named run. In this case the actions of a
pin ball thread will be to move the ball slightly, schedule the window for repainting, then
sleep for 10 milliseconds. The latter action is performed by the method sleep inherited
from class Thread, and is designed to keep the simulation from moving too quickly. We will
subsequently add more work to the loop controlling the life of a PinBall.
When the programmer hits the re button, a new PinBall is created, and a new instance
of the class PinBallThread is created to manage the new ball. The method start, inherited
from class Thread, arranges the thread for execution, and begins processing.
PinBall newBall = new PinBall(x, y)
balls.addElement (newBall)
Thread newThread = new PinBallThread (newBall)
newThread.start()

1 This is not the only way. In Chapter 21 will present an alternative way to create a thread.
112 CHAPTER 7. PIN BALL GAME CONSTRUCTION KIT

Note carefully that to start a thread the programmer should execute the inherited method
start. This, in turn, will eventually execute the method run that the programmer provides.
You should never directly execute the run method as there are a number of actions that
the Thread manager must perform before the actions of a new thread can be initiated.

7.1.4 Exception Handling


The sleep command is the rst occasion we have encountered where a method can potentially
throw an exception. An exception is an error condition, which can be caught and handled
so as to recover from the error in a systematic fashion. Methods that can potentially throw
exceptions must declare so in their heading, in a manner we will describe in a later chapter.
Any use of such a method must either occur inside a method that itself declares that it can
throw an exception, or within a try statement, as shown here:
try f
sleep(10)
g catch (InterruptedException e) f System.exit(0) g

In this case, the potential source of exception is the fact that a thread could be inter-
rupted while it is sleeping, for example (on some operating systems) by the user entering
the \interrupt" key while the program is running. If such a condition occurs, the sleep state-
ment will be halted before it can complete, and control will transfer to the statement in the
closing block that matches the condition of the interrupt. In this case, we will terminate the
program by invoking the function System.exit. In subsequent programs we will encounter
many more statements that can throw a variety of dierent exceptional conditions.

7.2 Adding Targets: Inheritance and Interfaces


To provide realism and interest to our pin ball game, we need to add targets for the ball to
encounter on its way down the playing surface. As in real pin ball games, we will want to
include a variety of dierent types of target. Some targets simply add values to the score,
some move the ball in a new direction, and some consume the ball, removing it from play.
In order to simplify the program, we will want to maintain all the dierent types of target
in a single data structure, a vector.

7.2.1 The Pin Ball Target Interface


Because we want to process targets uniformly, for example in a loop that asks whether a
ball has hit any target, we need all the targets to have a uniform interface. However, the
various dierent types of target will be represented internally by dierent data structures.
Thus, we do not want to use inheritance, such as we have been doing with the dierent
7.2. ADDING TARGETS: INHERITANCE AND INTERFACES 113

forms of Ball up to this point. Inheritance is a mechanism for sharing structure a PinBall,
for example, is simply a type of Ball that has the same structure and behavior as a Ball,
adding a few new features (such as being able to run as a separate thread), but maintaining
all the characteristics of the original. There is little in the way of common structure between
a Peg (a target that when hit by a ball scores a number of points and moves the ball in a
new direction) and a Wall (a target that when struck simply re ects the motion of the ball).
What is needed in this case is the ability to state that the two concepts (Peg and Wall,
in this case), share the same behavior, although they have nothing in common in structure.
As we saw in our earlier case study, in Java, this is accomplished by describing the common
behavior as an interface, and declaring that both objects implement the same interface. An
interface for our pin ball targets might be described as follows:
interface PinBallTarget f
public boolean intersects (Ball aBall)
public void moveTo (int x, int y)
public void paint (Graphics g)
public void hitBy (Ball aBall)
g

The interface in this case is declaring that there are four characteristics of interest in
a pin ball target. Each target can tell if it has been hit by a ball that is, if it intersects
the region occupied by a ball. Each target can be moved to a specic point on the playing
surface, each can paint itself, and each provides some response when it has been hit by a
given ball. However, the means by which each of these behaviors will be achieved is left
unspecied, and dierent targets are free to implement the interface in dierent fashions.
An examination of a few dierent targets will help illustrate the point. Our rst type of
target will be a Spring. When hit by a falling ball, a Spring rebounds the ball back into the
playing area, moving it upwards where it hopefully will encounter further targets. A spring
is represented graphically by a small horizontal box, and a series of zig-zag lines, such as
the following:

h hh
hhhh
hh
( (
(( ( ((((
hhhh
hhhh
(h(
( ( ((((
(

The class description for Spring is shown in Figure 7.2. Note how the class Spring must
explicitly state that it implements the PinBallTarget interface, and must provide a specic
meaning for each of the four elements of that interface. In this case, we will state that a
spring intersects a ball if the rectangle surrounding the ball intersects with the rectangle
114 CHAPTER 7. PIN BALL GAME CONSTRUCTION KIT

representing the spring platform. When the spring is hit by the ball, it reverses the vertical
direction of movement for the ball. (In actual fact, it simply guarantees the ball is moving
upward. In rare situations, a spring could possibly be hit from below, although we don't
expect this to be the norm). It then gives the ball a slight boost in the vertical direction.
We have added one slight element of interest to the drawing of the Spring object. We have
provided two dierent graphical representations for the Spring object, selected by an integer
variable named state. Normally, a spring will be held in state 1. When struck, the value of
state is changed to 2, and the next time the spring is redrawn it will present an alternative
image, one in which the spring has been elongated. Drawing this second image changes the
state back to state 1, and a subsequent redraw will display the original. The eect is a
simple form of animation, where a moving spring will appear to stretch momentarily, then
return to a ready state.
A second type of target is a Wall. A Wall (Figure 7.3) is a rectangular region. A
ball intersects a wall if their regions overlap, and if so the ball is simply re ected back, in
eect bouncing o the wall. The bounce is either along the horizontal or vertical direction,
depending upon which side of the wall has been hit.
The advantage of declaring these two dierent structures as both implementing an in-
terface is that an interface name can be used as a type. That is, we can declare a variable
as holding a value of type PinBallTarget. In much the same way that a variable declared as
maintaining a value of type Ball could, in fact, be holding a cannon ball, a pin ball, or any
other value derived from a class that extends the original class Ball, a variable declared as
maintaining a PinBallTarget could, in fact, be holding either a Spring a Wall, or any of the
other varieties of target we will subsequently describe. (This is one aspect of polymorphism,
a topic we will return to in more detail in Chapter 12). We will shortly make use of this
property, by storing all the targets in our game in a single data structure, and testing the
motion of the ball against the location of each target in turn.
Consider now a third type of target, a Hole. A Hole (Figure 7.4) consumes any ball it
comes in contact with, removing the ball from the playing surface. A Hole is represented by
a circular colored image, just like a ball. A Hole has a location on the playing surface, just
like a Ball. In fact, because a hole is structurally similar to a Ball, we can use inheritance to
simplify the implementation of the Hole abstraction.
This illustrates the important dierence between the use of inheritance and the use of
interfaces. The mechanism of inheritance should be used when two (or more) concepts have
a structural relationship. Note that with objects, a structural relationship almost always
implies at least some behavioral relationship. In contrast, the interface mechanism should
be used with two (or more) concepts having a behavioral relationship, but no structural
relationship. We will explore these ideas in more detail in Chapter 8.
Note how a Hole uses both inheritance and an interface. The hole inherits much of its
behavior from the class Ball, including the methods paint and moveTo. The Hole declares
that it implements the PinBallTarget interface, and to do so must provide a method to see
if the hole has intersected with a ball, and the actions to be performed when such an event
occurs. In the case of a hole, the ball is moved clear o the playing surface, and motion of
7.2. ADDING TARGETS: INHERITANCE AND INTERFACES 115
class Spring implements PinBallTarget f
private Rectangle pad
private int state = 1

public Spring (int x, int y)


f pad = new Rectangle (x, y, 30, 3) g

public void moveTo (int x, int y)


f pad.setLocation (x, y) g

public void paint (Graphics g) f


int x = pad.x int y = pad.y
g.setColor(Color.black)
if (state == 1) f
g.fillRect(x, y, pad.width, pad.height)
g.drawLine(x, y+3, x+30, y+5)
g.drawLine(x+30, y+5, x, y+7)
g.drawLine(x, y+7, x+30, y+9)
g.drawLine(x+30, y+9, x, y+11)
g
else f // draw extended spring
g.fillRect(x, y-8, pad.width, pad.height)
g.drawLine(x, y+5, x+30, y-1)
g.drawLine(x+30, y-1, x, y+3)
g.drawLine(x, y+3, x+30, y+7)
g.drawLine(x+30, y+7, x, y+11)
state = 1
g
g

public boolean intersects (Ball aBall)


f return pad.intersects(aBall.location) g

public void hitBy (Ball aBall) f


// make sure we are moving up
if (aBall.dy > 0) aBall.dy = - aBall.dy
// give the ball a little boost
aBall.dy = aBall.dy - 0.5
state = 2
g
g

Figure 7.2: Denition for class Spring.


116 CHAPTER 7. PIN BALL GAME CONSTRUCTION KIT

class Wall implements PinBallTarget f


public Rectangle location

public Wall (int x, int y, int width, int height)


f location = new Rectangle(x, y, width, height) g

public void moveTo (int x, int y)


f location.setLocation (x, y) g

public void paint (Graphics g) f


g.setColor(Color.black)
g.fillRect(location.x, location.y,
location.width, location.height)
g

public boolean intersects (Ball aBall)


f return location.intersects(aBall.location) g

public void hitBy (Ball aBall) f


if ((aBall.y() < location.y)
jj (aBall.y() > (location.y + location.height)))
aBall.dy = - aBall.dy
else
aBall.dx = - aBall.dx
g
g

Figure 7.3: Denition of class Wall.


7.2. ADDING TARGETS: INHERITANCE AND INTERFACES 117
class Hole extends Ball implements PinBallTarget f

public Hole (int x, int y) f


super (x, y, 12)
setColor (Color.black)
g

public boolean intersects (Ball aBall)


f return location.intersects(aBall.location) g

public void hitBy (Ball aBall) f


// move ball totally o frame
aBall.moveTo (0, PinBallGame.FrameHeight + 30)
// stop motion of ball
aBall.setMotion(0, 0)
g
g

Figure 7.4: Denition of class Hole.

the ball is halted. Eventually the ball will note that its location exceeds the window size,
and the tread controlling the ball will halt.
A class that inherits from an existing class that implements an interface must of necessity
also implement the interface. We will use this property in dening the next two types of
targets in our pin ball game. A ScorePad is, like a hole, represented by a circular region.
When struck by a ball the score pad has no eect on the ball (the ball simply moves over it),
however the score pad adds a certain amount to the player's score. The particular amount
to add is dened as part of the state for the score object.
Note how the ScorePad class (Figure 7.5) inherits the intersects behavior from class Hole
and the moveTo behavior from class Ball, but overrides the paint and hitBy methods that
would otherwise be inherited from class Hole. The rst now draws a colored circle with the
scoring amount in the middle, while the latter adds the given value to the player score.
Because a ScorePad inherits from class Hole, which implements the PinBallTarget inter-
face, the class ScorePad is also said to implement the interface. This means, for example,
that a ScorePad could be assigned to a variable which was declared to be a PinBallTarget.

7.2.2 Adding a Label to our Pin Ball Game


In the revised version of our program we will add a new graphical element, a textual label in
a banner across the top of the window. This is accomplished by declaring a new Label, and
adding it in the \North" part of the window (Figure 7.6). As the user scores new points,
118 CHAPTER 7. PIN BALL GAME CONSTRUCTION KIT

class ScorePad extends Hole f


protected int value

public ScorePad (int x, int y, int v) f


super (x, y)
value = v
setColor (Color.red)
g

public void hitBy (Ball aBall)


f PinBallGame.world.addScore(value) g

public void paint (Graphics g) f


g.setColor (color)
g.drawOval (location.x, location.y,
location.width, location.height)
String s = "" + value
g.drawString(s, location.x, y()+2)
g
g

Figure 7.5: Denition of the class ScorePad.


7.2. ADDING TARGETS: INHERITANCE AND INTERFACES 119
public class PinBallGame extends Frame f

private int score


private Label scoreLabel

public PinBallGame () f
...
score = 0
scoreLabel = new Label ("Score: 0")
add ("North", scoreLabel)
...
g

synchronized public void addScore (int v) f


score = score + v
scoreLabel.setText ("score = " + score)
g
...
g

Figure 7.6: Adding Labels to our Pin Ball game

the text of the label is updated. Note that a ScorePad refers back to the application object
through the variable world.
A feature of the addScore method deserves note. In the revised form of the pin ball
game application class, the method addScore is declared as synchronized. Recall that balls
move independently of each other, and so in theory two balls could roll over two dierent
score pads at much the same time. Two attempts could then be made to update the player
score at once. The resulting actions could easily be unpredictable, and almost certainly
wrong. By declaring the function that updates the score value as synchronized, the Java
language will guarantee that two threads cannot be executing this function at once. If a
thread tries to execute a synchronized method that is currently being executed by another
thread, the second thread is suspended until the rst thread exits the synchronized routine.
Synchronization and threads will be discussed in more detail in Chapter 21.
A Peg is similar to a ScorePad, however it sticks up above the playing surface. Thus,
when a Peg is struck, it re ects the ball o in a new direction, depending upon the angle
of the ball and the point it encounters the peg. (The algorithm used here is not exactly
correct as far as actual physics is concerned, but it does have the advantage of being easy
to compute). The ball is then updated until it no longer intersects with the Peg, thereby
avoiding having the procedure executed multiple times for a single encounter. We have once
again added a simple animation to the class Peg, so that the rst time a peg is redrawn after
120 CHAPTER 7. PIN BALL GAME CONSTRUCTION KIT

class Peg extends ScorePad f


private int state = 1

public Peg (int x, int y, int v)


f super(x, y, v) g

public void paint (Graphics g) f


super.paint(g)
if (state == 2) f // draw expanded circle
g.drawOval(location.x-3, location.y-3,
location.width+6, location.height+6)
state = 1
g
else
g.drawOval(location.x-2, location.y-2,
location.width+4, location.height+4)
g

public void hitBy (Ball aBall) f


super.hitBy (aBall) // update the score
aBall.setMotion (-aBall.dy, -aBall.dx) // update direction
while (intersects(aBall)) // move out of range
aBall.move()
state = 2 // next draw will expand circle
g
g

Figure 7.7: Denition of the class Peg.


7.3. PIN BALL GAME CONSTRUCTION: MOUSE EVENTS RECONSIDERED 121

it has been struck the circle surrounding the peg will appear to enlarge, and then return to
a normal size.

To create the second version of our game (Figure 7.8), we simply create a Vector of
targets, along with the vector of balls. We initialize the targets in the constructor for the
game, including placing walls on the sides and top of the playing area, to re ect wayward
balls. The user res balls as before, which then proceed to interact with the various targets
as the balls move down the playing surface. Each time a ball moves, a loop is executed to
determine if the new location of the ball has struck a target. If so, the target is informed,
and the location of the ball potentially updated. Finally, the entire screen is repainted,
which involves repainting both the targets and the collection of balls.

7.3 Pin Ball Game Construction: Mouse Events Recon-


sidered

Although the second version of pin ball game is certainly more interesting than the rst,
it is still limited by the fact that the layout of the various targets is determined by the
original programmer. To create a dierent layout, the program must be changed, and then
recompiled and executed. In our nal version, we will show how this limitation can be over-
come, by providing a pallet of target elements from which the user can select, dynamically
constructing the pin ball game while the program is executing.

In appearance, our revised game will move the playing area slightly to the right, placing
a sequence of potential target components along a strip in the far left. The user can click
the mouse down in one of these alternatives, then slide the mouse (still down) over into the
playing area. When the user releases the mouse, the selected target element will be installed
into the new location.
122 CHAPTER 7. PIN BALL GAME CONSTRUCTION KIT
public class PinBallGame extends Frame f

private Vector targets

public PinBallGame () f
...
// create the targets
targets = new Vector()
targets.addElement(new Wall(30, 50, 2, 350))
targets.addElement(new Wall(30, 50, 360, 2))
targets.addElement(new Wall(390, 50, 2, 380))
targets.addElement(new Hole(100, 100))
targets.addElement(new ScorePad(150, 220, 100, this))
targets.addElement(new Peg(300, 140, 200, this))
targets.addElement(new Spring(120, 350))
g

private class PinBallThread extends Thread f


...
public void run () f
while (theBall.y() < FrameHeight) f
theBall.move ()
// see if we ran into anything
for (int j = 0 j < targets.size() j++) f
PinBallTarget target =
(PinBallTarget) targets.elementAt(j)
if (target.intersects(theBall)) target.hitBy(theBall)
g
...
g
g
g

public void paint (Graphics g) f


...
for (int j = 0 j < targets.size() j++) f // draw targets
PinBallTarget target = (PinBallTarget) targets.elementAt(j)
target.paint(g)
g
g
g

Figure 7.8: Addition of targets to the class PinBallGame.


7.4. CHAPTER SUMMARY 123

The eect is produced by overriding both the mousePressed and the mouseReleased meth-
ods inherited from the mouse adapter (Figure 7.9). The two methods communicate with
each other by means of a variable named element. The mousePressed procedure creates a
potential target, determined by the coordinates of the point at which the mouse goes down.
Note that we have not eliminated the original use of the mouseDown procedure, simply
added a new condition. The MouseReleased procedure checks the location of the release,
and if it is in the playing area and if a target item was previously selected (both conditions
must be true), then a new target is added to the game.
Other changes needed to provide our nal version of the pin ball construction kit simply
involve repositioning the left wall, and drawing the images of the selection pallet.

7.4 Chapter Summary


In this chapter we have once again used the development of an example program as a vehicle
to introduce a number of features of the Java programming language. In particular, in this
chapter we have introduced the following:
124 CHAPTER 7. PIN BALL GAME CONSTRUCTION KIT

private class MouseKeeper extends MouseAdapter f

private PinBallTarget element

public void mousePressed (MouseEvent e) f


element = null
int x = e.getX()
int y = e.getY()
if ((x > FrameWidth-40) && (y > FrameHeight -40)) f
PinBall newBall = new PinBall(e.getX(), e.getY())
balls.addElement (newBall)
Thread newThread = new PinBallThread (newBall)
newThread.start()
g
if (x < 40) f
switch (y / 40) f
case 2: element = new Hole(0, 0) break
case 3: element = new Peg(0, 0, 100) break
case 4: element = new Peg(0, 0, 200) break
case 5: element = new ScorePad(0, 0, 100) break
case 6: element = new ScorePad(0, 0, 200) break
case 7: element = new Spring(0, 0) break
case 8: element = new Wall(0, 0, 2, 15) break
g
g
g

public void mouseReleased (MouseEvent e) f


int x = getX()
int y = getY()
if ((element != null) && (x > 50))f
element.moveTo(x, y)
targets.addElement (element)
repaint()
g
g
g

Figure 7.9: Capturing both mouse presses and releases.


7.4. CHAPTER SUMMARY 125

 The use of collection classes, in particular the container class Vector. We will discuss
containers in more detail in Chapter 20.
 We have expanded the discussion we started in Chapter 6 of the Java listener event
model. In particular, we here describe how to create objects that will listen for mouse
events.
 We introduced the concept of a program having multiple threads of execution. In
particular, while the main thread of our program listens for events and paints the
windows, each ball in the simulation is being controlled by an independent routine.
 We saw our rst example of a statement that could potentially produce an exception,
and how the Java language permits the programmer to specify what actions to take
when an exception occurs.
 We returned to the discussion of interfaces, and contrasted the use of the interface
mechanism with the use of inheritance.
 We were introduced to one aspect of the important concept of polymorphism. In
particular, a variable declared as an instance of a parent class (such as Ball) can,
in fact, be holding a value derived from a child class (such as PinBall). Similarly, a
variable declared as an interface value (such as PinBallTarget) can, in fact, hold any
object that implements that interface (such as Peg). This property allows us to create
arrays of dierent objects, such as an array of pin ball targets, and process them in a
uniform fashion.

Cross References
The distinction between interfaces and inheritances is explored in more detail in Chapter 8.
Collection classes will be investigated in detail in Chapter 20. Chapter 13 presents a more
systematic investigation of the services provided by the AWT. Chapter 21 explores the
multithreading features of Java.

Study Questions
1. Why must the variable world be declared static?
2. In what ways is a Vector object similar to an array? In what ways is it dierent?
3. What command is used to determine the number of elements held in a Vector? What
method is used to access the values? What method is used to insert a new value into
the collection?
126 CHAPTER 7. PIN BALL GAME CONSTRUCTION KIT

4. What is the relationship between MouseAdapter and MouseListener? In what ways are
they dierent?
5. What is a thread? What are the tasks assigned to the dierent threads in our appli-
cation?
6. How is a thread started? How the programmer specify the actions a thread should
perform?
7. What is an exception?
8. What action is performed by the function System.exit? Under what circumstances in
our program will this function be called?
9. When should two software components be tied together through the use of inheritance
rather than a common interface?
10. What type of objects can be held by a variable declared using the interface PinBall-
Target?
11. In what ways does the class Hole modify the behavior inherited from class Ball?
12. What is a Label? How is a label attached to a window? What methods are used to
change the text of a label?
13. What does it mean to say that a method is synchronized?

Exercises
1. The class Peg inherits from ScorePad, which in turn inherits from Hole, which in turn
inherits from Ball. For each of these classes, describe all the methods dened in the
class or inherited from parent classes, and for each of the latter indicate in which
parent class the method denition occurs.
2. The pin ball game as presented allows the user an unlimited number of balls. Change
the program to re only a xed number of balls, disallowing ring once the supply
is exhausted. Change the display at the top of the screen so that it will indicate the
number of remaining balls, as well as the score.
3. Add a \reset" button to the bottom of the screen. When pressed, the reset button sets
the score back to zero and, if you implemented the suggestion in previous question,
resets the number of balls in play.
4. On some platforms it may be dicult to halt the PinBall application once it has
nished. Add a button labeled \Quit" to the bottom (south) part of the application
window. When pressed, this button should execute the method System.exit(0).
7.4. CHAPTER SUMMARY 127

5. In the nal program, the items in the pallet are still stored on the targets vector, so
that they will be redrawn, even though they can never be hit by a ball. A better
solution would have been to create a new vector pallet that will hold these items,
redrawing both the pallet and the targets on a repaint. Modify the program in this
fashion.
6. Currently balls do not test to see if they intersect with other balls. We could support
this modication by making PinBall implement the PinBallTarget interface, and adding
balls to the list of targets as well as the list of balls. Describe what changes would
need to be added to modify the program in this fashion.
7. Another change could allow the programmer to reposition items even after they have
been placed in the playing area. If a mouse click occurs on the playing surface over
a target, select the target and move it to the location given by the associated mouse
up. Be careful that you don't end up placing the element in the target vector twice.
8. Create a program that opens a window, listens for mouse clicks, and when a mouse is
released will display the distance (in pixel units) between the location the mouse was
pressed and the location it was released.
9. Write a program that places a red circle in the middle of the window. The circle should
change color to blue when the mouse enters the window, then return to red when the
mouse leaves the window. When the mouse is clicked inside the window, the circle
should change color to green and remain green for 1000 milliseconds, before returning
to blue. Finally, if the mouse is clicked within the bounds of the circle and released
outside the circle, the circle should be moved so as to be centered on the location of
the mouse release.
10. Develop a \paddle" target object. When the user clicks the mouse over the paddle,
the paddle should move back and forth (perhaps only once). If a paddle encounters a
ball, the ball is re ected o the paddle.
128 CHAPTER 7. PIN BALL GAME CONSTRUCTION KIT
Part III

Understanding Inheritance

129
Chapter 8

Understanding Inheritance
The rst step in learning object-oriented programming is understanding the basic philosophy
of organizing a computer program as the interaction of loosely coupled software components.
This idea was the central lesson in the case studies presented in the rst part of the book.
The next step in learning object-oriented programming is organizing classes into a hierar-
chical structure based on the concept of inheritance. By inheritance, we mean the property
that instances of a child class (or subclass) can access both data and behavior (methods)
associated with a parent class (or superclass).
Although in Java the term inheritance is correctly applied only to the creation of new
classes using subclassing (the extends keyword), there are numerous correspondences be-
tween subclassing and the designation that classes satisfy an interface (the implements key-
word). The latter is sometimes termed \inheritance of specication," contrasted with the
\inheritance of code" provided by subclassing. In this chapter we will use the word in a
general fashion, meaning both mechanisms.
While the intuitive meaning of inheritance is clear, and we have used inheritance in many
of our earlier case studies, and the mechanics of using inheritance are relatively simple,
there are nevertheless subtle features involved in the use of inheritance in Java. In this and
subsequent chapters we will explore some of these issues.

8.1 An Intuitive Description of Inheritance


Let us return to Flora the orist from the rst chapter. There is a certain behavior we expect
orists to perform, not because they are orists but simply because they are shopkeepers.
For example, we expect Flora to request money for the transaction and in turn give us
a receipt. These activities are not unique to orists, but are common to bakers, grocers,
stationers, car dealers, and other merchants. It is as though we have associated certain
behavior with the general category Shopkeeper, and as Florists are a specialized form of
131
132 CHAPTER 8. UNDERSTANDING INHERITANCE

shopkeepers, the behavior is automatically identied with the subclass.


In programming languages, inheritance means that the behavior and data associated
with child classes are always an extension (that is, a larger set) of the properties associated
with parent classes. A child class will be given all the properties of the parent class, and
may in addition dene new properties. On the other hand, since a child class is a more
specialized (or restricted) form of the parent class, it is also, in a certain sense, a contraction
of the parent type. For example, the Java library Frame represents any type of window, but
a PinBallGame frame is restricted to a single type of game. This tension between inheritance
as expansion and inheritance as contraction is a source for much of the power inherent in
the technique, but at the same time it causes much confusion as to its proper employment.
We will see this when we examine a few of the uses of inheritance in a subsequent section.
Inheritance is always transitive, so that a class can inherit features from superclasses
many levels away. That is, if class Dog is a subclass of class Mammal, and class Mammal
is a subclass of class Animal, then Dog will inherit attributes both from Mammal and from
Animal.
A complicating factor in our intuitive description of inheritance is the fact that subclasses
can override behavior inherited from parent classes. For example, the class Platypus overrides
the reproduction behavior inherited from class Mammal, since platypuses lay eggs. We will
brie y mention the mechanics of overriding in this chapter, then return to a more detailed
discussion of the semantics of overriding in Chapter 11.

8.2 The base class Object


In Java all classes use inheritance. Unless specied otherwise, all classes are derived from a
single root class, named Object. If no parent class is explicitly provided, the class Object is
implicitly assumed. Thus, the class declaration for FirstProgram (Chapter 4, Figure 4.1) is
the same as the following:
class FirstProgram extends Object f
// ...
g

The class Object provides minimal functionality guaranteed to be common to all objects.
These include the following methods:
equals (Object obj) Determine whether the argument object is the same as the receiver. This
method is often overridden to change the equality test for dierent classes.
getClass () Returns the name of the class of the receiver as a string.
hashCode () Returns a hash value for this object (see Section 20.7). This method should
also be overridden when the equals method is changed.
8.3. SUBCLASS, SUBTYPE, AND SUBSTITUTABILITY 133

notify() Notify a thread waiting for this object. Used by the threads system, to be described
in Chapter 21.
toString () Converts object into a string value. This method is also often overridden.
wait () Temporarily halt the current thread until it is subsequently notied. Used by the
threads system, to be described in Chapter 21.

8.3 Subclass, Subtype, and Substitutability


The concept of substitutability is fundamental to many of the most powerful software devel-
opment techniques in object-oriented programming. The idea of substitutability is that the
type given in a declaration of a variable may not match the type associated with a value
the variable is holding. Note that this is never true in conventional programming languages,
but is a common occurrence in object-oriented programs.
We have seen several examples of substitutability in our earlier case studies. In the Pin
Ball game program described in Chapter 7, the variable target was declared as a PinBallTar-
get, but in fact held a variety of dierent types of values that were created using subclasses
of PinBallTarget. (These target values were held in the vector named targets).
PinBallTarget target = (PinBallTarget) targets.elementAt(j)

Substitutability can also occur through the use of interfaces. An example is the instance
of the class FireButtonListener created in the Cannon-ball game (Chapter 6). The class from
which this value was dened was declared as implementing the interface ActionListener.
Because it implements the ActionListener interface, we can use this value as a parameter to
a function (in this case, addActionListener) that expects an ActionListener value.
class CannonWorld extends Frame f
...
private class FireButtonListener implements ActionListener f
public void actionPerformed (ActionEvent e) f
...
g
g

public CannonWorld () f
...
fire.addActionListener(new FireButtonListener())
g
g
134 CHAPTER 8. UNDERSTANDING INHERITANCE

Because Object is a parent class to all objects, a variable declared using this type can
hold any non-primitive value. The collection class Vector makes use of this property, holding
its values in an array of Object values. Because the array is declared as Object, any object
value can be stored in a Vector.
When new classes are constructed using inheritance from existing classes, the argument
used to justify the validity of substitutability is as follows:
 Instances of the subclass must possess all data areas associated with the parent class.
 Instances of the subclass must implement, through inheritance at least (if not explicitly
overridden) all functionality dened for the parent class. (They can also dene new
functionality, but that is unimportant for the argument).
 Thus, an instance of a child class can mimic the behavior of the parent class and
should be indistinguishable from an instance of the parent class if substituted in a
similar situation.
We will see later in this chapter, when we examine the various ways in which inheritance
can be used, that this is not always a valid argument. Thus, not all subclasses formed using
inheritance are candidates for substitution.
The term subtype is used to describe the relationship between types that explicitly rec-
ognizes the principle of substitution. That is, a type B is considered to be a subtype of A if
two conditions hold. The rst is that an instance of B can legally be assigned to a variable
declared as type A. And the second is that this value can then be used by the variable with
no observable change in behavior.
The term subclass refers merely to the mechanics of constructing a new class using
inheritance, and is easy to recognize from the source description of a program by the presence
of the keyword extends. The subtype relationship is more abstract, and is only loosely
documented directly by the program source. In the majority of situations a subclass is also
a subtype. However, later in this chapter we will discover ways in which subclasses can be
formed that are not subtypes. In addition, subtypes can be formed using interfaces, linking
types that have no inheritance relationship whatsoever. So it is important to understand
both the similarities and the dierences between these two concepts.

8.4 Forms of Inheritance


Inheritance is used in a surprising variety of ways. In this section we will describe a few of
its more common uses. Note that the following list represents general abstract categories
and is not intended to be exhaustive. Furthermore, it sometime happens that two or more
descriptions are applicable to a single situation, because some methods in a single class
use inheritance in one way while others use it in another. In the following list, pay careful
attention to which uses of inheritance support the subtyping relationship and which do not.
8.4. FORMS OF INHERITANCE 135

8.4.1 Inheritance for Specialization


Probably the most common use of inheritance and subclassing is for specialization. In this
form, the new class is a specialized variety of the parent class but satises the specications
of the parent in all relevant respects. Thus, this form always creates a subtype, and the
principle of substitutability is explicitly upheld. Along with the following category (sub-
classing for specication) this is the most ideal form of inheritance, and something that a
good design should strive for.
The creation of application window classes using inheritance from the Java library class
Frame is an example of subclassing for specialization. The following is from the PinBall
Game program in Chapter 7.
public class PinBallGame extends Frame f
...
g

To run such an application an instance of PinBallGame is rst created. Various methods


inherited from class Frame, such as setSize, setTitle, and show, are then invoked. These
methods do not realize they are manipulating an instance of PinBallGame, but instead act
as if they were operating on an instance of Frame. The actions they perform would be the
same for any instance of class Frame.
Where application specic behavior is necessary, for example, in repainting the window,
a method is invoked that is overridden by the application class. For example, the method in
the parent class will invoke the method repaint. Although the parent class Frame possesses
a method of this name, the parent method is not the one executed. Instead, the function
dened in the child class is executed.
We say that subclassing for specialization is occurring in this example because the child
class (in this example, PinBallGame) satises all the properties that we expect of the parent
class (Frame). In addition, the new class overrides one or more methods, specializing them
with application-specic behavior.

8.4.2 Inheritance for Specication


Another frequent use for inheritance is to guarantee that classes maintain a certain common
interface{that is, they implement the same methods. The parent class can be a combination
of implemented operations and operations that are deferred to the child classes. Often,
there is no interface change of any sort between the parent class and the child class{the
child merely implements behavior described, but not implemented, in the parent.
This is actually a special case of subclassing for specialization, except that the subclasses
are not renements of an existing type but rather realizations of an incomplete abstract
specication. That is, the parent class denes the operation, but has no implementation.
136 CHAPTER 8. UNDERSTANDING INHERITANCE

It is only the child class that provides an implementation. In such cases the parent class is
sometimes known as an abstract specication class.
There are two dierent mechanisms provided by the Java language to support the idea
of inheritance of specication. The most obvious technique is the use of interfaces. We
have seen examples of this in the way that events are handled by the Java library. For
instance, the characteristics needed for an ActionListener (the object type that responds to
button presses) can be described by a single method, and the implementation of that method
cannot be predicted, since it diers from one application to another. Thus, an interface is
used to describe only the necessary requirements, and no actual behavior is inherited by a
subclass that implements the behavior.
interface ActionListener f
public void actionPerformed (ActionEvent e)
g

When a button is created, an associated listener class is dened. The listener class
provides the specic behavior for the method in the context of the current application.
class CannonWorld extends Frame f
...
// a re button listener implements the action listener interface
private class FireButtonListener implements ActionListener f
public void actionPerformed (ActionEvent e) f
... // action to perform in response to button press
g
g
g

Subclassing for specication can also take place with inheritance of classes formed using
extension. One way to guarantee that a subclass must be constructed is to use the keyword
abstract. A class declared as abstract must be subclassed it is not possible to create an
instance of such a class using the operator new. In addition, individual methods can also be
declared as abstract, and they, too, must be overridden before instances can be constructed.
An example abstract class in the Java library is Number, a parent class for the numeric
wrapper classes Integer, Long, Double and so on. The class description is as follows:
public abstract class Number f

public abstract int intValue()

public abstract long longValue()


8.4. FORMS OF INHERITANCE 137

public abstract float floatValue()

public abstract double doubleValue()

public byte byteValue()


f return (byte) intValue() g

public short shortValue()


f return (short) intValue() g
g

Subclasses of Number must override the methods intValue, longValue, oatValue and
doubleValue. Notice that not all methods in an abstract class must themselves be declared
abstract. Subclasses of Number need not override byteValue or shortValue, as these methods
are provided with an implementation that can be inherited without change.
In general, subclassing for specication can be recognized when the parent class does
not implement actual behavior but merely denes the behavior that must be implemented
in child classes.

8.4.3 Inheritance for Construction


A class can often inherit almost all of its desired functionality from a parent class, perhaps
changing only the names of the methods used to interface to the class, or modifying the
arguments in a certain fashion. This may be true even if the new class and the parent class
fail to share any relationship as abstract concepts.
An example of subclassing for construction occurred in the Pin ball game application
described in Chapter 7. In that program, the class Hole was declared as a subclass of
Ball. There is no logical relationship between the concepts of a Ball and a Hole, but from a
practical point of view much of the behavior needed for the Hole abstraction matches the
behavior of the class Ball. Thus, using inheritance in this situation reduces the amount of
work necessary to develop the class Hole.
class Hole extends Ball implements PinBallTarget f

public Hole (int x, int y) f


super (x, y, 12)
setColor (Color.black)
g

public boolean intersects (Ball aBall)


f return location.intersects(aBall.location) g
138 CHAPTER 8. UNDERSTANDING INHERITANCE

public void hitBy (Ball aBall) f


// move ball totally o frame
aBall.moveTo (0, PinBallGame.FrameHeight + 30)
// stop motion of ball
aBall.setMotion(0, 0)
g
g

Another example of inheritance for construction occurs in the Java Library. There, the
class Stack is constructed using inheritance from the class Vector:
class Stack extends Vector f

public Object push(Object item)


f addElement(item) return item g

public boolean empty ()


f return isEmpty() g

public synchronized Object pop() f


Object obj = peek()
removeElementAt(size() - 1)
return obj
g

public synchronized Object peek()


f return elementAt(size() - 1) g
g

As abstractions, the concept of the stack and the concept of a vector have little in
common however from a pragmatic point of view using the Vector class as a parent greatly
simplies the implementation of the stack.
Inheritance for construction is sometimes frowned upon, since it often directly breaks
the principle of substitutability (forming subclasses that are not subtypes). On the other
hand, because it is often a fast and easy route to developing new data abstractions, it
is nevertheless widely used. In Chapter 10 we will discuss the construction of the Stack
abstraction in more detail.

8.4.4 Inheritance for Extension


Subclassing for extension occurs when a child class only adds new behavior to the parent
class, and does not modify or alter any of the inherited attributes. An example of inheritance
8.4. FORMS OF INHERITANCE 139

for extension in the Java library is the class Properties, which inherits from class HashTable.
A hash table is a dictionary structure (see Section 20.7). A dictionary stores a collection
of key/value pairs, and allows the user to retrieve the value associated with a given key.
Properties represent information concerning the current execution environment. Examples
of properties are the name of the user running the Java program, the version of the Java
interpreter being used, the name of the operating system under which the Java program
is running, and so on. The class Properties uses the parent class, HashTable, to store and
retrieve the actual property name/value pairs. In addition, the class denes a few methods
specic to the task of managing properties, such as reading or writing properties to or from
a le.
class Properties extends Hashtable f
...

public synchronized void load(InputStream in) throws IOException f ... g

public synchronized void save(OutputStream out, String header) f ... g

public String getProperty(String key) f ... g

public Enumeration propertyNames() f ... g

public void list(PrintStream out) f ... g


g

As the functionality of the parent remains available and untouched, subclassing for
extension does not contravene the principle of substitutability and so such subclasses are
always subtypes.

8.4.5 Inheritance for Limitation


Subclassing for limitation occurs when the behavior of the subclass is smaller or more
restrictive than the behavior of the parent class. Like subclassing for extension, subclassing
for limitation occurs most frequently when a programmer is building on a base of existing
classes that should not, or cannot, be modied.
There are no examples of subclassing for limitation in the Java library, however we could
imagine the following. Suppose one wanted to create the class Set, in a fashion similar to
the way the class Stack is subclassed from Vector. However, you also wanted to ensure that
only Set operations were used on the set, and not vector operations. One way to accomplish
this would be to override the undesired methods, so that if they were executed they would
140 CHAPTER 8. UNDERSTANDING INHERITANCE

generate an exception.1
class Set extends Vector f
// methods addElement, removeElement, contains
// isEmpty and size
// are all inherited from vector
public int indexOf (Object obj)
f throw new IllegalOperation("indexOf") g

public int elementAt (int index)


f throw new IllegalOperation("indexOf") g
g

Where IllegalOperation is a subclass of Exception:


class IllegalOperation extends Exception f
IllegalOperation (String str) f super(str) g
g

Subclassing for limitation is characterized by the presence of methods that take a pre-
viously permitted operation and makes it illegal. Because subclassing for limitation is an
explicit contravention of the principle of substitutability, and because it builds subclasses
that are not subtypes, it should be avoided whenever possible.

8.4.6 Inheritance for Combination


When discussion abstract concepts, it is common for a new abstraction to be formed as a
combination of features from two or more abstractions. A teaching assistant, for example,
may have characteristics of both a teacher and a student, and can therefore logically behave
as both. The ability of a class to inherit from two or more parent classes is known as multiple
inheritance.
Although the Java language does not permit a subclass to be formed by inheritance
from more than one parent class, several approximations to the concept are possible. For
example, it is common for a new class to both extend an existing class and implement an
interface. We saw this in the example of the class Hole that both extended class Ball and
implemented the interface for PinBallTarget.
class Hole extends Ball implements PinBallTarget f
1 In actuality, the methods indexOf and elementAt are declared as nal in class Vector, so this example
will not compile. But it does illustrate the concept.
8.5. MODIFIERS AND INHERITANCE 141

...
g

It is also possible for classes to implement more than one interface, and thus be viewed
as a combination of the two categories. Many examples occur in the input/output sections
of the Java Library. A RandomAccessFile, for example, implements both the DataInput and
DataOutput protocols.

8.4.7 Summary of the Forms of Inheritance


We can summarize the various forms of inheritance by the following table:
 Specialization. The child class is a special case of the parent class in other words, the
child class is a subtype of the parent class.
 Specication. The parent class denes behavior that is implemented in the child class
but not in the parent class.
 Construction. The child class makes use of the behavior provided by the parent class,
but is not a subtype of the parent class.
 Extension. The child class adds new functionality to the parent class, but does not
change any inherited behavior.
 Limitation. The child class restricts the use of some of the behavior inherited from the
parent class.
 Combination. The child class inherits features from more than one parent class. Al-
though multiple inheritance is not supported directly by Java, it can be simulated
in part by classes that use both inheritance and implementation of an interface, or
implement two or more interfaces.
The Java language implicitly assumes that subclasses are also subtypes. This means that
an instance of a subclass can be assigned to a variable declared as the parent class type.
Methods in the child class that have the same name as those in the parent class override the
inherited behavior. We have seen that this assumption that subclasses are subtypes is not
always valid, and creating subclasses that are not subtypes is a frequent source of program
error.

8.5 Modiers and Inheritance


The language Java provides several modiers that can be used to alter aspects of the inher-
itance process. For example, in the case studies in earlier chapters, we made extensive use
of the visibility (or access control) modiers public, protected and private.
142 CHAPTER 8. UNDERSTANDING INHERITANCE

 A public feature (data eld or method) can be accessed outside the class denition. A
public class can be accessed outside the package in which it is declared.
 A protected feature can be accessed only within the class denition in which it appears,
or within the denition of subclasses.
 A private feature can be accessed only within the class denition in which it appears.
We have seen from our rst case studies how both methods and data elds can be
declared as static. A static eld is shared by all instances of a class. A static method can be
invoked even when no instance of the class has been created. Static data elds and methods
are inherited in the same manner as non-static items, except that static methods cannot be
overridden.
Both methods and classes can be declared to be abstract. An abstract class cannot be
instanciated. That is, it is not legal to create an instance of an abstraction class using the
operator new. Such a class can only be used as a parent class, to create a new type of object.
Similarly, an abstract method must be overridden by a subclass.
An alternative modier, nal, is the opposite of abstract. When applied to a class, the
keyword indicates that the class cannot be subclassed. Similarly, when applied to a method,
the keyword indicates that the method cannot be overridden. Thus, the user is guaranteed
that the behavior of the class will be as dened and not modied by a later subclass.
final class newClass extends oldClass f
...
g

We have seen that program constants are generally dened by variables that are both
static and nal:
class CannonGame extends Frame f
...
public static final int FrameWidth = 600
public static final int FrameHeight = 400
...
g

Optimizing compilers can sometimes make use of the fact that a data eld, class or
method is declared as nal, and generate better code than would otherwise be possible.

8.6 Programming as a Multi Person Activity


When programs are constructed out of reusable, o-the-shelf components, programming
moves from an individual activity (one programmer and the computer) to a community
8.7. THE BENEFITS OF INHERITANCE 143

eort. A programmer may operate both as the developer of new abstractions, and as the
user of a software system created by an earlier programmer. The reader should not confuse
the term user when applied to a programmer with the same term denoting the application
end-user. Similarly, we will often speak of the organization of several objects by describing
a client object, that is requesting the services of a provider. Again, the client in this case
is likely a programmer (or the code being developed by a programmer) making use of the
services developed by an earlier programmer. This should not be confused with the idea of
client/sever computing, as described in Chapter 2.

8.7 The Benets of Inheritance


In this section we will describe some of the many important benets of the proper use of
inheritance.

8.7.1 Software Reusability


When behavior is inherited from another class, the code that provides that behavior does
not have to be rewritten. This may seem obvious, but the implications are important.
Many programmers spend much of their time rewriting code they have written many times
before{for example, to search for a pattern in a string or to insert a new element into a
table. With object-oriented techniques, these functions can be written once and reused.

8.7.2 Increased Reliability


Code that is executed frequently will tend to have fewer bugs then code that executed
infrequently. When the same components are used in two or more applications, the code
will be exercised more than code that is developed for a single application. Thus, bugs
in such code tend to be more quickly discovered, and latter applications gain the benet
of using components are more error free. Similarly, the costs of maintenance of shared
components can be split among many projects.

8.7.3 Code Sharing


Code sharing can occur on several levels with object-oriented techniques. On one level,
many users or projects can use the same classes. (Brad Cox Cox 1986] calls these software-
ICs, in analogy to the integrated circuits used in hardware design). Another form of sharing
occurs when two or more classes developed by a single programmer as part of a project
inherit from a single parent class. For example, a Set and an Array may both be considered
a form of Collection. When this happens, two or more types of objects will share the code
that they inherit. This code needs to be written only once and will contribute only once to
the size of the resulting program.
144 CHAPTER 8. UNDERSTANDING INHERITANCE

8.7.4 Consistency of Interface


When two or more classes inherit from the same superclass, we are assured that the behavior
they inherit will be the same in all cases. Thus, it is easier to guarantee that interfaces to
similar objects are in fact similar, and that the user is not presented with a confusing
collection of objects that are almost the same but behave, and are interacted with, very
dierently.

8.7.5 Software Components


Inheritance provides programmers with the ability to construct reusable software compo-
nents. The goal is to permit the development of new and novel applications that nevertheless
require little or no actual coding. The Java library provides a rich collection of software
components for use in the development of applications.

8.7.6 Rapid Prototyping


When a software system is constructed largely out of reusable components, development
time can be concentrated on understanding the new and unusual portion of the system.
Thus, software systems can be generated more quickly and easily, leading to a style of
programming known as rapid prototyping or exploratory programming. A prototype system is
developed, users experiment with it, a second system is produced that is based on experience
with the rst, further experimentation takes place, and so on for several iterations. Such
programming is particularly useful in situations where the goals and requirements of the
system are only vaguely understood when the project begins.

8.7.7 Polymorphism and Frameworks


Software produced conventionally is generally written from the bottom up, although it may
be designed from the top down. That is, the lower-level routines are written, and on top
of these slightly higher abstractions are produced, and on top of these even more abstract
elements are generated. This process is like building a wall, where every brick must be laid
on top of an already laid brick.
Normally, code portability decreases as one moves up the levels of abstraction. That
is, the lowest-level routines may be used in several dierent projects, and perhaps even
the next level of abstraction may be reused, but the higher-level routines are intimately
tied to a particular application. The lower-level pieces can be carried to a new system and
generally make sense standing on their own the higher-level components generally make
sense (because of declarations or data dependencies) only when they are built on top of
specic lower-level units.
Polymorphism in programming languages permits the programmer to generate high-level
reusable components that can be tailored to t dierent applications by changes in their
8.8. THE COSTS OF INHERITANCE 145

low-level parts. The Java AWT is an example of a large software framework that relies on
inheritance and substitutability for its operation.

8.7.8 Information Hiding


A programmer who reuses a software component needs only to understand the nature of
the component and its interface. It is not necessary for the programmer to have detailed
information concerning matters such as the techniques used to implement the component.
Thus, the interconnectedness between software systems is reduced. We earlier identied
the interconnected nature of conventional software as being one of the principle causes of
software complexity.

8.8 The Costs of Inheritance


Although the benets of inheritance in object-oriented programming are great, almost noth-
ing is without cost of one sort or another. For this reason, we must consider the cost of
object-oriented programming techniques, and in particular the cost of inheritance.

8.8.1 Execution Speed


It is seldom possible for general-purpose software tools to be as fast as carefully hand-
crafted systems. Thus, inherited methods, which must deal with arbitrary subclasses, are
often slower than specialized code.
Yet, concern about eciency is often misplaced.2 First, the dierence is often small.
Second, the reduction in execution speed may be balanced by an increase in the speed of
software development. Finally, most programmers actually have little idea of how execution
time is being used in their programs. It is far better to develop a working system, monitor
it to discover where execution time is being used, and improve those sections, than to spend
an inordinate amount of time worrying about eciency early in a project.

8.8.2 Program Size


The use of any software library frequently imposes a size penalty not imposed by systems
constructed for a specic project. Although this expense may be substantial, as memory
costs decrease the size of programs becomes less important. Containing development costs
and producing high-quality and error-free code rapidly are now more important than limiting
the size of programs.
2 The following quote from an article by Bill Wulf o ers some apt remarks on the importance of e ciency:
\More computing sins are committed in the name of e ciency (without necessarily achieving it) than for
any other single reason{including blind stupidity" Wulf 1972].
146 CHAPTER 8. UNDERSTANDING INHERITANCE

8.8.3 Message-Passing Overhead


Much has been made of the fact that message passing is by nature a more costly operation
than simple procedure invocation. As with overall execution speed, however, overconcern
about the cost of message passing is frequently penny-wise and pound-foolish. For one thing,
the increased cost is often marginal{perhaps two or three additional assembly-language
instructions and a total time penalty of 10 percent. This increased cost, like others, must
be weighed against the many benets of the object-oriented technique.

8.8.4 Program Complexity


Although object-oriented programming is often touted as a solution to software complexity,
in fact, overuse of inheritance can often simply replace one form of complexity with another.
Understanding the control ow of a program that uses inheritance may require several
multiple scans up and down the inheritance graph. This is what is known as the yo-yo
problem, which we will discuss in more detail in a later chapter.

8.9 Chapter Summary


Inheritance is a mechanism for relating a new software abstraction being developed to an
older, existing abstraction. By stating that the new component inherits (or extends) the
older abstraction, the programmer means that all the public and protected properties of
the original class are also now part of the new abstraction. In addition, the new class can
add new data elds and behavior, and can override methods that are inherited from the
original class. Interfaces are a closely related mechanism, which tie the concrete realization
of behavior to an abstract description.
All classes in Java use inheritance. If not explicitly stated, classes are assumed to inherit
from the fundamental root class Object.
Inheritance is tied to the principle of substitutability. A variable that is declared as one
class can be assigned a value that created from a child class. A similar mechanism also works
with interfaces. A class that can be used in lieu of another class is said to be a subtype.
Java implicitly assumes that all subclasses are subtypes. However, this need not be true (a
subclass can override a method in an incompatible fashion, for example). Subtypes can also
be constructed from interfaces, avoiding subclasses altogether.
There are many dierent types of inheritance, used for dierent purposes. Variations
include specialization, specication, construction, extension, limitation, and combination.
A variety of modiers alter the meaning of inheritance. A private feature is not inherited
by subclasses. A static feature (data eld or method) is shared by all instances. An abstract
method must be overridden. A nal feature (data eld or method) cannot be overridden.
EXERCISES 147

Study Questions
1. Give an intuitive description of inheritance.
2. What does it mean for a method to override an inherited method?
3. What is the name of the root class for all objects in Java?
4. What behavior is provided by the root class in Java?
5. What does it mean to say that child classes are substitutable for parent classes in
Java?
6. What is the dierence between a subclass and a subtype?
7. What are the characteristics of inheritance for specialization?
8. What are the characteristics of inheritance for specication? How does this dier from
inheritance for specialization?
9. What are the characteristics of inheritance for construction? Why is this not generally
considered to be a good use of inheritance?
10. What are the characteristics of inheritance for extension?
11. What are the characteristics of inheritance for limitation? Why is this not generally
considered to be a good use of inheritance?
12. Why would it not make sense for a method in Java to be declared both abstract and
nal ?
13. What are some of the benets of developing classes using inheritance, rather than
developing each new class from scratch?
14. What are some of the costs of using inheritance for software development?

Exercises
1. Suppose you were required to program a project in a non-object oriented language,
such as Pascal or C. How would you simulate the notion of classes and methods? How
would you simulate inheritance? Could you support multiple inheritance? Explain
your answer.
2. We noted that the execution overhead associated with message passing is typically
greater than the overhead associated with a conventional procedure call. How might
you measure these overheads? For a language that supports both classes and proce-
dures (such as C++ or Object Pascal), devise an experiment to determine the actual
performance penalty of message passing.
148 CHAPTER 8. UNDERSTANDING INHERITANCE

3. Consider the three geometric concepts of a line (innite in both directions), a ray
(xed at a point, innite in one direction), and a segment (a portion of a line with
xed end points). How might you structure classes representing these three concepts
in an inheritance hierarchy? Would your answer dier if you concentrated more on
the data representation or more on the behavior? Characterize the type of inheritance
you would use. Explain the reasoning behind your design.
4. Why is the example used in the following explanation not a valid illustration of inher-
itance?
Perhaps the most powerful concept in object-oriented programming sys-
tems is inheritance. Objects can be created by inheriting the properties of
other objects, thus removing the need to write any code whatsoever! Sup-
pose, for example, a program is to process complex numbers consisting of
real and imaginary parts. In a complex number, the real and imaginary
parts behave like real numbers, so all of the operations (+, -, /, *, sqrt,
sin, cos, etc.) can be inherited from the class of objects call REAL, instead
of having to be written in code. This has a major impact on programmer
productivity.
Chapter 9

A Case Study: Solitaire


A program for playing the card game solitaire will illustrate the utility and power of inher-
itance and overriding. A major part of the game of Solitaire is moving cards from one pile
of cards to another. There are a number of dierent types of card piles, each having some
features in common with the others, while other features are unique. A common parent
class can therefore be used to capture the common elements, while inheritance and over-
riding can be used to produce specialized types of piles. The development of this program
will illustrate how inheritance can be used to simplify the creation of these components and
ensure that they can all be manipulated in a similar fashion.

9.1 The Class Card


To create a card game, we rst need to dene a class to represent a playing card. Each
instance of class Card (Figure 9.1) maintains a suit value and a rank. To prevent modication
of these values, the instance variables maintaining them are declared private and access is
mediated through accessor functions. The value of the suit and rank elds are set by the
constructor for the class. Integer constant values (in Java dened by the use of nal static
constants) are dened for the height and width of the card as well as for the suits. Another
function permits the user to determine the color of the card. The Java library class Color
is used to represent the color abstraction. The Color class denes constants for various
colors. The values Color.red, Color.black, Color.yellow and Color.blue are used in the solitaire
program.
There are important reasons that data values representing suit and rank should be
returned through an accessor function, as opposed to dening the data elds s and r as
public and allowing direct access to the data values. One of the most important is that
access through a function ensures that the rank and suit characteristics of a card can be
read but not altered once the card has been created.
149
150 CHAPTER 9. A CASE STUDY: SOLITAIRE

import java.awt.

public class Card f


// public constants for card width and suits
public final static int width = 50
public final static int height = 70
public final static int heart = 0
public final static int spade = 1
public final static int diamond = 2
public final static int club = 3
// internal data elds for rank and suit
private boolean faceup
private int r
private int s

// constructor
Card (int sv, int rv) f s = sv r = rv faceup = false g

// access attributes of card


public final int rank () f return r g

public final int suit() f return s g

public final boolean faceUp() f return faceup g

public final void flip() f faceup = ! faceup g

public final Color color() f


if (faceUp())
if (suit() == heart jj suit() == diamond)
return Color.red
else
return Color.black
return Color.yellow
g

public void draw (Graphics g, int x, int y) f ... g


g

Figure 9.1: Description of the class card.


9.2. THE GAME 151

Note that many of the methods in the Card abstraction have been declared as nal. This
modier serves two important purposes. First, it is a documentation aid, signaling to the
reader of the listing that the methods cannot be overridden by subclasses. Second, in some
situations the Java compiler can optimize the invocation of nal methods, creating faster
code than could be generated for the execution of non-nal methods.
The only other actions a card can perform, besides setting and returning the state of
the card, are to ip over and to display itself. The function ip() is a one-line function
that simply reverses the value held by an instance variable. The drawing function is more
complex, making use of the drawing facilities provided by the Java standard application
library. As we have seen in the earlier case studies, the application library provides a data
type called Graphics that provides a variety of methods for drawing lines and common shapes,
as well as for coloring. An argument of this type is passed to the draw function, as are the
integer coordinates representing the upper left corner of the card.
The card images are simple line drawings, as shown below. Diamonds and hearts are
drawn in red, spades and clubs in black. The hash marks on the back are drawn in yellow.
A portion of the procedure for drawing a playing card is shown in Figure 9.2.

A;@ ;@
3 A
; @; @
JJ  A
 A
J A
J   D A
J  D

The most important feature of the playing-card abstraction is the manner in which each
card is responsible for maintaining within itself all card-related information and behaviors.
The card knows both its value and how to draw itself. In this manner the information is
encapsulated and isolated from the application using the playing card. If, for example, one
were to move the program to a new platform using dierent graphics facilities, only the draw
method within the class itself would need to be altered.

9.2 The Game


The version of solitaire we will describe is known as klondike. The countless variations on
this game make it probably the most common version of solitaire so much so that when
you say \solitaire," most people think of klondike. The version we will use is that described
in Morehead 1949] in the exercises we will explore some of the common variations.
152 CHAPTER 9. A CASE STUDY: SOLITAIRE
public class Card f
...
public void draw (Graphics g, int x, int y) f
String names] = f"A", "2", "3", "4", "5", "6",
"7", "8", "9", "10", "J", "Q", "K"g
// clear rectangle, draw border
g.clearRect(x, y, width, height)
g.setColor(Color.blue)
g.drawRect(x, y, width, height)
// draw body of card
g.setColor(color())
if (faceUp()) f
g.drawString(namesrank()], x+3, y+15)
if (suit() == heart) f
g.drawLine(x+25, y+30, x+35, y+20)
g.drawLine(x+35, y+20, x+45, y+30)
g.drawLine(x+45, y+30, x+25, y+60)
g.drawLine(x+25, y+60, x+5, y+30)
g.drawLine(x+5, y+30, x+15, y+20)
g.drawLine(x+15, y+20, x+25, y+30)
g
else if (suit() == spade) f ... g
else if (suit() == diamond) f ... g
else if (suit() == club) f
g.drawOval(x+20, y+25, 10, 10)
g.drawOval(x+25, y+35, 10, 10)
g.drawOval(x+15, y+35, 10, 10)
g.drawLine(x+23, y+45, x+20, y+55)
g.drawLine(x+20, y+55, x+30, y+55)
g.drawLine(x+30, y+55, x+27, y+45)
g
g
else f // face down
g.drawLine(x+15, y+5, x+15, y+65)
g.drawLine(x+35, y+5, x+35, y+65)
g.drawLine(x+5, y+20, x+45, y+20)
g.drawLine(x+5, y+35, x+45, y+35)
g.drawLine(x+5, y+50, x+45, y+50)
g
g
g

Figure 9.2: Procedure to draw a playing card.


9.2. THE GAME 153

The layout of the game is shown in Figure 9.3. A single standard pack of 52 cards is
used. The tableau, or playing table, consists of 28 cards in 7 piles. the rst pile has 1 card,
the second 2, and so on up to 7. The top card of each pile is initially face up all other cards
are face down.

Suit Piles
Discard Deck

Table Piles

Figure 9.3: Layout for the solitaire game.


The suit piles (sometimes called foundations) are built up from aces to kings in suits.
They are constructed above the tableau as the cards become available. The object of the
game is to build all 52 cards into the suit piles.
The cards that are not part of the tableau are initially all in the deck. Cards in the deck
are face down, and are drawn one by one from the deck and placed, face up, on the discard
pile. From there, they can be moved onto either a tableau pile or a foundation. Cards are
drawn from the deck until the pile is empty at this point, the game is over if no further
moves can be made.
Cards can be placed on a tableau pile only on a card of next-higher rank and opposite
color. They can be placed on a foundation only if they are the same suit and next higher
154 CHAPTER 9. A CASE STUDY: SOLITAIRE

card or if the foundation is empty and the card is an ace. Spaces in the tableau that arise
during play can be lled only by kings.
The topmost card of each tableau pile and the topmost card of the discard pile are always
available for play. The only time more than one card is moved is when an entire collection
of face-up cards from a tableau (called a build) is moved to another tableau pile. This can
be done if the bottommost card of the build can be legally played on the topmost card of
the destination. Our initial game will not support the transfer of a build, but we will discuss
this as a possible extension. The topmost card of a tableau is always face up. If a card is
moved from a tableau, leaving a face-down card on the top, the latter card can be turned
face up.
From this short description, it is clear that the game of solitaire mostly involves manip-
ulating piles of cards. Each type of pile has many features in common with the others and
a few aspects unique to the particular type. In the next section, we will investigate in detail
how inheritance can be used in such circumstances to simplify the implementation of the
various card piles by providing a common base for the generic actions and permitting this
base to be redened when necessary.

9.3 Card Piles{Inheritance in Action


Much of the behavior we associate with a card pile is common to each variety of pile in
the game. For example, each pile maintains a collection of the cards in the pile (held in
a Stack), and the operations of inserting and deleting elements from this collection are
common. Other operations are given default behavior in the class CardPile, but they are
sometimes overridden in the various subclasses. The class CardPile is shown in Figure 9.4.
Each card pile maintains the coordinate location for the upper left corner of the pile,
as well as a Stack. The stack is used to hold the cards in the pile. All three of these
values are set by the constructor for the class. The data elds are declared as protected
and thus accessible to member functions associated with this class and to member functions
associated with subclasses.
The three functions top(), pop(), and isEmpty() manipulate the list of cards, using func-
tions provided by the Stack utility class. Note that these three methods have been declared
as nal, and can not therefore be overridden in subclasses.
The topmost card in a pile is returned by the function top(). This card will be the last
card in the underlying container. Note that the function peek() provided by the Stack class
returns a value declared as Object. This result must be cast to a Card value before it can be
returned as the result.
The method pop() uses the similarly named operation provided by the underlying stack.
The stack method throws an exception if an attempt is made to remove an element from an
empty stack. The pop() method in the class CardPile catches the exception, and returns a
null value in this situation.
The ve operations that are not declared nal are common to the abstract notion of our
9.3. CARD PILES{INHERITANCE IN ACTION 155

import java.util.Stack
import java.util.EmptyStackException

public class CardPile f


protected int x // coordinates of the card pile
protected int y
protected Stack thePile // the collection of cards

CardPile (int xl, int yl) f x = xl y = yl thePile = new Stack() g

public final Card top() f return (Card) thePile.peek() g

public final boolean isEmpty() f return thePile.empty() g

public final Card pop() f


try f
return (Card) thePile.pop()
g catch (EmptyStackException e) f return null g
g

// the following are sometimes overridden


public boolean includes (int tx, int ty) f
return x <= tx && tx <= x + Card.width &&
y <= ty && ty <= y + Card.height
g

public void select (int tx, int ty) f g

public void addCard (Card aCard) f thePile.push(aCard) g

public void display (Graphics g) f


g.setColor(Color.blue)
if (isEmpty()) g.drawRect(x, y, Card.width, Card.height)
else top().draw(g, x, y)
g

public boolean canTake (Card aCard) f return false g


g

Figure 9.4: Description of the class CardPile.


156 CHAPTER 9. A CASE STUDY: SOLITAIRE

card piles, but they dier in details in each case. For example, the function canTake(Card)
asks whether it is legal to place a card on the given pile. A card can be added to a foundation
pile, for instance, only if it is an ace and the foundation is empty, or if the card is of the
same suit as the current topmost card in the pile and has the next-higher value. A card
can be added to a tableau pile, on the other hand, only if the pile is empty and the card is
a king, or if it is of the opposite color as the current topmost card in the pile and has the
next lower value.
The actions of the ve non-nal functions dened in CardPile can be characterized as
follows:
includes {Determines if the coordinates given as arguments are contained within the bound-
aries of the pile. The default action simply tests the topmost card this is overridden
in the tableau piles to test all card values.
canTake {Tells whether a pile can take a specic card. Only the tableau and suit piles can
take cards, so the default action is simply to return no this is overridden in the two
classes mentioned.
addCard {Adds a card to the card list. It is redened in the discard pile class to ensure that
the card is face up.
display {Displays the card deck. The default method merely displays the topmost card of
the pile, but is overridden in the tableau class to display a column of cards. The top
half of each hidden card is displayed. So that the playing surface area is conserved,
only the topmost and bottommost face-up cards are displayed (this permits us to give
denite bounds to the playing surface).
select {Performs an action in response to a mouse click. It is invoked when the user selects
a pile by clicking the mouse in the portion of the playing eld covered by the pile. The
default action does nothing, but is overridden by the table, deck, and discard piles to
play the topmost card, if possible.
The following table illustrates the important benets of inheritance. Given ve oper-
ations and ve classes, there are 25 potential methods we might have had to dene. By
making use of inheritance we need to implement only 13. Furthermore, we are guaranteed
that each pile will respond in the same way to similar requests.
CardPile SuitPile DeckPile DiscardPile TableauPile
includes  
canTake   
addCard  
display  
select    
9.3. CARD PILES{INHERITANCE IN ACTION 157
class SuitPile extends CardPile f

SuitPile (int x, int y) f super(x, y) g

public boolean canTake (Card aCard) f


if (isEmpty())
return aCard.rank() == 0
Card topCard = top()
return (aCard.suit() == topCard.suit()) &&
(aCard.rank() == 1 + topCard.rank())
g
g

Figure 9.5: The class SuitPile.

9.3.1 The Suit Piles


We will examine each of the subclasses of CardPile in detail, pointing out various uses of
object-oriented features as they are encountered. The simplest subclass is the class SuitPile,
shown in Figure 9.5, which represents the pile of cards at the top of the playing surface, the
pile being built up in suit from ace to king.
The class SuitPile denes only two methods. The constructor for the class takes two
integer arguments and does nothing more than invoke the constructor for the parent class
CardPile. Note the use of the keyword super to indicate the parent class. The method
canTake determines whether or not a card can be placed on the pile. A card is legal if the
pile is empty and the card is an ace (that is, has rank zero) or if the card is the same suit
as the topmost card in the pile and of the next higher rank (for example, a three of spades
can only be played on a two of spades).
All other behavior of the suit pile is the same as that of our generic card pile. When
selected, a suit pile does nothing. When a card is added it is simply inserted into the
collection of cards. To display the pile only the topmost card is drawn.

9.3.2 The Deck Pile


The DeckPile (Figure 9.6) maintains the original deck of cards. It diers from the generic
card pile in two ways. When constructed, rather than creating an empty pile of cards, it
creates the complete deck of 52 cards, inserting them in order into the collection. Once all
the cards have been created, the collection is then shu"ed. To do this, a random number
generator is rst created. This generator is provided by the Java utility class Random. A
loop then examines each card in turn, exchanging the card with another randomly selected
card. To produce the index of the latter card, the random number generator rst produces
a randomly selected integer value (using by the method nextInt). Since this value could
158 CHAPTER 9. A CASE STUDY: SOLITAIRE

potentially be negative, the math library function abs is called to make it positive. The
modular division operation is nally used to produce a randomly selected integer value
between 0 and 51.
A subtle feature to note is that we are here performing a random access to the elements
of a Stack. The conventional view of a stack does not allow access to any but the topmost
element. However, in the Java library the Stack container is constructed using inheritance
from the Vector class. Thus, any legal operation on a Vector, such as the method elementAt(),
can also be applied to a Stack.
The method select is invoked when the mouse button is used to select the card deck. If
the deck is empty, it does nothing. Otherwise, the topmost card is removed from the deck
and added to the discard pile.
Java does not have global variables. Where a value is shared between multiple instances
of similar classes, such as the various piles used in our solitaire game, an instance variable
can be declared static. As we will noted in Chapter 4, one copy of a static variable is created
and shared between all instances. In our present program, static variables will be used to
maintain all the various card piles. These will be held in an instance of class Solitaire, which
we will subsequently describe. To access these values we use a complete qualied name,
which includes the name of the class as well as the name of the variable. This is shown in
the select method in Figure 9.6, which refers to the variable Solitare.discardPile to access the
discard pile.

9.3.3 The Discard Pile


The class DiscardPile (Figure 9.7) is interesting in that it exhibits two very dierent forms of
inheritance. The select method overrides or replaces the default behavior provided by class
CardPile, replacing it with code that when invoked (when the mouse is pressed over the card
pile) checks to see if the topmost card can be played on any suit pile or, alternatively, on
any tableau pile. If the card cannot be played, it is kept in the discard pile.
The method addCard is a dierent sort of overriding. Here the behavior is a renement
of the default behavior in the parent class. That is, the behavior of the parent class is
completely executed, and, in addition, new behavior is added. In this case, the new behavior
ensures that when a card is placed on the discard pile it is always face up. After satisfying
this condition, the code in the parent class is invoked to add the card to the pile by passing
the message to the pseudo-variable named super.
Another form of renement occurs in the constructors for the various subclasses. Each
must invoke the constructor for the parent class to guarantee that the parent is properly
initialized before the constructor performs its own actions. The parent constructor is in-
voked by the pseudo-variable super being used as a function inside the constructor for the
child class. In Chapter 12 we will have much more to say about the distinction between
replacement and renement in overriding.
9.3. CARD PILES{INHERITANCE IN ACTION 159

class DeckPile extends CardPile f

DeckPile (int x, int y) f


// rst initialize parent
super(x, y)
// then create the new deck
// rst put them into a local pile
for (int i = 0 i < 4 i++)
for (int j = 0 j <= 12 j++)
addCard(new Card(i, j))

// then shue the cards


Random generator = new Random()
for (int i = 0 i < 52 i++) f
int j = Math.abs(generator.nextInt()) % 52
// swap the two card values
Object temp = thePile.elementAt(i)
thePile.setElementAt(thePile.elementAt(j), i)
thePile.setElementAt(temp, j)
g
g

public void select(int tx, int ty) f


if (isEmpty())
return
Solitare.discardPile.addCard(pop())
g
g

Figure 9.6: The class DeckPile.


160 CHAPTER 9. A CASE STUDY: SOLITAIRE

class DiscardPile extends CardPile f

DiscardPile (int x, int y) f super (x, y) g

public void addCard (Card aCard) f


if (! aCard.faceUp())
aCard.flip()
super.addCard(aCard)
g

public void select (int tx, int ty) f


if (isEmpty())
return
Card topCard = pop()
for (int i = 0 i < 4 i++)
if (Solitare.suitPilei].canTake(topCard)) f
Solitare.suitPilei].addCard(topCard)
return
g
for (int i = 0 i < 7 i++)
if (Solitare.tableaui].canTake(topCard)) f
Solitare.tableaui].addCard(topCard)
return
g
// nobody can use it, put it back on our list
addCard(topCard)
g
g

Figure 9.7: The class DiscardPile.


9.4. THE APPLICATION CLASS 161

9.3.4 The Tableau Piles


The most complex of the subclasses of CardPile is that used to hold a tableau, or table
pile. It is shown in Figures 9.8 and 9.9. Table piles dier from the generic card pile in the
following ways:
 When initialized (by the constructor), the table pile removes a certain number of cards
from the deck, placing them in its pile. The number of cards so removed is determined
by an additional argument to the constructor. The topmost card of this pile is then
displayed face up.
 A card can be added to the pile (method canTake) only if the pile is empty and the
card is a king, or if the card is the opposite color from that of the current topmost
card and one smaller in rank.
 When a mouse press is tested to determine if it covers this pile (method includes) only
the left, right, and top bounds are checked the bottom bound is not tested since the
pile may be of variable length.
 When the pile is selected, the topmost card is ipped if it is face down. If it is face
up, an attempt is made to move the card rst to any available suit pile, and then to
any available table pile. Only if no pile can take the card is it left in place.
 To display the pile, each card in the pile is drawn in turn, each moving down slightly.
To access the individual elements of the stack, an Enumeration is created. Enumeration
objects are provided by all the containers in the Java library, and allow one to easily
loop over the elements in the container.

9.4 The Application Class


Figure 9.10 shows the central class for the solitaire application. As in our earlier case
studies, the control is initially given to the static procedure named main, which creates
an instance of the application class. The constructor for the application creates a window
for the application, by constructing an instance of a nested class SolitareFrame that inherits
from the library class Frame. After invoking the init method, which performs the application
initialization, the window is given the message show, which will cause it to display itself.
We noted earlier that the variables maintaining the dierent piles, which are shared in
common between all classes, are declared as static data elds in this class. These data elds
are initialized in the method name init.
Arrays in Java are somewhat dierent from arrays in most languages. Java distinguishes
the three activities of array declaration, array allocation, and assignment to an array lo-
cation. Note that the declaration statements indicate only that the named objects are an
array and not that they have any specic bound. One of the rst steps in the initialization
162 CHAPTER 9. A CASE STUDY: SOLITAIRE

class TablePile extends CardPile f

TablePile (int x, int y, int c) f


// initialize the parent class
super(x, y)
// then initialize our pile of cards
for (int i = 0 i < c i++) f
addCard(Solitare.deckPile.pop())
g
// ip topmost card face up
top().flip()
g

public boolean canTake (Card aCard) f


if (isEmpty())
return aCard.rank() == 12
Card topCard = top()
return (aCard.color() != topCard.color()) &&
(aCard.rank() == topCard.rank() - 1)
g

public boolean includes (int tx, int ty) f


// don't test bottom of card
return x <= tx && tx <= x + Card.width &&
y <= ty
g

public void display (Graphics g) f


int localy = y
for (Enumeration e = thePile.elements() e.hashMoreElements() ) f
Card aCard = (Card) e.nextElement()
aCard.draw (g, x, localy)
localy += 35
g
g
...
g

Figure 9.8: The class TablePile, part 1.


9.4. THE APPLICATION CLASS 163

class TablePile extends CardPile f


...

public void select (int tx, int ty) f


if (isEmpty())
return

// if face down, then ip


Card topCard = top()
if (! topCard.faceUp()) f
topCard.flip()
return
g

// else see if any suit pile can take card


topCard = pop()
for (int i = 0 i < 4 i++)
if (Solitare.suitPilei].canTake(topCard)) f
Solitare.suitPilei].addCard(topCard)
return
g
// else see if any other table pile can take card
for (int i = 0 i < 7 i++)
if (Solitare.tableaui].canTake(topCard)) f
Solitare.tableaui].addCard(topCard)
return
g
// else put it back on our pile
addCard(topCard)
g
g

Figure 9.9: The class TablePile, part 2.


164 CHAPTER 9. A CASE STUDY: SOLITAIRE

public class Solitare f


static public DeckPile deckPile
static public DiscardPile discardPile
static public TablePile tableau  ]
static public SuitPile suitPile  ]
static public CardPile allPiles  ]
private Frame window

static public void main (String  ] args) f


Solitare world = new Solitare()
g

public Solitare () f
window = new SolitareFrame()
init()
window.show()
g

public void init () f


// rst allocate the arrays
allPiles = new CardPile13]
suitPile = new SuitPile4]
tableau = new TablePile7]
// then ll them in
allPiles0] = deckPile = new DeckPile(335, 30)
allPiles1] = discardPile = new DiscardPile(268, 30)
for (int i = 0 i < 4 i++)
allPiles2+i] = suitPilei] =
new SuitPile(15 + (Card.width+10)  i, 30)
for (int i = 0 i < 7 i++)
allPiles6+i] = tableaui] =
new TablePile(15+(Card.width+5)i, Card.height+35, i+1)
g

private class SolitareFrame extends Frame f ... g


g

Figure 9.10: The class Solitaire.


9.5. PLAYING THE POLYMORPHIC GAME 165

routine is to allocate space for the three arrays (the suit piles, the tableau, and the array
allPiles we will discuss shortly). The new command allocates space for the arrays, but does
not assign any values to the array elements.
The next step is to create the deck pile. Recall that the constructor for this class creates
and shu"es the entire deck of 52 cards. The discard pile is similarly constructed. A loop
then creates and initializes the four suit piles, and a second loop creates and initializes the
tableau piles. Recall that as part of the initialization of the tableau, cards are removed from
the deck and inserted in the tableau pile.
The inner class SolitareFrame, used to manage the application window, is shown in Fig-
ure 9.11. In addition to the cards, a button will be placed at the bottom of the window.
Listeners are created both for mouse events (see Chapter 7) and for the button. When
pressed, the button will invoke the button listener method. This method will reinitialize the
game, then repaint the window. Similarly, when the mouse listener is invoked (in response
to a mouse press) the collection of card piles will be examined, and the appropriate pile will
be displayed.

9.5 Playing the Polymorphic Game


Both the mouse listener and the repaint method for the application window make use of
the array allPiles. This array is used to represent all 13 card piles. Note that as each
pile is created it is also assigned a location in this array, as well as in the appropriate static
variable. We will use this array to illustrate yet another aspect of inheritance. The principle
of substitutability is used here: The array allPiles is declared as an array of CardPile, but in
fact is maintaining a variety of card piles.
This array of all piles is used in situations where it is not important to distinguish
between various types of card piles for example, in the repaint procedure. To repaint the
display, each dierent card pile is simply asked to display itself. Similarly, when the mouse is
pressed, each pile is queried to see if it contains the given position if so, the card is selected.
Remember, of the piles being queried here seven are tableau piles, four are foundations, and
the remaining are the discard pile and the deck. Furthermore, the actual code executed in
response to the invocation of the includes and select routines may be dierent in each call,
depending upon the type of pile being manipulated. In other object-oriented languages,
such methods are often described as virtual.
The use of a variable declared as an instance of the parent class holding a value from
a subclass is one aspect of polymorphism, a topic we will return to in more detail in a
subsequent chapter.
166 CHAPTER 9. A CASE STUDY: SOLITAIRE

private class SolitareFrame extends Frame f

private class RestartButtonListener implements ActionListener f


public void actionPerformed (ActionEvent e) f
init()
repaint()
g
g

private class MouseKeeper extends MouseAdapter f


public void mousePressed (MouseEvent e) f
int x = e.getX()
int y = e.getY()
for (int i = 0 i < 13 i++)
if (allPilesi].includes(x, y)) f
allPilesi].select(x, y)
repaint()
g
g
g

public SolitareFrame() f // constructor for window


setSize(600, 500)
setTitle("Solitaire Game")
addMouseListener (new MouseKeeper())
Button restartButton = new Button("New Game")
restartButton.addActionListener(new RestartButtonListener())
add("South", restartButton)
g

public void paint(Graphics g) f


for (int i = 0 i < 13 i++)
allPilesi].display(g)
g
g

Figure 9.11: The inner class SolitareFrame


9.6. BUILDING A MORE COMPLETE GAME 167

9.6 Building a More Complete Game


The solitaire game described here is minimal and exceedingly hard to win. A more realistic
game would include at least a few of the following variations:
 The method select in class TablePile would be extended to recognize builds. That
is, if the topmost card could not be played, the bottommost face-up card in the pile
should be tested against each tableau pile if it could be played, the entire collection
of face-up cards should be moved.
 Our game halts after one series of moves through the deck. An alternative would be
that when the user selected the empty deck pile (by clicking the mouse in the area
covered by the deck pile) the discard pile would be reshu"ed and copied back into the
deck, allowing execution to continue.
Various other alternatives are described in the exercises.

9.7 Chapter Summary


We have used a solitaire program as a case study to present many of the features and benets
of inheritance. The various dierent types of card piles found in the game can all be special-
ized from one common parent class. This parent class provides default behavior, which can
be overridden when a pile requires more specialized code. In the program presented here,
the default behavior is overridden less than half the time.
The following are some of the aspects of inheritance discussed in this chapter:
 By creating a common parent class, default behavior can be shared among several
dierent software components.
 Methods can be declared as nal, in which case they cannot be overridden in subclasses.
(We noted in the previous chapter that methods can also be declared as abstract, in
which case they must be overridden in subclasses).
 Overriding methods can be divided into those that replace the code inherited from
the parent, and those that rene the parent code. In the former case only the child
code is executed, while in the latter both the child and the parent code is executed.
In Java renement is specied by explicitly executing the parent function, using the
pseudo-variable super.
 Inheritance is tied to polymorphism through the concept of substitutability. An in-
stance of a child class can be assigned to a variable that is declared as a parent class
type. Using this idea, we can create a variable (or, in this case, an array of values)
that maintains any type of card pile.
168 CHAPTER 9. A CASE STUDY: SOLITAIRE

 When an overridden method is applied to a polymorphic variable, the code executed


is determined by the value the variable currently holds, not the declared type of the
variable. Such methods are sometimes described as virtual.

Study Questions
1. What data values are maintained by class Card? What behaviors can a card perform?
(That is, what methods are implemented by the class Card?)
2. Explain why the suit and rank data elds are declared as private.
3. What is a default constructor? What is a copy constructor?
4. What is an accessor function? What is the advantage of using an accessor function as
opposed to direct access to a data member?
5. What are the 13 dierent card piles that are used in the solitaire game?
6. What does it mean when a function is declared as nal?
7. Describe the ve non-nal functions implemented in class CardPile and overridden in
at least one child class.
8. How does the use of inheritance reduce the amount of code that would otherwise be
necessary to implement the various types of card piles?
9. Explain the dierence between overriding used for replacement and overriding used
for renement. Find another example of each in the methods associated with class
CardPile and its various subclasses.
10. Explain how polymorphism is exhibited in the solitaire game application.

Exercises
1. The solitaire game has been designed to be as simple as possible. A few features are
somewhat annoying, but can be easily remedied with more coding. These include the
following:
(a) The topmost card of a tableau pile should not be moved to another tableau pile
if there is another face-up card below it.
(b) An entire build should not be moved if the bottommost card is a king and there
are no remaining face-down cards.
For each, describe what procedures need to be changed, and give the code for the
updated routine.
EXERCISES 169

2. The following are common variations of klondike. For each, describe which portions
of the solitaire program need to be altered to incorporate the change.
(a) If the user clicks on an empty deck pile, the discard pile is moved (perhaps with
shu"ing) back to the deck pile. Thus, the user can traverse the deck pile multiple
times.
(b) Cards can be moved from the suit pile back into the tableau pile.
(c) Cards are drawn from the deck three at a time and placed on the discard pile in
reverse order. As before, only the topmost card of the discard pile is available
for playing. If fewer than three cards remain in the deck pile, all the remaining
cards (as many as that may be) are moved to the discard pile. (In practice, this
variation is often accompanied by variation 1, permitting multiple passes through
the deck).
(d) The same as variation 3, but any of the three selected cards can be played. (This
requires a slight change to the layout as well as an extensive change to the discard
pile class).
(e) Any royalty card, not simply a king, can be moved onto an empty tableau pile.
3. The game \thumb and pouch" is similar to klondike except that a card may be built
on any card of next-higher rank, of any suit but its own. Thus, a nine of spades can be
played on a ten of clubs, but not on a ten of spades. This variation greatly improves
the chances of winning. (According to Morehead Morehead 1949], the chances of
winning Klondike are 1 in 30, whereas the chances of winning thumb and pouch are
1 in 4.) Describe what portions of the program need to be changed to accommodate
this variation.
4. The game \whitehead" is supercially similar to klondike, in the sense that it uses the
same layout. However, uses dierent rules for when card can be played in the tableau:
(a) A card can be moved onto another faceup card in the tableau only if it has the
same color and is one smaller in rank. For example, a ve of spades can be played
on either a six of clubs or a six of spades, but not on a six of diamonds or a six
of hearts.
(b) A build can only be moved if all cards in the build are of the same suit.
Describe what portions of the program need to be changed to accommodate this
variation.
170 CHAPTER 9. A CASE STUDY: SOLITAIRE
Chapter 10

Mechanisms for Software Reuse


Object-oriented programming has been billed as the technology that will nally permit
software to be constructed from general-purpose reusable components. Writers such as
Brad Cox have even gone so far as to describe object orientation as heralding the \industrial
revolution" in software development Cox 1986]. While the reality may not quite match the
expectations of OOP pioneers, it is true that object-oriented programming makes possible
a level of software reuse that is orders of magnitude more powerful than that permitted by
previous software construction techniques. In this chapter, we will investigate the two most
common mechanisms for software reuse, which are known as inheritance and composition.
Inheritance in Java is made more exible by the presence of two dierent mechanisms,
interfaces and subclassing. In addition to contrasting inheritance and composition, we will
contrast inheritance performed using subclassing and inheritance performed using interfaces,
and relate all to the concept of substitutability.

10.1 Substitutability
The concept of substitutability fundamental to many of the most powerful software devel-
opment techniques in object-oriented programming. You will recall that substitutability
referred to the situations that occurs when a variable declared as one type is used to hold
a value derived from another type.
In Java, substitutability can occur either through the use of classes and inheritance, or
through the use of interfaces. We have seen examples of both in our earlier case studies. In
the Pin Ball game program described in Chapter 7, the variable targets was declared as a
PinBallTarget, but in fact held a variety of dierent types of values that were created using
subclasses of PinBallTarget. In the Solitaire program from Chapter 9, the variable allPiles
was declared as holding a CardPile, but in fact held values that were subclasses of CardPile,
such as DeckPile and DiscardPile:
171
172 CHAPTER 10. MECHANISMS FOR SOFTWARE REUSE

public class Solitare f


static public CardPile allPiles  ]

public void init () f


...
allPiles = new CardPile13]
// then ll them in
allPiles0] = new DeckPile(335, 30)
allPiles1] = new DiscardPile(268, 30)
g
...
g

We have also seen examples of substitutability arising through interfaces. An example


is the instance of the class FireButtonListener created in the Cannon-ball game (Chapter 6).
The class from which this value was dened was declared as implementing the interface
ActionListener. Because it implements the ActionListener interface, we can this value as
a parameter to a function (in this case, addActionListener) that expects an ActionListener
value.
class CannonWorld extends Frame f
...
private class FireButtonListener implements ActionListener f
public void actionPerformed (ActionEvent e) f
...
g
g

public CannonWorld () f
...
fire.addActionListener(new FireButtonListener())
g
g

We will return to the distinction between inheritance of classes and inheritance of inter-
faces in Section 10.1.2.

10.1.1 The Is-a Rule and the Has-a Rule


A commonly employed rule-of-thumb that can be used to understand when inheritance
is an appropriate software technique is known colloquially as is-a and has-a (or part-of )
10.1. SUBSTITUTABILITY 173

relationship.
The is-a relationship holds between two concepts when the rst is a specialized instance
of the second. That is, for all practical purposes the behavior and data associated with the
more specic idea form a subset of the behavior and data associated with the more abstract
idea. For example, all the examples of inheritance we described in the early chapters satisfy
the is-a relationship (a Florist is-a Shopkeeper, a Dog is-a Mammal, a PinBall is-a Ball,
and so on). The relationship derives its name from a simple rule of thumb that tests the
relationship. To determine if concept X is a specialized instance of concept Y, simply form
the English sentence \An X is a Y". If the assertion \sounds correct," that is, if it seems to
match your everyday experience, you may judge that X and Y have the is-a relationship.
The has-a relationship, on the other hand, holds when the second concept is a component
of the rst but the two are not in any sense the same thing, no matter how abstract the
generality. For example, a Car has-a Engine, although clearly it is not the case that a Car
is-a Engine or that an Engine is-a Car. A Car, however, is-a Vehicle, which in turn is-a
MeansOfTransportation. Once again, the test for the has-a relationship is to simply form the
English sentence \An X has a Y", and let common sense tell you whether the result sounds
reasonable.
Most of the time, the distinction is clear-cut. But, sometimes it may be subtle or may
depend on circumstances. In Section 10.2 we will use one such indenite case to illustrate
the two software development techniques that are naturally tied to these two relationships.

10.1.2 Inheritance of Code and Inheritance of Behavior


There are at least two dierent ways in which a concept can satisfy the is-a relationship with
another concept, and these are re ected in two dierent mechanisms in the Java language.
Inheritance is the mechanism of choice when two concepts share structure or code relationship
with each other, while an interface is the more appropriate technique when two concepts
share the specication of behavior, but no actual code.
This can be illustrated with examples from the Java run-time library. The class Frame,
from which most Java windows inherit, provides a great deal of code, in the form of methods
that are inherited and used without being overridden. Thus, inheritance using the extends
modier in the class heading is the appropriate mechanism to use in this situation.
// a cannon game is a type of Frame
public class CannonGame extends Frame f
...
g

On the other hand, the characteristics needed for an ActionListener (the object type that
responds to button presses) can be described by a single method, and the implementation
of that method cannot be predicted, since it diers from one application to another. Thus,
174 CHAPTER 10. MECHANISMS FOR SOFTWARE REUSE

an interface is used to describe only the necessary requirements, and no actual behavior is
inherited by a subclass that implements the behavior.
class CannonWorld extends Frame f
...
// a re button listener implements the action listener interface
private class FireButtonListener implements ActionListener f
public void actionPerformed (ActionEvent e) f
...
g
g
g

In general, the class-subclass relationship should be used whenever a subclass can usefully
inherit either code, data values, or behavior from the parent class. The interface mechanism
should be used when the child class inherits only the specication of the expected behavior,
but no actual code.

10.2 Composition and Inheritance Described


Two dierent techniques for software reuse are composition and inheritance. We have explic-
itly noted uses of inheritance in earlier chapters, and have several places used composition as
well, although we have not pointed it out. One way to view these two dierent mechanisms
is as manifestations of the has-a rule and the is-a rule, respectively.
Although in most situations the distinction between is-a and has-a is clear-cut, it does
happen occasionally that it can be dicult to determine which mechanism is most appro-
priate to use in a particular situation. By examining one such indenite case, we can more
easily point out the dierences between the use of inheritance and the use of composition.
The example we will use is taken from the Java library, and concerns the development of a
Stack abstraction from an existing Vector data type.
The Vector data type is described in detail in Chapter 20. While abstractly a vector
is most commonly thought of as an indexed collection, the Java implementation also per-
mits values to be added or removed from the end of the collection, growing and shrinking
the container as necessary. The particular methods of interest for this discussion can be
described as follows:
class Vector f
// see if collection is empty
public boolean isEmpty () f ... g

// return size of collection


10.2. COMPOSITION AND INHERITANCE DESCRIBED 175

public int size () f ... g

// add element to end of collection


public void addElement (Object value) f ... g

// return last element in collection


public Object lastElement () f ... g

// remove element at given index


public Object removeElementAt (int index) f ... g

...
g

A stack is an abstract data type that allows elements to be added or removed from one
end only. If you think about a stack of dishes sitting on a counter you can get a good
intuition. It is easy to access the topmost dish, or to place a new dish on the top. It is much
more dicult to access any dish other than the topmost dish. In fact, it might be that the
only way to do this is to remove dishes one by one until you reach the dish you want.
The Stack abstractions dened here will be slightly simpler than the version provided by
the Java library. In particular, the library abstraction will throw an exception if an attempt
is made to access or remove an element from an empty stack, a condition we will ignore.
The Java library stack routine names the empty test method empty, instead of the method
isEmpty from class Vector, and nally the Stack abstraction provides a method to search the
stack to determine if it includes a given element. We will not describe this method here.

10.2.1 Using Composition


We will rst investigate how the stack abstraction can be formed with composition. Recall
from our earlier discussion that an object is simply an encapsulation of data values and
behavior. When composition is employed to reuse an existing data abstraction in the de-
velopment of a new data type, a portion of the state of the new data structure is simply an
instance of the existing structure. This is illustrated in Figure 10.1, where the data type
Stack contains a private instance eld named theData, which is declared to be of type Vector.
Because the Vector abstraction is stored as part of the data area for our stack, it must be
initialized in the constructor. The constructor for class Stack allocates space for the vector,
giving a value to the variable theData.
Operations that manipulate the new structure are implemented by making use of the
existing operations provided for the earlier data type. For example, the implementation
of the isEmpty operation for our stack data structure simply invokes the similarly named
function already dened for vectors. The peek operation is known by a dierent name, but
176 CHAPTER 10. MECHANISMS FOR SOFTWARE REUSE

class Stack f
private Vector theData

public Stack ()
f theData = new Vector() g

public boolean isEmpty ()


f return theData.isEmpty() g

public Object push (Object newValue)


f theData.addElement (newValue) g

public Object peek ()


f return theData.lastElement() g

public Object pop () f


Object result = theData.lastElement()
theData.removeElementAt(theData.size()-1)
return result
g
g

Figure 10.1: A Stack created using Composition


10.2. COMPOSITION AND INHERITANCE DESCRIBED 177

the task is already provided by the lastElement operation in the Vector class. Similarly, the
push operation is simply performed by executing an addElement on the vector.
The only operation that is slightly more complex is popping an element from the stack.
This involves using two methods provided by the Vector class, namely obtaining the topmost
element, and removing it from the collection. Notice that to remove the element we must
rst determine its index position, then remove the element by naming its position.
The important point to emphasize is the fact that composition provides a way to leverage
o an existing software component in the creation of a new application. By use of the
existing Vector class, the majority of the dicult work in managing the data values for our
new component have already been addressed.
On the other hand, composition makes no explicit or implicit claims about substitutabil-
ity. When formed in this fashion, the data types Stack and Vector are entirely distinct and
neither can be substituted in situations where the other is required.

10.2.2 Using Inheritance


An entirely dierent mechanism for software reuse in object-oriented programming is the
concept of inheritance with which a new class can be declared a subclass, or child class, of
an existing class. In this way, all data areas and functions associated with the original class
are automatically associated with the new data abstraction. The new class can, in addition,
dene new data values or new functions it can also override functions in the original class,
simply by dening new functions with the same names as those of functions that appear in
the parent class.
These possibilities are illustrated in the class description shown in Figure 10.2, which
implements a dierent version of the Stack abstraction. By naming the class Vector in the
class heading, we indicate that our Stack abstraction is an extension, or a renement, of
the existing class Vector. Thus, all data elds and operations associated with vectors are
immediately applicable to stacks as well.
The most obvious features of this class in comparison to the earlier are the items that
are missing. There are no local data elds. There is no constructor, since no local data
values need be initialized. The method isEmpty need not be provided, since the method is
inherited already from the parent class Vector.
Compare this function with the earlier version shown in Figure 10.1. Both techniques are
powerful mechanisms for code reuse, but unlike composition, inheritance carries an implicit
assumption that subclasses are, in fact, subtypes. This means that instances of the new
abstraction should react similarly to instances of the parent class.
In fact, the version using inheritance provides more useful functionality than the version
using composition. For example, the method size in the Vector class yields the number of
elements stored in a vector. With the version of the Stack formed using inheritance we
get this function automatically for free, while the composition version needs to explicitly
add a new operation if we want to include this new ability. Similarly, the ability to access
178 CHAPTER 10. MECHANISMS FOR SOFTWARE REUSE
class Stack extends Vector f

public void push (Object value)


f addElement (value) g

public Object peek ()


f return lastElement() g

public Object pop () f


Object result = lastElement()
removeElementAt(theData.size()-1)
return result
g
g

Figure 10.2: A stack created using Inheritance

intermediate values directly using an index is in this version possible with a stack, but not
permitted in the abstraction created using composition.

10.3 Composition and Inheritance Contrasted


Having illustrated two mechanisms for software reuse, and having seen that they are both
applicable to the implementation of stacks, we can comment on some of the advantages and
disadvantages of the two approaches.
 Inheritance carries with it an implicit, if not explicit, assumption of substitutability.
That is, classes formed by inheritance are assumed to be subtypes of the parent class,
and therefore candidates for values to be used when an instance of the parent class
is expected. No such assumption of substitutability is associated with the use of
composition.
 Composition is the simpler of the two techniques. Its advantage is that it more clearly
indicates exactly what operations can be performed on a particular data structure.
Looking at the declaration for the Stack data abstraction, it is clear that the only
operations provided for the data type are the test for emptiness, push, peek and pop.
This is true regardless of what operations are dened for vectors.
 In inheritance the operations of the new data abstraction are a superset of the oper-
ations of the original data structure on which the new object is built. Thus, to know
exactly what operations are legal for the new structure the programmer must examine
the declaration for the original. An examination of the Stack declaration, for example,
10.3. COMPOSITION AND INHERITANCE CONTRASTED 179

does not immediately indicate that the size method can be legally applied to stacks. It
is only by examination of the declaration for the earlier Vector data abstraction that
the entire set of legal operations can be ascertained.
The diculty that occurs when, to understand a class constructed using inheritance,
the programmer must frequently ip back and forth between two (or more) class
declarations has been labeled the \yo-yo" problem Taenzer 1989].
 The brevity of data abstractions constructed with inheritance is, in another light, an
advantage. Using inheritance it is not necessary to write any code to access the func-
tionality provided by the class on which the new structure is built. For this reason,
implementations using inheritance are almost always, as in the present case, consid-
erably shorter in code than are implementations constructed with composition, and
they often provide greater functionality. For example, the inheritance implementation
makes available not only the size test for stacks but also the index related operations
(inserting, modifying or removing elements by giving their index locations).
 Inheritance does not prevent users from manipulating the new structure using methods
from the parent class, even if these are not appropriate. For example, when we use
inheritance to derive the class Stack from the class Vector, nothing prevents users
from adding new elements to the stack using the inherited method insertElementAt,
and thereby placing elements in locations other than the top of the stack.
 In composition the fact that the class Vector is used as the storage mechanism for
our stack is merely an implementation detail. With this technique it would be easy
to reimplement the class to make use of a dierent technique (such as a linked list),
with minimal impact on the users of the Stack abstraction. If users counted on the
fact that a Stack is merely a specialized form of Vector, such changes would be more
dicult to implement.
 A component constructed using inheritance has access to elds and methods in the
parent class that have been declared as protected. A component constructed using
composition can only access the public portions of the parent class.
 Inheritance may allow us to use the new abstraction as an argument in an existing poly-
morphic function. We will investigate this possibility in more detail in Chapter 12.
Because composition does not imply substitutability, it usually precludes polymor-
phism.
 Understandability and maintainability are dicult to judge. Inheritance has the ad-
vantage of brevity of code but not of protocol. Composition code, although longer, is
the only code that another programmer must understand to use the abstraction. A
programmer faced with understanding the inheritance version needs to ask whether
any behavior inherited from the parent class was necessary for proper utilization of
the new class, and would thus have to understand both classes.
180 CHAPTER 10. MECHANISMS FOR SOFTWARE REUSE

 Data structures implemented through inheritance tend to have a very small advantage
in execution time over those constructed with composition, since one additional func-
tion call is avoided (although optimization techniques, such as inline functions, can in
theory be used to eliminate much of this overhead).
Of the two possible implementation techniques, can we say which is better in this case?
One answer involves the substitution principle. Ask yourself whether, in an application that
expected to use a Vector data abstraction, it is correct to substitute instead an instance of
class Stack.
The bottom line is that the two techniques are very useful, and an object-oriented
programmer should be familiar with both of them.

10.4 Combining Inheritance and Composition


The Java I/O system, which we will investigate in more detail in Chapter 14, provides an
interesting illustration of the way in which inheritance and composition can interact with
each other, and the particular problems each mechanism is designed to solve.
To begin with, there is an abstract concept, and several concrete realizations of the
concept. For the le input system the abstract concept is the idea of reading a stream of
bytes in sequence, one after the other. This idea is embodied in the class InputStream, which
denes a number of methods for reading byte values. The concrete realizations dier in the
source of the data values. Values can come from an array of bytes being held in memory,
from an external le, or from another process that is generating values as needed. There is
a dierent subclass of InputStream for each of these.
InputStream

ByteArrayInputStream

FileInputStream

PipedInputStream

SequenceInputStream

StringBuerInputStream

Because each of these is declared as a subclass of InputStream, they can be substituted


for a value of type InputStream. In this fashion procedures can be written to process a
10.5. NOVEL FORMS OF SOFTWARE REUSE 181

stream of byte values, without regard to where the values originate (whether in memory, or
from an external le, or from another process).
However, there is an additional source of variation among input streams. Or rather,
there is additional functionality that is sometimes required when using an input stream.
Furthermore, this functionality is independent of the source for the byte values. One example
is the ability to keep track of line numbers, so that the programmer can determine on which
line of the input the current byte originates. Another useful function is the ability to buer
input, so as to have the possibility of rereading recently referenced bytes once again.
These features are provided by dening a subclass of InputStream, named FilterInput-
Stream. A FilterInputStream is formed as a subclass of InputStream. Thus, using the prin-
ciple of substitutability, a FilterInputStream can be used in places where an InputStream is
expected. On the other hand, a FilterInputStream holds as a component another instance
of InputStream, which is used as the source for data values. Thus, the class InputStream is
both parent and component to FilterInputStream. As requests for values are received, the
FilterInputStream will access the InputStream it holds to get the values it needs, performing
whatever additional actions are required (for example, counting newline characters in order
to keep track of the current line number).
class FilterInputStream extends InputStream f

protected InputStream in


...
g

Because the component held by the FilterInputStream can be any type of InputStream, this
additional functionality can be used to augment and type of InputStream, regardless of where
the byte values originate. This idea of lters (sometimes called wrappers in other object-
oriented literature) can be quite powerful when there are orthogonal sources of variation.
Here the two sources of variation are the source of the input values, and the additional
functionality that may or may not be needed in any particular application.

10.5 Novel Forms of Software Reuse


In this section we will describe several novel ways in which composition, or inheritance, or
both, are used to achieve dierent eects.

10.5.1 Dynamic Composition


One advantage of composition over inheritance concerns the delay in binding time. With
inheritance, the link between child class and parent class is established at compile time, and
cannot later be modied. With composition, the link between the new abstraction and the
182 CHAPTER 10. MECHANISMS FOR SOFTWARE REUSE
class Frog f
private FrogBehavior behavior

public Frog () f
behavior = new TadpoleBehavior()
g

public grow () f // see if behavior should change


if (behavior.growUp())
behavior = new AdultFrogBehavior()
behavior.grow() // behavior does actual work
behavior.swim()
g
g

Figure 10.3: Class Frog holds dynamically changing behavior component

older abstraction is created at run-time, and is therefore much weaker, since it can also be
changed at run time. This is sometimes called dynamic composition.
To illustrate, imagine a class that is simulating the behavior of a Frog. Although the
frog interface can be xed throughout its life, the actual actions it performs might be very
dierent when the frog is a tadpole or when it is an adult. One way to model this is to
create a class Frog that uses composition to hold a value of type FrogBehavior (Figure 10.3).
The Frog class is largely a facade, invoking the FrogBehavior object to do the majority of
the real work.
The novel idea is that the variable holding the instance of FrogBehavior can actually
be polymorphic. There might be more than one class that implements the FrogBehavior
specication, such as TadpoleBehavior and AdultFrogBehavior Figure 10.4 illustrates these
classes. The parent class FrogBehavior is here declared abstract, which means that it must
be overridden before any instances can be created. As the frog \grows", it can dynamically
change behavior by reassigning the value behavior to a dierent value (for example, moving
from a TadpoleBehavior to an AdultFrogBehavior). The user of the Frog abstraction need not
know about this change, or even be aware when it occurs.
Dynamic composition is a useful technique if the behavior of an object varies dramatically
according to some internal concept of \state", and the change in state occurs infrequently.

10.5.2 Inheritance of Inner Classes


We have seen another combination of inheritance and composition in some of the case studies
presented in earlier chapters. For example, the \listener" classes that responded to events
were constructed using inheritance, but were themselves components in the application class.
10.5. NOVEL FORMS OF SOFTWARE REUSE 183

abstract class FrogBehavior f


public boolean growUp () f return false g

public void grow ()

public void swim ()


g

class TadpoleBehavior extends FrogBehavior f


private int age = 0

public boolean growUp () f if (++age > 24) return true g

public void grow () f ... g

public void swim () f ... g


g

class AdultFrogBehavior extends Behavior f

public void grow () f ... g

public void swim () f ... g


g

Figure 10.4: Class FrogBehavior can dynamically change


184 CHAPTER 10. MECHANISMS FOR SOFTWARE REUSE

The following is a skeleton of the class PinBallGame from Chapter 7.


public class PinBallGame extends Frame f
...
private class MouseKeeper extends MouseAdapter f ... g

private class PinBallThread extends Thread f ... g


g

The classes MouseKeeper and PinBallThread are each constructed using inheritance. Each
are then used to create a new component, that will be held as part of the state of the pin
ball game. When used in this fashion, inner classes combine aspects of both inheritance and
composition.

10.5.3 Unnamed Classes


In several of the earlier case studies we have seen situations where inheritance is used to
override an existing class, yet only one instance of the new class is created. An alternative
to naming the new class and then creating an instance of the named class is to use a class
denition expression. Such an expression places the entire class denition inside the instance
creation expression.
An example where this could be used is in the denition of an event listener. For example,
the CannonBall game described in Chapter 6 contained the following code:
class CannonWorld extends Frame f
...

private class FireButtonListener implements ActionListener f


public void actionPerformed (ActionEvent e) f
...
g
g

public CannonWorld () f
fire.addActionListener(new FireButtonListener())
...
g
g

Note how the constructor for the class CannonWorld creates an instance of the inner class
FireButtonListener. This is the only instance of this class created. An alternative way to
achieve the same eect would be as follows:
10.6. CHAPTER SUMMARY 185

class CannonWorld extends Frame f


...

public CannonWorld () f
fire.addActionListener(new ActionListener()f
public void actionPerformed (ActionEvent e) f
...
g
g)
...
g
g

Notice that in this example the object being created is declared only as being an instance
of ActionListener. However, a class denition follows immediately the ending parenthesis,
indicating that a new and unnamed class is being dened. This class denition would have
exactly the same form as before, ending with a closing curly brace. The parenthesis that
follows this curly brace ends the argument list for the addActionListener call. (The reader
should carefully match curly braces and parenthesis to see how this takes place).
An advantage to the use of the class denition expression in this situation is that it avoids
the need to introduce a new class name (in this case, the inner class name FireButtonListener).
A disadvantage is that such expressions tend to be dicult to read, since the entire class
denition must be wrapped up as an expression, and the close of the expressions occurs
after the end of the class denition.

10.6 Chapter Summary


The two most common techniques for reusing software abstractions are inheritance and
composition. Both are valuable techniques, and a good object-oriented system will usually
have many examples of each.
A good rule of thumb for deciding when to use inheritance is the is-a rule. Form the
sentence \An X is a Y", and if it sounds right to your ear, then the two concepts X and Y
can probably be related using inheritance. The corresponding rule for composition is the
has-a rule.
The decision whether to use inheritance or not is not always clear. By examining one
borderline case, one can more easily see some of the advantages and disadvantages of the
two software structuring techniques.
The idea of lters, found in the portion of the Java library used for input and output, is
an interesting technique that combines features of both inheritance and composition.
186 CHAPTER 10. MECHANISMS FOR SOFTWARE REUSE

Study Questions
1. Explain in your own words the principle of substitutability.
2. What is the is-a rule? What is the has-a rule? How are these two rules related to
inheritance and composition?
3. How are interfaces and inheritance related?
4. What are some of the advantages of composition over inheritance?
5. What are some of the advantages of using inheritance that are not available when
composition is used?
6. How does a FilterStream combine inheritance and composition?

Exercises
1. A set is simply an unorganized collection of values. Describe how one could use a
Vector to implement a set. Would inheritance or composition be the more appropriate
mechanism in this case?
2. Modify the Stack data abstractions given in this chapter so that they will throw a
EmptyStackException if an attempt is made to read or remove a value from an empty
stack.
Chapter 11

Implications of Inheritance
The decision to support inheritance and the principle of substitutability in a language sets
o a series of chain reactions that end up impacting almost every aspect of a programming
language. In this chapter, we will illustrate this point by considering some of the implications
of the decision to support in a natural fashion the idea of the polymorphic variable.
The links between inheritance and other language features can be summarized as follows:
 In order to make the most eective use of object-oriented techniques, the language
must support the polymorphic variable. A polymorphic variable is a variable that is
declared as one type but actually maintains a value derived from a subtype of the
declared type.
 Because at compile time we cannot determine the amount of memory that will be
required to hold the value assigned to a polymorphic variable, all objects must reside
on the heap, rather than on the stack.
 Because values are heap resident, the most natural interpretation of assignment and
parameter passing uses reference semantics, rather than copy semantics.
 Similarly, the most natural interpretation of equality testing is to test object identity.
However, since often the programmer requires a dierent meaning for equality, two
dierent operators are necessary.
 Because values reside on the heap there must be some memory management mecha-
nism. Because assignment is by reference semantics it is dicult for the programmer
to determine when a value is no longer being used. Therefore a garbage collection
system is necessary to recover unused memory.
Each of these points will be more fully developed in the following sections.
187
188 CHAPTER 11. IMPLICATIONS OF INHERITANCE

11.1 The Polymorphic Variable


As we will see in Chapter 12, a great deal of the power of the object-oriented features of
Java comes through the use of a polymorphic variable. A polymorphic variable is declared as
maintaining a value of one type, but in fact holds a value from another type. We have seen
many such examples in the sample programs presented in Part 1. For instance, much of the
standard user interface code thinks only that an application is an instance of class Frame,
when in fact each program we created used inheritance to create a new type of application.
Similarly, in the pin ball game construction program (Chapter 7) a variable was declared as
holding a value of type PinBallTarget, when in fact it would hold a Hole or a ScorePad.
Figure 11.1 provides a class hierarchy consisting of three classes, Shape and two sub-
classes Circle and Square. In the small test program shown below variable named form is
declared as type Shape, then assigned a value of type Circle. As expected, when the function
describe() is invoked, the method that is executed is the procedure in class Circle, not the
function inherited from class Shape. We will use this example class in some of the subsequent
discussion in this chapter.
class ShapeTest f
static public void main (String  ] args) f
Shape form = new Circle (10, 10, 5)
System.out.println("form is " + form.describe())
g
g

11.2 Memory Layout


Before we can describe the impact of the polymorphic variable on memory management,
it is rst necessary to review how variables are normally represented in memory in most
programming languages. From the point of view of the memory manager, there are two
major categories of memory values. These are stack based memory locations, and heap
based memory values.
Stack based memory locations are tied to procedure entry and exit. When a procedure
is started, space is allocated on a run-time stack for local variables. These values exist as
long as the procedure is executing, and are erased, and the memory recovered, when the
procedure exits. Figure 11.2 shows, for example, a snapshot of the run-time stack for the
following simple recursive algorithm:
class FacTest f
static public void main (String  ] args) f
int f = factorial(3)
11.2. MEMORY LAYOUT 189

class Shape f
protected int x
protected int y

public Shape (int ix, int iy)


f x = ix y = iy g

public String describe ()


f return "unknown shape" g
g

class Square extends Shape f


protected int side

public Square (int ix, int iy, int is)


f super(ix, iy) side = is g

public String describe ()


f return "square with side " + side g
g

class Circle extends Shape f


protected int radius

public Circle (int ix, int iy, int ir)


f super(ix, iy) radius = ir g

public String describe ()


f return "circle with radius " + radius g
g

Figure 11.1: Shape classes and Polymorphic Variable


190 CHAPTER 11. IMPLICATIONS OF INHERITANCE

0 n: 1
4 r: 1
8 c: 0
0 n: 2
4 r: ?
8 c: 1
0 n: 3
4 r: ?
8 c: 2

Figure 11.2: A Snapshot of the Activation Frame Stack

System.out.println("Factorial of 3 is " + f)


g

static public int factorial (int n) f


int c = n - 1
int r
if (c > 0)
r = n  factorial(c)
else
r = 1
return r
g
g

The snapshot is taken after the procedure has recursed three times, just as the innermost
procedure is starting to return. The data values for three procedures are shown. In the
innermost procedure the variable r has been assigned the value 1, while the two pending
procedures the value of r has yet to be determined.
There are a number of advantages of stack based memory allocation. All local variables
can be allocated or deallocated as a block, for example, instead of one by one. This block
is commonly called an activation record. Internally, variables can be described by their
numeric oset within the activation record, rather than by their symbolic address. These
numeric osets have been noted in Figure 11.2. Most machines are much more ecient at
dealing with numeric osets than with symbolic names. Notice that each new activation
record creates a new set of osets, so that the oset is always relative to the activation frame
in which a variable appears.
11.2. MEMORY LAYOUT 191

There is one serious disadvantage to stack based allocation. This is that these numeric
osets associated with variables must be determined at compile time, not a run time. In
order to do this, the compiler must know the amount of memory to assign to each variable.
In Figure 11.2, the compiler only knows that variable c can be found at address 8 because
it knows that variable r, which starts at location 4, requires only four bytes.
But, this is exactly the information we do not know for a polymorphic variable. The
storage requirements for a polymorphic variable value are determined when the value is
created at run-time, and can even change during the course of execution. Recall the classes
shown in Figure 11.1, and the memory requirements for the procedure main in the sample
program described earlier. Here the variable form, which is declared as holding a Shape, can
at one moment be holding a Circle, at another a Square, and so on. Both the subclasses
add additional data elds that are not found as part of the parent class. Thus the compiler
cannot know, at compile time, exactly how much memory will be required to hold the
variable form.
In Java, the solution to this problem is that objects are not stored on the activation record
stack, but are instead stored on the heap. A heap is an alternative memory management
system, one that is not tied to procedure entry and exit. Instead, memory is allocated on
the heap when explicitly requested (to create a new object, using the new operator), and is
freed, and recycled, when no longer needed. At run-time, when the memory is requested, the
Java system knows precisely how much memory is required to hold a value. In this fashion
the Java language avoids the need to predict, at compile time, the amount of memory that
will be needed at run-time.
The code generated by the compiler, however, must still be able to accesses variables
through numeric osets, even through the actual heap addresses will not be known until
run time. The solution to this dilemma is to use one level of indirection. Local variables are
represented on the stack as pointer values. The size of a pointer is known at compile time,
and is independent of the size of the object it points to. This pointer eld is lled when an
object is created.
It is said that the programming language Java has no pointers. This is true as far as the
language the programmer sees is concerned. But ironically, this is only possible because all
object values are, in fact, represented internally by pointers.

11.2.1 An Alternative Technique


It should be noted that the solution to this problem selected by the designers of Java is not
the only possibility. This can be illustrated by considering the language C++, that uses an
entirely dierent approach. C++ treats assignment of variables and assignment of pointers
very dierently.
The designers of C++ elected to store variable values on the stack. Thus, the memory
allocated to a variable of type Shape is only large enough to hold a value of type Shape, not
the additional elds added by the subclass Circle. During the process of assignment these
extra elds are simply sliced o and discarded. Of course, the resulting value is then no
192 CHAPTER 11. IMPLICATIONS OF INHERITANCE

longer a Circle. For this reason, when we try to execute a member function, such as the
describe function, the code executed will be that associated with class Shape, not the value
associated with class Circle, as in Java. The programmer who uses both C++ and Java
should be aware of this subtle, but nevertheless important dierence.

11.3 Assignment
In Section 11.2 we described why values in Java are most naturally maintained on the
heap, rather than being held in the activation record stack. Because to the compiler the
underlying \value" of a variable is simply a pointer into the heap, the most natural semantics
for assignment simply copy this pointer value. In this manner, the right and left sides of an
assignment statement end up referring to the same object. This is often termed reference
semantics (sometimes also called pointer semantics). The consequences of this interpretation
of assignment are subtle, but are again a key point for Java programmers to remember.
Suppose we create a simple class Box as follows:
public class Box f
private int value

public Box () f value = 0 g


public void setValue (int v) f value = v g
public int getValue () f return value g
g

Now imagine that we create a new box, assign it to a variable x, and set the internal
value to 7. We then assign the box held by x to another variable named y. Since both x
and y now hold non-null values of type Box, the programmer might assume that they are
distinct. But, in fact, they are exactly the same box, as can be veried by changing the
value held in the y box and printing the value held by the x box:
public class BoxTest f
static public void main (String  ] args) f

Box x = new Box()


x.setValue (7) // set value of x
Box y = x // assign y the same value as x
y.setValue (11) // change value of y

System.out.println("contents of x " + x.getValue())


System.out.println("contents of y " + y.getValue())
11.3. ASSIGNMENT 193

g
g

The key observation is that the two variables, although assigned separate locations on
the activation record stack, nevertheless point to the same location on the heap:

x HH
HH
HH
j
a box

*

y 

11.3.1 Clones
If the desired eect of an assignment is indeed a copy, then the programmer must indicate
this. One way would be to explicitly create a new value, copying the internal contents from
the existing value:
// create new box with same value as x
Box y = new Box (x.getValue())

If making copies is a common operation, it might be better to provide a method in the


original class:
public class Box f
...
public Box copy() f // make copy of box
Box b = new Box()
b.setValue (getValue())
return b
g
...
g

A copy of a box is then created by invoking the copy() method:


// create new box with same value as x
Box y = x.copy()
194 CHAPTER 11. IMPLICATIONS OF INHERITANCE

There is no general mechanism in Java to copy an arbitrary object, however the base
class Object does provide a protected method named clone() that creates a bit-wise copy of
the receiver, as well as an interface Cloneable that represents objects that can be cloned.
Several methods in the Java library require that arguments be values that are cloneable.
To create a class that is cloneable, the programmer must not only override the clone
method to make it public, but also explicitly indicate that the result satises the Cloneable
interface. The following, for example, shows how to create a cloneable box.
public class Box implements Cloneable f
private int value

public Box () f value = 0 g


public void setValue (int v) f value = v g
public int getValue () f return value g

public Object clone () f


Box b = new Box()
b.setValue (getValue())
return b
g
g

The clone method is declared as yielding a result of type Object. This property cannot
be modied when the method is overridden. As a consequence, the result of cloning a value
must be cast to the actual type before it can be assigned to a variable.
public class BoxTest f
static public void main (String  ] args) f

Box x = new Box()


x.setValue (7)

Box y = (Box) x.clone() // assign copy of x to y


y.setValue (11) // change value of x
System.out.println("contents of x " + x.getValue())
System.out.println("contents of y " + y.getValue())
g
g
11.3. ASSIGNMENT 195

As always, there are subtleties here that can trap the unwary programmer. Consider
the object that is being held by our box. Imagine, instead of simply an integer, that it is
something more complex, such as a Shape. Should a clone also clone this value, or just copy
it? A copy results in two distinct boxes, but ones that share a common value. This is called
a shallow copy.

x - a box HH
HH
HH
j
a shape
 
*

y - a box 

Cloning the contents of the box (which must therefore be itself a type that is cloneable)
results in two box values which are not only themselves distinct, but which point to values
that are also distinct. This is termed a deep copy.

x - a box - a shape

y - a box - a shape

Whether a copy should be shallow or deep is something that must be determined by the
programmer when they override the clone interface.

11.3.2 Parameters are a form of Assignment


Note that a variable passed as an argument to a member function can be considered to be a
form of assignment, in that the parameter passing, as with assignment, results in the same
value being accessible through two dierent names. Thus, the issues raised in Section 11.3
regarding assignment apply equally to parameter values. Consider the function sneeky in
the following example, which modies the value held in a box that is passed through a
parameter value.
public class BoxTest f
static public void main (String  ] args) f
196 CHAPTER 11. IMPLICATIONS OF INHERITANCE

Box x = new Box()


x.setValue (7)

sneeky (x)

System.out.println("contents of x " + x.getValue())


g

static void sneeky (Box y) f


y.setValue (11) // change value of parameter
g
g

A programmer who passes a box to this function, as shown in the main procedure, could
subsequently see the resulting change in a local variable.
A Java programmer should always keep in mind that when a value is passed to a pro-
cedure, a certain degree of control over the variable is lost. In particular, the procedure is
free to invoke any method applicable to the parameter type, which could result in the state
of the variable being changed, as in this example.

11.4 Equality Test


For basic data types the concept of equality is relatively simple. The value 7 should clearly be
equivalent to 3 + 4, for example, because we think of integers as being unique entities{there
is one and only one 7 value. This is true even when there are two syntactic representations
for the same quantity, for example the ASCII value of the letter a is 141, and thus '\141'
is the same as 'a'.
The situation becomes slightly more complicated when the two values being dened are
not the same type. For example, should the value 2 (an integer constant) be considered
equal to 2.0 (a oating-point constant)? The Java language says that the two values are
equivalent in this case, since the integer value can be converted into a oating point value,
and the two oating values compared. Thus, all the following expressions will yield a true
result.
7 == (3 + 4)
a == \141
2 == 2.0

When the concept of equality testing is expanded to include objects, the most natural
interpretation becomes less obvious. In Section 11.3 it was argued that because objects are
11.4. EQUALITY TEST 197

internally represented by pointers, the natural interpretation of assignment uses reference


semantics. One interpretation of equality testing follows the same reasoning. That is, two
objects can be considered equal if they are identically the same. This form of equality testing
is often termed as testing object identity, and is the interpretation provided by the operator
== and its inverse, the operator ! =. This can cause certain anomalies. For example,
although 7 is equal to 3 + 4, the following code fragment will nevertheless show that an
Integer value 7 is a distinct object from a dierent Integer object, even if it has the same
value:
Integer x = new Integer(7)
Integer y = new Integer(3 + 4)
if (x == y)
System.out.println("equivalent")
else
System.out.println("not equivalent")

The Java compiler does apply type checking rules to the two arguments, which will help
detect many programming errors. For example, although a numeric value can be compared
to another numeric, a numeric cannot be compared to a dierent type of object (for example,
a String). Two object values can be compared if they are the same type, or if the class of
one can be converted into the class of the second. For example, a variable that was declared
to be an instance of class Shape could be compared with a variable of type Circle, since a
Circle would be converted into a Shape. A particular instance of the conversion rule is one
of the more frequent uses of the == operator namely, any object can be compared to the
constant null, since the value null can be assigned to any object type.
Often object identity is not the relation one would like to test, and instead one is in-
terested in object equality. This is provided by the method equals, which is dened by the
base class Object and redened by a number of classes. The following, for example, would
return true using the equals function, but would not be true using the == operator:
String a = "abc"
String b = "abc"
Integer c = new Integer(7)
Integer d = new Integer(3 + 4)
if (a.equals(b))
System.out.println("strings are equal")
if (c.equals(d))
System.out.println("integers are equal")

Because the equals function is dened in class Object, it can be used with any object
type. However, for the same reason, the argument is declared only as type Object. This
198 CHAPTER 11. IMPLICATIONS OF INHERITANCE

means that any two objects can be compared for equality, even if there is no possible way
that for either to be assigned to the other:
if (a.equals(c)) // can never be true
System.out.println("string equal to integer")

The developer of a class is free to override the equals operator and thereby allow com-
parison between objects. Since the argument is an Object it must rst be tested to ensure it
is the correct type. By convention, the value false is returned in cases where the type is not
correct. The following, for example, would be how one could dene a function to compare
two instances of class Circle (Figure 11.1).
class Circle extends Shape f
...
public boolean equals (Object arg) f
if (arg instanceof Circle) f
// convert argument to circle
Circle argc = (Circle) arg
if (radius == argc.radius)
return true // just test radius
g
return false // return false otherwise
g
g

Because the type of the argument is not necessarily the same as the type of the receiver,
unusual situations can occur if the programmer is not careful. For example, suppose we
dened equals in class Shape from Figure 11.1 to test equality of the x and y values, but
forgot to also override the function in class Square. It could happen in this situation that if
we tried to compare a square and a circle, the comparison would be true one way and false
the other.
Square s = new Square(10, 10, 5)
Circle c = new Circle(10, 10, 5)
if (s.equals(c)) // true, since method in shape is used
System.out.println("square equal to circle")
if (c.equals(s)) // false, since method in circle is used
System.out.println("circle equal to square")

When overriding the equals method the programmer should be careful to avoid this
problem, and ensure that the resulting functions are both symmetric (if x is equal to y, then
y is equal to x) and associative (if x is equal to y and y is equal to z , then x is equal to z ).
11.5. GARBAGE COLLECTION 199

A good rule of thumb is to use == when testing numeric quantities, and when testing an
object against the constant null. In all other situations the function equals should be used.

11.5 Garbage Collection


In Section 11.2 it was argued that support for polymorphic variables naturally implies that
values are allocated on the heap, rather than on the stack. Memory of any type is always
a nite resource, which must be managed if a program is to avoid running out of memory.
In order to prevent this problem, both stack and heap based memory is recycled, with new
memory requests being assigned the same locations as previous memory values that are no
longer being used.
Unlike stack based memory allocation, heap based memory management is not tied to
procedure activation, and is thus not automatically recovered when a procedure returns.
This means an alternative mechanism must be introduced.
Two dierent approaches to the recovery of heap based memory values are found in pro-
gramming languages. In languages such as Object Pascal or C++ it is up to the programmer
to explicitly indicate when a memory value is no longer being used, and can therefore be
reused to satisfy new memory requests. In Object Pascal, for example, this is accomplished
by means of the statement dispose:
var
aShape : Shape
begin
new (aShape) ( allocate a new shape )
...
dispose (aShape) ( free memory used by variable )
end.

In such languages, if an object is to be freed, it must be \owned" by some variable (the


variable that will be the target for the free request). But Java values are simply references,
and are shared equally by all variables that refer to the same value. If a programmer assigns
a value to another variable, or passes the value as an argument, the programmer may no
longer be aware of how many references a value might have.
Leaving to the programmer the responsibility for freeing memory in this situation exposes
a program to a number of common errors, such as freeing the same memory location twice.
Even more commonly, programmers avoid committing this error by simply never freeing
memory, causing long-running programs to slowly degrade as they consume more and more
memory resources.
For these reasons, the designers of Java elected a dierent approach. Rather than having
the programmer indicate when a value is no longer needed, a run-time system is provided
that periodically searches the memory being used by a program to discover which heap
200 CHAPTER 11. IMPLICATIONS OF INHERITANCE

values are being accessed and, more importantly, which heap values are no longer being
referenced by any variable and can therefore be recovered and recycled. This mechanism is
known as the garbage collection system.
The use of a garbage collection system in Java is a compromise. The task of garbage
collection does exact a toll in execution time. However, in return a garbage collection system
greatly simplies the programming process, and eliminates several categories of common
programming errors. For most programs, the improvements in reliability are well worth the
execution time overhead.

11.6 Chapter Summary


The idea of a polymorphic variable is an extremely powerful concept, one we will explore
in detail in later chapters. However, the decision to support the concept of a polymorphic
variable raises a number of subtle and dicult issues in other aspects of the language. In
this chapter we have investigated some of these, showing how inheritances alters the way
the language must handle storage management, the concept of assignment, and the testing
of two values for equality.

Study Questions
1. What is a polymorphic variable?
2. From the language implementation point of view, what are the two major categories
of memory values?
3. How does the idea of a polymorphic variable con ict with the ability to determine
memory requirements at compile time?
4. What does it mean to say that Java uses reference semantics for assignment?
5. What must a programmer do to create a class that supports the Cloneable interface?
6. What is the dierence between a deep and shallow copy?
7. In what way is passing a parameter similar to an assignment?
8. What is the dierence between the == operator and the equals() method?
9. What task is being performed by the garbage collection system in the Java run-time
library?
10. What are some advantages of a language that uses garbage collection? What are some
disadvantages?
11.6. CHAPTER SUMMARY 201

Exercises
1. Rewrite the Shape classes shown in Figure 11.1 so that they support the cloneable
interface.
2. Rewrite the class Box so that it holds values that are cloneable, and when cloned it
created a deep copy.
202 CHAPTER 11. IMPLICATIONS OF INHERITANCE
Part IV

Understanding Polymorphism

203
Chapter 12

Polymorphism
The term polymorphic has Greek roots and means roughly \many forms." (poly = many,
morphos = form. Morphos is related to the Greek god Morphus, who could appear to
sleeping individuals in any form he wished and hence was truly polymorphic.) In biology, a
polymorphic species is one, such as Homo Sapiens, that is characterized by the occurrence
of dierent forms or color types in individual organisms or among organisms. In chemistry,
a polymorphic compound is one that can crystallize in at least two distinct forms, such as
carbon, which can crystallize both as graphite and as diamond.

12.1 Varieties of Polymorphism


In object-oriented languages, polymorphism is a natural result of the is-a relationship and of
the mechanisms of message passing, inheritance, and the concept of substitutability. One of
the great strengths of the OOP approach is that these devices can be combined in a variety
of ways, yielding a number of techniques for code sharing and reuse.
Pure polymorphism occurs when a single function can be applied to arguments of a
variety of types. In pure polymorphism, there is one function (code body) and a number
of interpretations. The other extreme occurs when we have a number of dierent functions
(code bodies) all denoted by the same name{a situation known as overloading or sometimes
ad hoc polymorphism. Between these two extremes are overriding and deferred methods.1
1 Note that there is little agreement regarding terminology in the programming language commu-
nity. In Horowitz 1984], Marcotty 1987], MacLennan 1987], and Pinson 1988] for example, polymor-
phism is dened in a manner roughly equivalent to what we are here calling overloading. In Sethi 1989]
and Meyer 1988a] and in the functional programming languages community (such as Wikstrom 1987,
Milner 1990]), the term is reserved for what we are calling pure polymorphism. Other authors use the
term for one, two, or all of the mechanisms described in this chapter. Two complete, but technically
daunting, analyses are Cardelli 1985] and Danforth 1988].

205
206 CHAPTER 12. POLYMORPHISM

12.2 Polymorphic Variables


With the exception of overloading, polymorphism in object-oriented languages is made pos-
sible only by the existence of polymorphic variables and the idea of substitutability. A
polymorphic variable is one with many faces that is, it can hold values of dierent types.
Polymorphic variables embody the principle of substitutability. In other words, while there
is an expected type for any variable the actual type can be from any value that is a subtype
of the expected type.
In dynamically bound languages (such as Smalltalk), all variables are potentially polymorphic{
any variable can hold values of any type. In these languages the desired type is dened by
a set of expected behaviors. For example, an algorithm may make use of an array value,
expecting the subscripting operations to be dened for a certain variable any type that
denes the appropriate behavior is suitable. Thus, the user could dene his or her own type
of array (for example, a sparse array) and, if the array operations were implemented using
the same names, use this new type with an existing algorithm.
In statically typed languages, such as Java, the situation is slightly more complex. Poly-
morphism occurs in Java through the dierence between the declared (static) class of a
variable and the actual (dynamic) class of the value the variable contains.
A good example of a polymorphic variable is the array allPiles in the Solitare game
presented in Chapter 9. The array was declared as maintaining a value of type CardPile,
but in fact it maintains values from each of the dierent subclasses of the parent class. A
message presented to a value from this array, such as display in the example code shown
below, executes the method associated with the dynamic type of the variable and not that
of the static class.
public class Solitaire extends Applet f
...
static CardPile allPiles  ]
...

public void paint(Graphics g) f


for (int i = 0 i < 13 i++)
allPilesi].display(g)
g
...
g

12.3 Overloading
We say a function name is overloaded if there are two or more function bodies associated
with it. Note that overloading is a necessary part of overriding, which we and will describe
12.3. OVERLOADING 207

in the next section, but the two terms are not identical and overloading can occur without
overriding.
In overloading, it is the function name that is polymorphic{it has many forms. Another
way to think of overloading and polymorphism is that there is a single abstract function
that takes various types of arguments the actual code executed depends on the arguments
given. The fact that the compiler can often determine the correct function at compile time
(in a strongly typed language), and can therefore generate only a single code sequence are
simply optimizations.

12.3.1 Overloading Names in Real Life


In Chapter 1 we saw an example in which overloading occurred without overriding, when
I wanted to surprise my friend with owers for her birthday. One possible solution was to
send the message sendFlowersTo to my local orist another was to give the same message to
my wife. Both my orist and my wife (an instance of class Spouse) would have understood
the message, and both would have acted on it to produce a similar result. In a certain sense,
I could have thought of sendFlowersTo as being one function understood by both my wife
and my orist, but each would have used a dierent algorithm to respond to my request.
Note, in particular, that there was no inheritance involved in this example. The rst
common superclass for my wife and my orist was the category Human. But certainly the
behavior sendFlowersTo was not associated with all humans. My dentist, for example, who
is also a human, would not have understood the message at all.

12.3.2 Overloading and Coercion


As an example more closely tied to programming languages, suppose a programmer is devel-
oping a library of classes representing common data structures. A number of data structures
can be used to maintain a collection of elements (sets, bags, dictionaries, arrays, and priority
queues, for example), and these might all dene a method, add, to insert a new element into
the collection.
This situation{in which two totally separate functions are used to provide semantically
similar actions for dierent data types{occurs frequently in all programming languages, not
simply in object-oriented languages. Perhaps the most common example is the overloading
of the addition operator, +. The code generated by a compiler for an integer addition is often
radically dierent from the code generated for a oating-point addition, yet programmers
tend to think of the operations as a single entity, the \addition" function.
In this example it is important to point out that overloading may not be the only activity
taking place. A semantically separate operation, coercion, is also usually associated with
arithmetic operations. It occurs when a value of one type is converted into one of a dierent
type. If mixed-type arithmetic is permitted, the addition of two values may be interpreted
in a number of dierent ways:
208 CHAPTER 12. POLYMORPHISM

 There may be four dierent functions, corresponding to integer + integer, integer +


real, real + integer, and real + real. In this case, there is overloading but no coercion.
 There may be two dierent functions for integer + integer and real + real. In integer
+ real and real + integer, the integer value is coerced by being changed into a real
value. In this situation there is a combination of overloading and coercion.
 There may be only one function, for real + real addition. All arguments are coerced
into being real. In this case there is coercion only, with no overloading.

12.3.3 Overloading from Separate Classes


There are two dierent forms of overloading that can be distinguished. One form occurs
when the same function name is found in two or more classes that are not linked by inher-
itance. A second form occurs when two or more functions with the same name are found
within one class denition. The latter form will be described in the next section.
A good example of overloading of the rst type is the method isEmpty. This method
is used to determine if an object is empty, however the exact meaning of empty will dier
depending upon circumstances. The message is understood by the classes Vector, Hashtable
and Rectangle. The rst two are collection classes, and the message returns true when there
are no elements in the collection. In the class Rectangle the message returns true if either
the height or width of a rectangle is zero, and thus the rectangle has no area.
Rectangle r1 = new Rectangle ()
if (r1.isEmpty()) ...

Overloading Does Not Imply Similarity


There is nothing intrinsic to overloading that requires the functions associated with an
overloaded name to have any semantic similarity. Consider a program that plays a card
game, such as the solitaire game we examined in Chapter 9. The method draw was used to
draw the image of a card on the screen. In another application we might also have included
a draw method for the pack of cards, that is, to draw a single card from the top of the deck.
This draw method is not even remotely similar in semantics to the draw method for the
single card, and yet they share the same name.
Note that this overloading of a single name with independent and unrelated meanings
should not necessarily be considered bad style, and generally it will not contribute to con-
fusion. In fact, the selection of short, clear, and meaningful names such as add, draw, and
so on, contributes to ease of understanding and correct use of object-oriented components.
It is far simpler to remember that you can add an element to a set than to recall that to
do so requires invoking the addNewElement method, or, worse, that it requires calling the
routine Set Module Addition Method.
12.4. OVERRIDING 209

All object-oriented languages permit the occurrence of methods with similar names in
unrelated classes. In this case the resolution of overloaded names is determined by obser-
vation of the class of the receiver for the message. Nevertheless, this does not mean that
functions or methods can be written that take arbitrary arguments. The statically typed
nature of Java still requires specic declarations of all names.

12.3.4 Parameteric Overloading


Another style of overloading, in which procedures (or functions or methods) in the same
context are allowed to share a name and are disambiguated by the number and type of
arguments supplied, is called parameteric overloading it occurs in Java as well as in some
imperative languages (such as Ada) and many functional languages. Parameteric overload-
ing is most often found in constructor functions. A new Rectangle, for example, can be
created either with no arguments (generating a rectangle with size zero and northwest cor-
ner 0,0), with two integer arguments (a width and height), with four integer arguments
(width, height, northwest corner), with a Point (the northwest corner, size is zero), with a
Dimension (height and width, corner 0,0), or with both a Point and a Dimension.
Rectangle r1 = new Rectangle ()
Rectangle r2 = new Rectangle (6, 7)
Rectangle r3 = new Rectangle (10, 10, 6, 7)
Point p1 = new Point (10, 10)
Dimension d1 = new Dimension (6, 7)
Rectangle r4 = new Rectangle (p1)
Rectangle r5 = new Rectangle (d1)
Rectangle r6 = new Rectangle (p1, d1)

There are six dierent constructor functions in this class, all with the same name. The
compiler decides which function to execute based on the number and type of arguments
used with the function call.
Overloading is a necessary prerequisite to the other forms of polymorphism we will
consider: overriding, deferred methods, and pure polymorphism. It is also often useful in
reducing the \conceptual space," that is, in reducing the amount of information that the
programmer must remember. Often, this reduction in programmer-memory space is just as
signicant as the reduction in computer-memory space permitted by code sharing.

12.4 Overriding
In Chapter 8 we described the mechanics of overriding, so it is not necessary to repeat that
discussion here. Recall, however, the following essential elements of the technique. In one
class (typically an abstract superclass), there is a general method dened for a particular
210 CHAPTER 12. POLYMORPHISM

message that is inherited and used by subclasses. In at least one subclass, however, a method
with the same name is dened, that hides access to the general method for instances of this
class (or, in the case of renement, subsumes access to the general method). We say the
second method overrides the rst.
Overriding is often transparent to the user of a class, and, as with overloading, frequently
the two functions are thought of semantically as a single entity.

12.4.1 Replacement and Renement


In Chapter 9 we brie y noted that overriding can occur in two dierent forms. A method
can replace the method in the parent class, in which case the code in the parent is not
executed at all. Alternatively, the code from the child can be used to form a renement,
which combines the code from the parent and the child classes.
Normally, overridden methods use replacement semantics. If a renement is desired, it
can be constructed by explicitly invoking the parent method as a function. This is accom-
plished by using the pseudo-variable super as the receiver in a message passing expression.
An example from the Solitare program described in Chapter 9 showed this:
class DiscardPile extends CardPile f

public void addCard (Card aCard) f


if (! aCard.faceUp())
aCard.flip()
super.addCard(aCard)
g

Constructors, on the other hand, always use renement semantics. A constructor for a
child class will always invoke the constructor for the parent class. This invocation will take
place before the code for the constructor is executed. If the constructor for the parent class
requires arguments, the pseudo-variable super is used as if it were a function:
class DeckPile extends CardPile f

DeckPile (int x, int y) f


// rst initialize parent
super(x, y)
// then create the new deck
// rst put them into a local pile
for (int i = 0 i < 4 i++)
for (int j = 0 j <= 12 j++)
12.5. ABSTRACT METHODS 211

addCard(new Card(i, j))

// then shue the cards


Random generator = new Random()
for (int i = 0 i < 52 i++) f
int j = Math.abs(generator.nextInt()) % 52
// swap the two card values
Object temp = thePile.elementAt(i)
thePile.setElementAt(thePile.elementAt(j), i)
thePile.setElementAt(temp, j)
g
g

When used in this fashion, the call on the parent constructor must be the rst statement
executed. If no call on super is make explicitly and there exist two or more overloaded
forms of the constructor, the constructor with no arguments (sometimes called the default
constructor) will be the form used.

12.5 Abstract Methods


A method that is declared as abstract can be thought of as dening a method that is deferred
it is specied in the parent class but must be implemented in the child class. Interfaces can
also be viewed as a method for dening deferred classes. Both can be considered to be
a generalization of overriding. In both cases, the behavior described in a parent class is
modied by the child class. In an abstract method, however, the behavior in the parent
class is essentially null, a place holder, and all useful activity is dened as part of the code
provided by the child class.
One advantage of abstract methods is conceptual, in that their use allows the program-
mer to think of an activity as associated with an abstraction at a higher level than may
actually be the case. For example, in a collection of classes representing geometric shapes,
we can dene a method to draw the shape in each of the subclasses Circle, Square, and
Triangle. We could have dened a similar method in the parent class Shape, but such a
method cannot, in actuality, produce any useful behavior since the class Shape does not
have sucient information to draw the shape in question. Nevertheless, the mere presence
of this method permits the user to associate the concept draw with the single class Shape,
and not with the three separate concepts Square, Triangle, and Circle.
There is a second, more practical reason for using abstract methods. In statically typed
object-oriented languages, such as Java, a programmer is permitted to send a message to
an object only if the compiler can determine that there is in fact a corresponding method
212 CHAPTER 12. POLYMORPHISM

that matches the message selector. Suppose the programmer wishes to dene a polymorphic
variable of class Shape that will, at various times, contain instances of each of the dierent
shapes. Such an assignment is possible, according to our rule of substitutability neverthe-
less, the compiler will permit the message draw to be used with this variable only if it can
ensure that the message will be understood by any value that may be associated with the
variable. Assigning a method to the class Shape eectively provides this assurance, even
when the method in class Shape is never actually executed.

12.6 Pure Polymorphism


Many authors reserve the term polymorphism (or pure polymorphism) for situations where
one function can be used with a variety of arguments, and the term overloading for situ-
ations where there are multiple functions all dened with a single name.2 Such facilities
are not restricted to object-oriented languages. In Lisp or ML, for example, it is easy to
write functions that manipulate lists of arbitrary elements such functions are polymorphic,
because the type of the argument is not known at the time the function is dened. The abil-
ity to form polymorphic functions is one of the most powerful techniques in object-oriented
programming. It permits code to be written once, at a high level of abstraction, and to be
tailored as necessary to t a variety of situations. Usually, the programmer accomplishes
this tailoring by sending further messages to the receiver for the method. These subsequent
messages often are not associated with the class at the level of the polymorphic method,
but rather are deferred methods dened in the lower classes.
An example will help us to illustrate this concept. As we noted in Chapter 8, the class
Number is an abstract class, parent to the wrapper classes such as Integer, Double, Float.
The denition of the class is similar to the following:
public abstract class Number f

public abstract int intValue()

public abstract long longValue()

public abstract float floatValue()

public abstract double doubleValue()

2 The extreme cases may be easy to recognize, but discovering the line that separates overloading from
polymorphism can be di cult. In both Java and ML a programmer can dene a number of functions, each
having the same name, but which take di erent arguments. Is it overloading in Java because the various
functions sharing the same name are not dened in one location, whereas in ML-style polymorphism they
must all be bundled together under a single heading?
12.7. EFFICIENCY AND POLYMORPHISM 213

public byte byteValue()


f return (byte) intValue() g

public short shortValue()


f return (short) intValue() g
g

The method intValue is abstract and deferred{each type of number must provide their
own implementation of this method. The method byteValue, on the other hand, is not
overridden. It is a purely polymorphic algorithm. Regardless of whether the receiver is an
integer, a double precision oating point value, or some other type of number, this is the
only denition that will be found. For all of these dierent types, when byteValue is invoked
this will be the algorithm that is executed.
The important dening characteristic of pure polymorphism, as opposed to overloading
and overriding, is that there is one function with the given name, used with a variety of
dierent arguments. Almost always, as in this case, the body of such an algorithm will make
use of other forms of polymorphism, such as the invocation of abstract functions shown here.

12.7 Eciency and Polymorphism


An essential point to note is that programming always involves compromises. In particular,
programming with polymorphism involves compromises between ease of development and
use, readability, and eciency. In large part, eciency has been already considered and
dismissed however, it would be remiss not to admit that it is an issue, however slight.
A function, such as the byteValue() method described in the last section, that does not
know the type of its arguments can seldom be as ecient as a function that has more
complete information. Nevertheless, the advantages of rapid development and consistent
application behavior and the possibilities of code reuse usually more than make up for any
small losses in eciency.

12.8 Chapter Summary


Polymorphism is an umbrella term that is used to describe a variety of dierent mechanisms
found in programming languages. In object-oriented languages the most important forms of
polymorphism are tied to the polymorphic variable{a variable that can hold many dierent
types of values. For example, overloading occurs when two or more functions share the same
name. If these functions happen to be found in classes that have a parent class/child class
relationship, then it is called overriding. If an overridden function is used with a polymorphic
variable, then the particular function executed will be determined by the run-time value of
the variable, not the compile-time declaration for the variable.
214 CHAPTER 12. POLYMORPHISM

Other forms of polymorphism include overloading from independent classes, parameteric


overloading (overloading that is disambiguated by the types of arguments used in a function
call), and abstract methods.
Note that the use of polymorphism tends to optimize program development time and
reliability, at the cost of run-time eciency. For most programs, the benets far exceed the
costs.

Study Questions
1. What does the term polymorphic mean in common usage?
2. What is a polymorphic variable?
3. How is the characterization of polymorphic variables dierent in dynamically typed
languages than in staticly typed languages?
4. What does it mean to say that a function name is overloaded?
5. What does it mean to say that a value has been coerced to a dierent type?
6. What is parameteric overloading?
7. What is overriding, and how is it dierent from overloading?
8. What is the dierence between overriding using replacement, and overriding using
renement?
9. What is the default semantics for overriding for methods? For constructors?
10. What is an abstract method?
11. How is an abstract method denoted?
12. What characterizes pure polymorphism?
13. Why should a programmer not be overly concerned with the loss of eciency due to
the use of polymorphic programming techniques?

Exercises
1. Describe the various types of polymorphism found in the Pinball game application
presented in Chapter 7.
2. Describe the various types of polymorphism found in the Solitare application presented
in Chapter 9.
Chapter 13

The AWT
The AWT, the Abstract Windowing Toolkit, is the portion of the Java run-time library
that is involved with creating, displaying, and facilitating user interaction with window
objects. The AWT is an example of a software framework. A framework is a way of
structuring generic solutions to a common problem, using polymorphism as a means of
creating specialized solutions for each new application. Thus, examining the AWT will
illustrate how polymorphism is used in a powerful and dynamic fashion in the language
Java.

13.1 The AWT Class Hierarchy


From the very rst, we have said that class Frame represents the Java notion of an application
window, a two dimensional graphical surface that is shown on the display device, and through
which the user interacts with a computer program. All our applications have been formed by
subclassing from Frame, overriding various methods, such as the paint method for repainting
the window. In actuality, much of the behavior provided by class Frame is inherited from
parent classes (see Figure 13.1). Examining each of these abstractions in turn will help
illustrate the functioning of the Java windowing system, as well as illustrating the power of
inheritance as a mechanism for code reuse and sharing.
The class Object is the parent class of all classes in Java. It provides the ability to
compare two objects for equality, compute a hash value for an object, and determine the
class of an object. Methods dened in class Object include the following:
equals (anObject) returns true if object is equal to argument
getClass () returns the name of the class of an object
hashCode () returns a hash value for an object
toString () returns a string representation of an object
215
216 CHAPTER 13. THE AWT

Object

Component

Button Checkbox Choice Container


XX Label List Scrollbar Canvas
Q XX
 Q XXX
 Q X
TextComponent Panel Window ScrollPane
Q Q
 Q  Q
 Q  Q
TextArea TextField Dialog Frame

Figure 13.1: The AWT class hierarchy

A Component is something that can be displayed on a two dimensional screen and with
which the user can interact. Attributes of a component include a size, a location, foreground
and background colors, whether or not it is visible, and a set of listeners for events. Methods
dened in class Component include the following:
enable(), disable() enable/disable a component
setLocation(int,int), getLocation() set and get component location
setSize(int,int), getSize() set and get size of component
setVisible(boolean) show or hide the component
setForeground(Color), getForegound() set and get foreground colors
setBackground(Color), getBackground() set and get background colors
setFont(Font), getFont() set and get font
repaint(Graphics) schedule component for repainting
paint(Graphics) repaint component appearance
addMouseListener(MouseListener) add a mouse listener for component
addKeyListener(KeyListener) add a keypress listener for component
Besides frames, other types of components include buttons, checkboxes, scroll bars, and
text areas.
A Container is a type of component that can nest other components within it. A container
is the way that complex graphical interfaces are constructed. A Frame is a type of Container,
so it can hold objects such as buttons and scroll bars. When more complicated interfaces
are necessary, a Panel (another type of container) can be constructed, which might hold, for
example, a collection of buttons. Since this Panel is both a Container and a Component, it can
be inserted into the Frame. A container maintains a list of the components it manipulates,
as well as a layout manager to determine how the components should be displayed. Methods
dened in class Container include the following:
13.1. THE AWT CLASS HIERARCHY 217
setLayout (LayoutManager) set layout manager for display
add (Component), remove (Component) add or remove component from display
A Window is a type of Container. A window is a two-dimensional drawing surface that
can be displayed on an output device. A window can be stacked on top of other windows,
and moved either to the front or back of the visible windows. Methods dened in class
Window include the following:
show() make the window visible
toFront() move window to front
toBack() move window to back
Finally, a Frame is a type of window with a title bar, a menu bar, a border, a cursor,
and other properties. Methods dened in class Frame include the following:
setTitle(String), getTitle() set or get title
setCursor(int) set cursor
setResizable() make the window resizable
setMenuBar(MenuBar) set menu bar for window
If we consider a typical application, such as the CannonWorld application of Chapter 6,
we see that it uses methods from a number of dierent levels of the class hierarchy:
setTitle(String) inherited from class Frame
setSize(int, int) inherited from class Component
show() inherited from class Window
repaint() inherited from class Component
paint() inherited from Component, overridden in application class
The power of the AWT, indeed the power of any framework, comes through the use of
a polymorphic variable. When the method show in class Window is invoked, it calls the
method setVisible in class Component. This method calls repaint, which in turn calls paint.
The code for the algorithm used by setVisible and repaint resides in class Component. When
it is being executed, the framework \thinks" that it is dealing only with an instance of
Component. However, in actuality the method paint that is being executed is the version
that has been overridden in the application class. Thus, there are two views of the function
being executed, as described in Figure 13.2.
The code in the parent classes (Component, Container, Window and Frame) can all be
written without reference to any particular application. Thus, this code can be easily carried
from one application to the next. To specialize the design framework to a new application
it is only necessary to override the appropriate methods (such as paint or event listeners) to
dene application specic behavior. Thus, the combination of inheritance, overriding, and
polymorphism permits design and software reuse on a grand scale.
218 CHAPTER 13. THE AWT

class Container extends Object f sm AWT
void paint (Graphics g) ... 
g
 view

 @
@

programmers
ms @


class CannonWorld extends Frame f
view void paint (Graphics g) ... 
g

Figure 13.2: Two views of a component

13.2 The Layout Manager


The idea of a layout manager, which is the technique used by the AWT to assign the
locations of components within a container, is an excellent illustration of the combination
of polymorphic techniques of composition and inheritance. The layout manager is charged
with taking the list of components held in a container, and assigning them positions on the
surface covered by the container. There are a variety of standard layout managers, each
of which will place components in slightly dierent ways. The programmer developing a
graphical user interface creates an instance of a layout manager and hands it to a container.
Generally, the task of creation is the only direct interaction the programmer will have with
the layout manager, as thereafter all commands will be handled by the container itself.
The connections between the application class, the container, and the layout manager
illustrate yet once more the many ways that inheritance, composition, and interfaces can be
combined (Figure 13.3). The application class may inherit from the container (as is usually
the case when an application is formed using inheritance from class Frame) or it may hold
the container as a component. The container itself, however, holds the layout manager as a
data eld, as part of the internal state of the container. But in actual fact, the variable that
holds the layout manager is polymorphic. While the Container thinks that it is maintaining
a value of type LayoutManager, in fact it will be holding a value from some other type, such
as GridLayout, that is implementing the LayoutManager interface.
There are three dierent mechanisms at work here inheritance, composition, and im-
plementation of an interface. Each is serving a slightly dierent purpose. Inheritance is the
is-a relation, and links the application class to the parent window class. This allows the
code written in the AWT class Window to perform application-specic actions, by invoking
13.2. THE LAYOUT MANAGER 219

holds
Container LayoutManager

inherits implements

Application GridLayout

Figure 13.3: Relationships between Layout Manager Components

methods in the application class that override methods in the parent class (paint(), for ex-
ample). The fact that composition is used to link the container with the layout manager
makes the link between these two items very dynamic{the programmer can easily change
the type of layout manager being employed by a container. This dynamic behavior is very
dicult to achieve using inheritance alone, since the inheritance relationship between a par-
ent and child is established at compile time. Finally, the fact that LayoutManager is simply
and interface, and that various dierent classes of objects implement this interface, means
that the programmer is free to develop alternative layout managers using a wide variety of
techniques. (This freedom would be much more constrained if, for example, LayoutManager
was a class which alternative layout managers needed to extend).

13.2.1 Layout Manager Types


There are ve standard types of layout managers. These are BorderLayout, GridLayout,
CardLayout, FlowLayout and GridBagLayout. The BorderLayout manager can manage no
more than ve dierent components. This is the default layout manager for applications
that are constructed by subclassing from Frame. The ve locations are shown in Figure 13.4.
They correspond to the left and right, top and bottom, and center of the display. Not all
ve locations need be lled. If a location is not used, the space is allocated to the remaining
components.
When a border layout manager is employed the rst argument in the add method is used
to specify which position a component in lling in a collection:
add("North", new Button("quit"))
add("Center", colorField)

The next most common type of layout is the GridLayout. The manager for this layout
creates rectangular array of components, each occupying the same size portion of the screen.
220 CHAPTER 13. THE AWT

Figure 13.4: Locations Recognized by Border Layout Manager

Using arguments with the constructor, the programmer species the number of rows and
the number of columns in the grid. Two additional integer arguments can be used to specify
a horizontal and vertical space between the components. An example of a panel formatted
using a GridLayout is shown in Figure 13.8. The section of code for that application that
creates the layout manager is as follows:
Panel p = new Panel()
// make a 4 by 4 grid,
// with 3 pixels between each element
p.setLayout (new GridLayout(4, 4, 3, 3))

p.add (new ColorButton(Color.black, "black"))


p.add (new ColorButton(Color.blue, "blue"))

A FlowLayout manager places components in rows left to right, top to bottom. Unlike the
layout created by a GridLayout manager, the components managed by a ow layout manager
need not all have the same size. When a component cannot be completely placed on a row
without truncation, a new row is created. The ow manager is the default layout manager
for the class Panel (as opposed to Frame, where the default manager is a BorderLayout).
A CardLayout manager stacks components vertically. Only one component is visible at
any one time. The components managed by a card layout manager can be named (using
the string argument to the add method). Subsequently, a named component can be made
the visible component. This is one of the few instances where the programmer would have
direct interaction with the layout manager.
CardLayout lm = new CardLayout()
Panel p = new Panel (lm)
p.add ("One", new Label ("Number One"))
13.3. USER INTERFACE COMPONENTS 221

p.add ("Two", new Label ("Number Two"))


p.add ("Three", new Label ("Number Three"))
...
lm.show ("Two") // show component \Two"

The most general type of layout manager is the GridBagLayout manager. This manager
allows the programmer to create a non-uniform grid of squares, and place components in
various positions within each square. However, the details of the use of this manager are
complex, and will not be described here.

13.3 User Interface Components


The variety of user interface components in the Java AWT library are again a good illustra-
tion of the power of polymorphism provided both through inheritance and interfaces. With
the exception of menu bars, all the user interface components are subclassed from the parent
class Component (Figure 13.1). Containers assume only that the elements they will hold
are instances of class Component. In fact, the values they maintain are polymorphic, and
represent more specialized values, such as buttons or scroll bars. Thus, the design of the user
interface construction system depends upon the mechanisms of inheritance, polymorphism
and substitutability.

13.3.1 Labels
The simplest type of user interface component is a Label. A label has only the text it will
display. It will display as much of the text as it can in the area it is given.
Label lab = new Label("score: 0 to 0")
add ("South", lab) // put label on top of window
Unlike other components, a label does not respond to any type of event, such as a mouse
click or a key press. However, the text of the label can be changed using the function
setText(String), and the current text of a label can be retrieved using getText().

13.3.2 Button
A Button is a labeled component, usually represented by a rounded box, that can respond
to user interaction. As we have seen in earlier programs, interaction with a button is
achieved by attaching an ActionListener object to the button. The ActionListener object is
then notied when the button is pressed.
Button b = new Button ("do it!")
222 CHAPTER 13. THE AWT

b.addActionListener (new doIt())


..
private class doIt implements ActionListener f
public void actionPerformed (ActionEvent e) f
// what ever do it does
...
g
g

A useful trick is to combine the button object and the button listener in one new class.
This new class both subclasses from the original Button class and implements the ActionLis-
tener interface. For example, in the case study that is presented in Section 13.5, we create
a set of buttons for dierent colors. Each button holds a color value, and when pressed
invokes a method using the color as argument. This class is written as follows:
private class ColorButton extends Button implements ActionListener f
private Color ourColor

public ColorButton (Color c, String name) f


super (name) // create the button
ourColor = c // save the color value
addActionListener (this) // add ourselves as listener
g

public void actionPerformed (ActionEvent e) f


// set color for middle panel
setFromColor (ourColor)
g
g

Notice how the object registers itself as a listener for button actions. The pseudo-variable
this is used when an object needs to denote itself. When pressed, the button will invoke the
method actionPerformed, which will then invoke the procedure setFromColor that is found in
the surrounding class.
We can even take this trick one step further, and dene a generic ButtonAdaptor class
that is both a button and a listener. The actions of the listener will be encapsulated by an
abstract method, which must be implemented by a subclass:
abstract class ButtonAdaptor extends Button implements ActionListener f
public ButtonAdaptor (String name) f
super (name)
addActionListener (this)
13.3. USER INTERFACE COMPONENTS 223

public void actionPerformed (ActionEvent e) f run() g

public abstract void run ()


g

To create a button using this abstraction, the programmer must subclass and override
the method run. This, however, can be done easily using a class denition expression. The
following, for example, creates a button that when pressed will halt the application.
Panel p = new Panel()

p.add (new ButtonAdaptor("Quit")f


public void run () f System.exit(0) gg)

13.3.3 Canvas
A Canvas is a simple type of component, having only a size and the ability to be a target
for drawing operations. Among other uses, the class Canvas is often subclassed to form
new types of components. We will illustrate one use of a Canvas when we discuss the class
ScrollPane (Section 13.4.1).

13.3.4 Scroll Bars


A ScrollBar is a slider, used to specify integer values over a wide range. Scroll bars can be
displayed in either a horizontal or a vertical direction. The maximum and minimum values
can be specied, as well as the line increment (the amount the scroll bar will move when it is
touched in the ends), and the page increment (the amount it will move when it is touched in
the background area between the slider and the end). Like a button, interaction is provided
for a scroll bar by dening a listener that will be notied when the scroll bar is modied.
The case study at the end of this chapter uses a trick similar to the one described earlier
in the section on buttons. Figure 13.8 shows a snapshot of this application, which includes
three vertical scroll bars. The class ColorBar represents a scroll bar for maintaining colors.
The constructor for the class creates a vertical scroll bar with an initial value of 40 and
a range between 0 and 255. The background color for the scroll bar is set using a given
argument. Finally, the object itself is made a listener for scroll bar events. When the scroll
bar is changed, the method adjustmentValueChanged will be executed. Typically, within
this method the current value of the scroll bar would be accessed using getValue(). In this
particular application, a bank of three scroll bars will be created, and the value of all three
will be recovered in a shared procedure named setFromBar.
224 CHAPTER 13. THE AWT

private class ColorBar extends Scrollbar implements AdjustmentListener f


public ColorBar (Color c) f
super (Scrollbar.VERTICAL, 40, 0, 0, 255)
setBackground (c)
addAdjustmentListener (this)
g

public void adjustmentValueChanged (AdjustmentEvent e) f


// method setFromBar will get scroll bar
// value using getValue ()
setFromBar ()
g
g

13.3.5 Text Components


A text component is used to display editable text. There are two varieties of text compo-
nents, TextField and TextArea. The rst is a xed size block, while the second uses scroll
bars to display a larger block of text, not all of which might be viewable at any one time.
The following illustrates these two types of items:

The text in a text component can be set or accessed by the program using the functions
setText(String) and getText(). Additional text can be added to the text area using the
method append(String). Various other methods can be used to indicate whether or not the
text is editable, and to select a subportion of the text. A TextListener can be attached to a
text component. The listener must implement the TextListener interface:
interface TextListener extends EventListener f
public void textValueChanged (TextEvent e)
13.3. USER INTERFACE COMPONENTS 225

13.3.6 Checkbox
A Checkbox is a component that maintains and displays a labeled binary state. The state
described by a check box can be either on or o. The current state of the Checkbox can be
set or tested by the programmer. A Checkbox is typically used in an application to indicate
a binary (on/o, yes/no) choice.

Both the label and the state of the Checkbox can be set by the programmer, using
the functions getLabel, setLabel, getState and setState. Changing the state of a check box
creates an ItemEvent, that is registered with any ItemListener objects. The following simple
application illustrates the use of these methods:
class CheckTest extends Frame f
private Checkbox cb = new Checkbox ("the checkbox is off")

public static void main (String  ] args)


f Frame world = new CheckTest() world.show() g

public CheckTest () f
setTitle("Check box example") setSize(300, 70)
cb.addItemListener (new CheckListener())
add ("Center", cb)
g

private class CheckListener implements ItemListener f

public void itemStateChanged (ItemEvent e) f


if (cb.getState())
cb.setLabel ("The checkbox is on")
else cb.setLabel ("The checkbox is off")
g
g
g
226 CHAPTER 13. THE AWT

13.3.7 Checkbox Groups, Choices and Lists


There are three types of interface components that are typically employed to allow the user
to select one item from a large number of possibilities. The rst is a group of connected
checkboxes, that have the property that only one can be set at any one time. Such a
collection is sometimes called a radio button group, since their behavior is similar to the
way buttons in car radios work. The second form is termed a Choice. A Choice object
displays only one selection, but when the user clicks the mouse in the selection area, a
pop-up menu appears that allows the choice to be changed to a dierent selection. A third
possibility is termed a List. A List is similar to a Choice, however several possibilities out of
the range can be displayed at one time.
The following gure illustrates all three possibilities. The code to produce this example
is shown in Figure 13.5.

A Checkbox group should be used when the number of alternatives is small. A choice
or a list should be used if the number of alternatives is ve or more. A choice takes up less
space in the display, but makes it more dicult to view all the alternatives.
To create a Choice or a List object, the programmer species each alternative using the
method addItem. An ItemListener can be attached to the object. When a selection is made,
the listener will be informed using the method itemStateChanged. The text of the selected
item can be recovered using the method getSelectedItem.
To structure a group of check boxes as a group, the programmer rst creates a Check-
boxGroup. This value is then passed as argument to each created check box, along with a
third argument that indicates whether or not the check box should be initially active. If
more than one button is made active (as here) only the last button will remain active. The
current check box can be accessed using the method getSelectedCheckbox.
As a check box group is constructed out of several components, it is almost always laid
out on a Panel. The Panel is then placed as a single element in the original layout. This is
shown in Figure 13.5. Here a ve by two grid is used as layout for the ten check boxes.
13.3. USER INTERFACE COMPONENTS 227

class ChoiceTest extends Frame f


public static void main (String  ] args)
f Window world = new ChoiceTest() world.show() g

private String  ] choices = f"One", "Two", "Three", "Four",


"Five", "Six", "Seven", "Eight", "Nine", "Ten"g
private Label display = new Label()
private Choice theChoice = new Choice()
private List theList = new List()
private CheckboxGroup theGroup = new CheckboxGroup()
private ItemListener theListener = new ChoiceListener()

public ChoiceTest () f
setTitle ("selection example ")
setSize (300, 300)
for (int i = 0 i < 10 i++) f
theChoice.addItem (choicesi])
theList.addItem (choicesi]) g
theChoice.addItemListener (theListener)
theList.addItemListener (theListener)
add ("West", makeCheckBoxes()) add ("North", theChoice)
add ("East", theList) add ("South", display)
g

private class ChoiceListener implements ItemListener f


public void itemStateChanged (ItemEvent e) f
display.setText (theGroup.getSelectedCheckboxGroup().getLabel()
+ theList.getSelectedItem() + theChoice.getSelectedItem())
g
g

private Panel makeCheckBoxes() f


Panel p = new Panel (new GridLayout(5,2))
for (int i = 0 i < 10 i++) f
Checkbox cb = new Checkbox(choicesi], theGroup, false)
cb.addItemListener (theListener) p.add (cb) g
return p
g
g

Figure 13.5: Alternative ways to display choices


228 CHAPTER 13. THE AWT

13.4 Panels
A Panel is a Container that acts like a Component. A panel represents a rectangular region
of the display. Each panel holds its own layout manager, which can dier from the layout
manager for the application display. Items can be inserted into the panel. The panel, as a
single unit, it then inserted into the application display.
The use of a panel is illustrated by the application described in Figure 13.5. Here the
method makeCheckBoxes creates a panel to hold the ten check boxes that make up the check
box group. This panel is structured, using a GridLayout as a ve by two element matrix.
This group of ten components can then be treated as a single element, and is placed on the
left side of the application layout.
More examples of the use of panels will be provided by the application that will be
described in the next section. A snapshot of the window for this application is shown in
Figure 13.8. The three scroll bars on the left are placed on a Panel. This panel is laid out
using a BorderLayout manager. The procedure to create and return this panel is described
as follows:
private Panel makeScrollBars () f
Panel p = new Panel()
p.setLayout (new BorderLayout())
p.add("West", redBar)
p.add("Center", greenBar)
p.add("East", blueBar)
return p
g

The panel returned as the result of this method is then placed on the left side of the
application window.

13.4.1 ScrollPane
A ScrollPane is in many ways similar to a Panel. Like a panel, it can hold another component.
However, a ScrollPane can only hold one component, and it does not have a layout manager.
If the size of the component being held is larger than the size of the ScrollPane itself, scroll
bars will be automatically generated to allow the user to move the underlying component.
We illustrate the use of a ScrollPane with a simple test program, shown in Figure 13.6.
The application window in this program will be set to 300 by 300 pixels, but a scroll pane
is created that holds a canvas that has been sized to 1000 by 1000 pixels. Scroll bars
will therefore be added automatically that allow the user to see portions of the underlying
canvas. As mouse events are detected by the canvas, points will be added to a Polygon. To
paint the application window, the canvas simply draws the polygon values.
13.4. PANELS 229

class BigCanvas extends Frame f

public static void main ( String  ] args) f


Frame world = new BigCanvas()
world.show()
g

private Polygon poly = new Polygon()


private Canvas cv = new Canvas()

public BigCanvas () f
setSize (300, 300)
setTitle ("Scroll Pane Test")
// make canvas larger than window
cv.setSize (1000, 1000)
cv.addMouseListener (new MouseKeeper())
// make scroll pane to manage canvas
ScrollPane sp = new ScrollPane()
sp.add(cv)
add("Center", sp)
g

public void paint (Graphics g) f


// redraw canvas
Graphics gr = cv.getGraphics()
gr.drawPolygon (poly)
g

private class MouseKeeper extends MouseAdapter f


public void mousePressed (MouseEvent e) f
poly.addPoint (e.getX(), e.getY())
repaint()
g
g
g

Figure 13.6: Test program for Scroll Panes


230 CHAPTER 13. THE AWT

13.5 Case Study: A Color Display


A simple test program will illustrate how panels and layout managers are used in developing
user interfaces. The application will also illustrate the use of scroll bars, and the use of
methods provided by the class Color. Finally, we can also use this program to illustrate how
nested classes can be employed to combine the actions of creating a new graphical component
(such as a button or a slider) and listening for actions relating to the component.
The class ColorTest (Figure 13.7) creates a window for displaying color values. The
window, shown in Figure 13.8, is divided into four separate regions. These four regions are
managed by the default layout manager for class Frame. This layout manager is a value of
type BorderLayout.
At the top (the \north" side) is a text region, a component of type TextField, that
describes the current color. To the left (the \west" region) is a trio of sliders that can be
used to set the red, green and blue values. To the right (the \east" region) is a four by
four bank of 16 buttons. These are constructed on a Panel that is organized by a GridLayout
manager. Thirteen of the buttons represent the pre-dened color values. Two more represent
the actions of making a color brighter and darker. The nal button will halt the application.
Finally, in the middle will be a square panel that represents the specied color.
The class ColorTest holds six data elds. The rst represents the current color in the
middle panel, while the remaining ve represent dierent graphical objects. Three represent
the slider, one represents the text eld at the top of the page, and one represents the color
panel in the middle.
The three sliders make use of the class ColorBar described earlier in Section 13.3.4. The
argument used with the constructor for each class is the color to be used in painting the
13.5. CASE STUDY: A COLOR DISPLAY 231

class ColorTest extends Frame f


static public void main (String  ] args)
f Frame window = new ColorTest() window.show() g

private TextField colorDescription = new TextField()


private Panel colorField = new Panel()
private Color current = Color.black
private Scrollbar redBar = new ColorBar(Color.red)
private Scrollbar greenBar = new ColorBar(Color.green)
private Scrollbar blueBar = new ColorBar(Color.blue)

public ColorTest () f
setTitle ("color test") setSize (400, 600)
add("North", colorDescription)
add("East", makeColorButtons())
add("Center", colorField)
add("West", makeScrollBars())
setFromColor (current)
g

private void setFromColor (Color c) f


current = c colorField.setBackground (current)
redBar.setValue(c.getRed()) greenBar.setValue(c.getGreen())
blueBar.setValue(c.getBlue())
colorDescription.setText(c.toString())
g

private void setFromBar () f


int r = redBar.getValue() int g = greenBar.getValue()
int b = blueBar.getValue() setFromColor (new Color(r, g, b))
g

private Panel makeColorButtons () f ... g


private Panel makeScrollBars () f ... g
private class BrightenButton extends Button implements ActionListener ...
private class ColorButton extends Button implements ActionListener ...
private class ColorBar extends Scrollbar implements AdjustmentListener ...
g

Figure 13.7: The class ColorTest


232 CHAPTER 13. THE AWT

Figure 13.8: Snaphot of ColorTest application

buttons and background for the scroll bar. You will recall that when adjusted, the scroll
bar will invoke its listener, which will execute the method adjustmentValueChanged. This
method will then execute the procedure setFromBar.
A method makeScrollBars, used to create the panel that holds the three scroll bars, was
described earlier in Section 13.4.
The idea of combining inheritance and implementation of an interface is used in creating
the buttons that represent the thirteen predened colors. Each instance of ColorButton,
shown earlier in Section 13.3.2, both extends the class Button and implements the Action-
Listener interface. When the button is pressed, the method setFromColor will be used to set
the color of the middle panel using the color stored in the button.
The class BrightenButton is slightly more complex. An index value is stored with the
button. This value indicates whether the button represents the \brighten" button or the
\darken" button. When pressed, the current color is modied by the appropriate method,
and the new value used to set the current color.
private class BrightenButton extends Button implements ActionListener f
private int index
public BrightenButton (int i) f
super ( i == 0 ? "brighter" : "darker")
index = i
addActionListener(this)
g

public void actionPerformed (ActionEvent e) f


13.6. DIALOGS 233

if (index == 0)
setFromColor (current.brighter())
else
setFromColor (current.darker())
g
g

A panel is used to hold the sixteen button values. In this case the layout is described
by a 4 by 4 grid pattern. Thirteen represent the predened buttons. Two represent the
brighter and darker buttons. And the nal creates a button that when pressed exits the
application.
private Panel makeColorButtons () f
Panel p = new Panel()
p.setLayout (new GridLayout(4,4,3,3))
p.add (new ColorButton(Color.black, "black"))
p.add (new ColorButton(Color.blue, "blue"))
p.add (new ColorButton(Color.cyan, "cyan"))
p.add (new ColorButton(Color.darkGray, "darkGray"))
p.add (new ColorButton(Color.gray, "gray"))
p.add (new ColorButton(Color.green, "green"))
p.add (new ColorButton(Color.lightGray, "lightGray"))
p.add (new ColorButton(Color.magenta, "magenta"))
p.add (new ColorButton(Color.orange, "orange"))
p.add (new ColorButton(Color.pink, "pink"))
p.add (new ColorButton(Color.red, "red"))
p.add (new ColorButton(Color.white, "white"))
p.add (new ColorButton(Color.yellow, "yellow"))
p.add (new BrightenButton(0))
p.add (new BrightenButton(1))
p.add (new ButtonAdaptor("Quit")f
public void run() f System.exit(0) gg)
return p
g

13.6 Dialogs
A Dialog is a special purpose window that is displayed for a short period of time during
the course of execution, and thereafter disappears. Dialogs are often used to notify the
user of certain events, or to ask simple questions. A dialog must always be attached to an
234 CHAPTER 13. THE AWT

instance of Frame, and disappears automatically when the frame is hidden (such as when
the application halts).
Dialog windows can be modal or nonmodal. A modal dialog must be handled, and pre-
vents the user from performing any further action until the dialog is dismissed. A nonmodal
dialog, sometimes called a modeless dialog, can be ignored by the user. The processing of
actions for a nonmodal dialog is often placed in a separate Thread (See Chapter 21), so that
the actions produced by the dialog will not disrupt the continuing processing of the rest of
the application. Whether or not a dialog is modal is determined when the dialog is created.
The two arguments used in the constructor for the dialog are the application Frame and a
boolean value that is true if the dialog is modal.
// create a new nonmodal dialog in current application
Dialog = new Dialog (this, false)

Because a Dialog is a type of Window, graphical components can be placed in the dialog
area, just as in a Frame or Panel. The default layout manager for a dialog is BorderLayout,
the same as with Frame.
The most common functions used with a dialog are not actually dened in the class
Dialog, but are inherited from parent classes. These include the following:
setSize(int, int) set window size
show() display window
setVisible(false) remove window from display
setTitle(String), getTitle() set or get title of window
For modal dialogs, the show method does not return until the dialog is dismissed. Such
dialogs must therefore invoke the setVisible(false) method sometime during their processing.

13.6.1 Example Program for Dialogs


An example program will illustrate the creation and manipulation of dialogs. The appli-
cation shown in Figure 13.10 creates a window with a check box, a button, and a text area.
The application window, as well as an example dialog box window, is shown in Figure 13.9.
The check box allows the user to specify either a modal or modeless dialog box should be
created. The button creates the dialog, while the text area records button presses performed
by the dialog.
The procedure makeDialog creates the dialog box. The size of the box is set at 100 by
100 pixels, and four buttons are placed on the box. Three buttons simply type text into the
display when pressed, while the last button will hide the dialog. For a modal dialog hiding
the dialog is the same as dismissing the dialog box, and returns control to the procedure
that created the dialog.
13.7. THE MENU BAR 235

Figure 13.9: Dialog Example Window

13.7 The Menu Bar


Although a menu bar is a graphical component, it is not declared as a subclass of Component.
This is because platforms dier in how they handle menu bars, so the implementation must
be much more constrained. Both menu bars and menus act much like containers. A menu
bar contains a series of menus, and each menu contains a series of menu items.
An instance of MenuBar can be attached to a Frame using the method setMenuBar:
...
MenuBar bar = new MenuBar()
setMenuBar (bar)
...

Individual menus are named, and are placed on the menu bar using the method add:
...
Menu helpMenu = new Menu ("Help")
bar.add (helpMenu)
...

Menu items are created using the class MenuItem. Each menu item maintains a list of
ActionListener objects, the same class used to handle Button events. The listeners will be
notied when the menu item is selected.
236 CHAPTER 13. THE AWT

class DialogTest extends Frame f


static public void main (String  ] args)
f Frame world = new DialogTest() world.show() g

private TextArea display = new TextArea()


private Checkbox cb = new Checkbox("Modal Dialog?")

public DialogTest () f
setTitle ("Dialog Test Program")
setSize (300, 220)

add ("West", cb)


add ("East", new Makebutton())
add ("South", display)
g

private class Makebutton extends ButtonAdaptor f


public Makebutton () f super ("Make Dialog") g
public void run () f makeDialog (cb.getState()) g
g

private void makeDialog (boolean modalFlag) f


final Dialog dlg = new Dialog (this, modalFlag)
dlg.setSize (100, 100)
dlg.add ("North", new CountButton(1))
dlg.add ("West", new CountButton(2))
dlg.add ("East", new CountButton(3))
dlg.add ("South", new ButtonAdaptor("Hide") f
public void run () f dlg.setVisible(false) gg)
dlg.show()
g

private class CountButton extends ButtonAdaptor f


public CountButton (int val) f super ("" + val) g
public void run () f
display.append("Button " + getLabel() + " pressed\n")g
g
g

Figure 13.10: Example Program for Creating Dialogs


13.7. THE MENU BAR 237

...
MenuItem quitItem = new MenuItem ("Quit")
quitItem.addActionListener (new QuitListener())
helpMenu.add (quitItem)
...

There are a number of techniques that can be used to create special-purpose menus, such
as tear-o menus, cascading menus, and so on. However, these will not be described here.
13.7.1 A Quit Menu Facility
On many platforms it is sometimes dicult to stop a running Java application. For this
reason, it is useful to dene a general purpose Quit menu bar facility. The class QuitItem
(Figure 13.11) creates a listener that will halt the running application when the associated
menu item is selected. By overloading the constructor, we make it trivial to add this
functionality to any application.
The constructor for QuitItem can be given a MenuItem as argument. In this case it
merely attaches itself as a listener to the menu item. Alternatively, it can be given a Menu,
in which case it creates a menu item labeled \Quit". Or it can be given a MenuBar, in which
case it creates a new menu labeled \Quit" that contains only the quit menu item. Finally,
the constructor can be given an application as argument, in which case it creates a new
menu bar containing only the one menu which contains only the single quit item. Using the
application constructor, a quit menu selection can be added to an application by placing
only a single line in the constructor for the application:
class ColorTest extends Frame f
...
public ColorTest () f
...
// add quit menu item to application
new QuitItem (this)
...
g
g

Study Questions
1. What do the letters AWT stand for?
2. What are the parent classes of class Frame?
238 CHAPTER 13. THE AWT

class QuitItem implements ActionListener f

public QuitItem (Frame application) f


MenuBar mbar = new MenuBar()
application.setMenuBar (mbar)
Menu menu = new Menu("Quit")
mbar.add (menu)
MenuItem mitem = new MenuItem("Quit")
mitem.addActionListener (this)
menu.add (mitem)
g

public QuitItem (MenuBar mbar) f


Menu menu = new Menu("Quit")
mbar.add (menu)
MenuItem mitem = new MenuItem("Quit")
mitem.addActionListener (this)
menu.add (mitem)
g

public QuitItem (Menu menu) f


MenuItem mitem = new MenuItem("Quit")
mitem.addActionListener (this)
menu.add (mitem)
g

public QuitItem (MenuItem mitem)


f mitem.addActionListener (this) g

public void actionPerformed (ActionEvent e)


f System.exit(0) g
g

Figure 13.11: A General Purpose Quite Item Class


13.7. THE MENU BAR 239

3. In what AWT class is the method setBackground dened?


4. How is a container dierent from other types of components?
5. Explain why in a framework there are two views of an overridden method, such as
paint.
6. What is the task performed by the layout manager?
7. Explain how the three mechanisms of inheritance, composition, and implementation of
an interface are all involved in the task of attaching a layout manager to a container.
8. What are the ve dierent layout manager types? Which mangers use the one argu-
ment add method, and which use the method in which the rst argument is a String
value and the second a component?
9. What is the dierence between a TextArea and a TextField?
10. What are the three dierent types of components that allow the user to select one
item out of many possibilities?
11. What is a Panel?
12. What are the thirteen predened values provided by class Color?
13. What do the three numerical values that dene a color represent?
14. In what ways is a MenuBar similar to a Component? In what ways is it dierent?

Exercises
1. Add a menu bar to the Solitare program described in Chapter 9. Then, add two menu
items, one to quit the application, and one to reset the application for a new game.
2. Using a text box and a grid of buttons, create a simple calculator application. Buttons
correspond to digits and the four arithmetic functions +, ;,  and =, as well as the
equals sign.
240 CHAPTER 13. THE AWT
Chapter 14

Input and Output Streams


At rst glance, the input/output facilities in Java seems to present a confusing profusion
of alternatives. Over two dozen classes are used to implement stream I/O. However, lying
behind this multitude there an elegant logical structure that makes ecient and eective
use of inheritance and polymorphism. Understanding how to manipulate the le facilities
will be much easier once you appreciate the structure and purpose of these classes.

14.1 Input Streams


The class InputStream is a virtual class, parent to ten subclasses in the Java library
(Figure 14.1). Each class implements roughly the same behavior. This input stream protocol
can be described as follows:

InputStream

ByteArray File Filter Piped Sequence StringBuer


InputStream InputStream InputStream InputStream InputStream InputStream

Data Buered LineNumber Pushback


InputStream InputStream InputStream InputStream

Figure 14.1: Subclasses of Input Stream


241
242 CHAPTER 14. INPUT AND OUTPUT STREAMS

abstract class InputStream f

// read a single byte from the input


int read() throws IOException

// read a buer of values from the input


int read (byte] buffer) throws IOException

// skip the indicated number of values from input


long skip (long n) throws IOException

// determine number of bytes readable without blocking


int available() throws IOException

// close this input stream


void close() throws IOException
g

An IOException will be thrown if a le cannot be opened, or if an interrupt is encountered


while reading from a pipe, or in similar situations.
The dierences between the variety of input stream classes can best be understood by
dividing them into two major categories. The rst are those classes that are tied to a physical
input source. These read values from a string buer, a byte array, a le or a pipe. The
second are those input streams that are virtual, that depend upon another input stream
for the actual reading operations, but extend the functionality of the input stream in some
fashion. For example, a LineNumberInputStream reads values from another input stream,
but watches for newline characters as values are read. As each newline character is seen a
counter is incremented. This counter therefore indicates the number of lines that have been
processed.

14.1.1 Physical Input Streams


There are four input stream classes that read from actual data areas. These are distinguished
by their names, and the arguments used in their constructor. A ByteArrayInputStream, for
example, must be given an array of byte values as an argument. A FileInputStream requires
either a le or a le name, and so on.
// constructors for various input stream
ByteArrayInputStream (byte  ] buffer)
ByteArrayInputStream (byte  ] buffer, int offset, int count)
FileInputStream (File f)
FileInputStream (String fileName)
14.1. INPUT STREAMS 243

StringBufferInputStream (String s)


PipedInputStream (PipedOutputStream p)

Each of the input stream classes support the common InputStream protocol. Some of
the classes augment this with additional methods. For example, a StringBuerInputStream
can be reset () back to the beginning of the underlying string.
Note that for simple reading and writing, a FileInputStream or FileOutputStream can be
manipulated without rst creating a File object. Generally, a File is necessary only if one is
doing operations to the le itself, such as renaming or removing the le. Another reason for
creating a File object would be to test whether a le is readable or writable before beginning
a sequence of input/output operations.

14.1.2 Virtual Input Streams


The class SequenceInputStream and the four subclasses of FilterInputStream can be thought
of as virtual input classes. None of these actually read characters from any input area, but
instead they rely on one or more underlying input streams for their data source. Each adds
useful functionality as values are passed through the class.
For example, a SequenceInputStream takes a sequence of two or more input streams, and
logically places them end to end. When one input stream is exhausted, the next stream in
sequence is started without interruption or any action on the users part. The underlying
streams can either be specied as two arguments to the constructor (if there are only two
input streams to be catenated), or as an enumeration to an underlying collection of input
streams (if there are more than two input streams).
InputStream f1 = new FileInputStream ("file1.text")
InputStream f2 = new FileInputStream ("file2.text")
InputStream f3 = new SequenceInputStream (f1, f2)
// f3 now represents the catenation of le1 and le2

Vector fv = new Vector ()


fv.addElement (f1) fv.addElement (f2)
InputStream f4 = new SequenceInputStream (fv.elements())
// f4 also now represents the same catenation

The structure of the class SequenceInputStream is an example of the composition design


pattern (see Section 15.2).
As we noted in Chapter 10, the subclasses of FilterInput represent an interesting combi-
nation of the object-oriented mechanisms of inheritance and composition, characteristic of
the wrapper design pattern (see Section 15.9). Because each class inherits from InputStream,
they can be used in all situations where an input stream is expected. But each also holds
as a component an underlying input stream, used to read the actual character values. One
244 CHAPTER 14. INPUT AND OUTPUT STREAMS

way to envision these classes is as an adapter, or lter, that sits between the client (the code
making the request for values) and the physical input stream producing the values.
client
?read ()
lter
?read ()
InputStream

Because they support the interface common to all input streams, such lters can be
easily added or removed as needed.
Perhaps the easiest lter to understand is the class LineNumberInputStream. The under-
lying input stream must be given as an argument to the constructor when an instance of
this class is created. The lter simply scans the input for newline characters as values are
read. Each time a newline character is found a counter is incremented. The value of this
counter can be retrieved by the method getLineNumber ().
The PushbackInputStream allows a single character to be unread. A value pushed back
into the input stream will subsequently be returned as the result of the next read() operation.
This facility is useful when scanning textual input. Imagine, for example, reading the textual
representation of a numeric value, such as 456. It is only after the rst non-digit character
is read that one knows that all of the digits of the number have been seen. But the last
character, the non-digit, should not be considered part of the number. Thus, this value is
\pushed back" into the input, to be read again later.
The PushbackInputStream only allows a single character to be reprocessed. The Buered-
InputStream is a more general facility, allowing the input operations to be backed up over a
larger range. The method mark() tells the stream to begin saving the values of characters
as they are read. A subsequent reset() then resets the input back to the marked location.
All characters between the mark and the reset will then be read again. The standard input
stream System.in is an example of a buered input stream. On most platforms, characters
cannot be read from this input stream until an entire line of text has been entered.
The input lter that adds the most new functionality is the class DataInputStream. In
this class, methods are provide to read the binary representation all of the primitive data
types:
class DataInputStream extends FilterInputStream implements DataInput f
...
public boolean readBoolean () throws IOException

public byte readByte () throws IOException

public char readChar () throws IOException


14.1. INPUT STREAMS 245

public double readDouble () throws IOException

public int readInt () throws IOException

public int readLong () throws IOException

public String readLine () throws IOException


g

Note that these methods read a binary representation, not a textual representation
of the values. Most generally, the input stream being processed was produced using a
DataOutputStream. However, DataInputStreams are also useful with purely textual les. In
particular, the method readLine () can be used when textual input must be processed in a
line by line fashion. An example was shown in Section 20.7.1. The DataInput interface is
also used by the class RandomAccessFile.

14.1.3 Stream Tokenizer


Although not specically an InputStream, the class StreamTokenizer provides a useful mecha-
nism for breaking a textual le into a sequence of tokens. The stream tokenenizer recognizes
words (sequences of letters separated by nonletters characters), numbers (sequences of digit
characters), and can even be set up to recognize comments or convert all tokens to lower
case. Each token returned is characterized by a token type, which is either one of several
symbol constants dened in the class, or the integer value of the last character read. The
following program illustrates several of the methods provided by this class:
class TokenTest f
public static void main (String  ] args) f
StreamTokenizer tok = new StreamTokenizer (System.in)
try f
while (tok.nextToken() != tok.TT EOF) f
switch (tok.ttype) f // ttype is token type
case tok.TT NUMBER: // nval is numeric value
System.out.println("number " + tok.nval)
break
case tok.TT EOL:
System.out.println("end of line")
break
case tok.TT WORD: // sval is text value
System.out.println("word " + tok.sval)
break
246 CHAPTER 14. INPUT AND OUTPUT STREAMS
OutputStream

ByteArray File Filter Piped


OutputStream OutputStream OutputStream OutputStream

Data Buered PrintStream


OutputStream OutputStream

Figure 14.2: Subclasses of output stream

default:
System.out.println("token " + (char) tok.ttype)
break
g
g
g catch (IOException e) f g
g

Giving this program the input \23-skido, kid!" yields the output:
number 23.0
token -
word skido
token ,
word kid
token !

14.2 Output Streams


Although not as extensive as the input stream classes, but subclasses of OutputStream
(Figure 14.2) also exhibit the same interesting use of polymorphism and inheritance. Like
InputStream, the abstract parent class OutputStream denes minimal functionality:
14.2. OUTPUT STREAMS 247

abstract class OutputStream f


// write a single byte value:w
pubic abstract void write (int b) throws IOException

// write an array of byte values


public void write (byte  ] buffer) throws IOException

// ush all output from buers


public void flush () throws IOException

// close stream
public void close () throws IOException
g

As we did with input streams, we can divide the description of output streams into two
major categories, those classes that characterize the physical location of the output, and
those classes that add additional behavior to an output stream.
In the rst group are the three classes ByteArrayOutputStream, FileOutputStream, and
PipedOutputStream. The rst writes values into an in-memory byte array, the second on to
an external le, and the third to a pipe (see Section 14.3).
The second category is represented by the class FilterOutputStream and its subclasses.
Just as FilterInputStream provided an orthogonal way of adding additional functionality to an
input stream regardless of the physical source of the data, a ltered output stream adds new
functionality to an output operation. Whereas a ltered input stream generally performs
new operations after reading from the underlying stream, a ltered output stream generally
performs some task before sending the (perhaps transformed) values to the underlying output
stream. There are three subclasses of FilterOutputStream. These are BueredOutputStream,
DataOutputStream and PrintStream.
A BueredOutputStream maintains an internal buer of values that have been output.
Rather than writing bytes one by one, values are written to the underlying output stream
only when the buer becomes full or when the output is ushed. This is useful if there
is a high overhead involved in writing an individual byte to the output stream, and this
overhead can be amortized if many characters are written at once.
A DataOutputStream is the output equivalent of DataInputStream. This class adds meth-
ods to write the binary values for each of the primitive data types. The output will be
subsequently read by a DataInputStream. A program in Section 14.3 will illustrate the use
of a DataOutputStream.
A PrintStream is similar, but generates a textual representation, rather than a binary
representation. The methods print() and println() are overloaded with functions specic
to each of the primitive data types. Typically, a print stream is used to generate output
that will be read by human users, as opposed to processing by another program. Both the
streams System.out and System.err are instances of PrintStream.
248 CHAPTER 14. INPUT AND OUTPUT STREAMS

class PrintStream extends FilterOutputStream f


...
// print textual representation of primitive value
public void print (boolean bool)
public void print (int inum)
public void print (float fnum)
public void print (double dnum)
public void print (String str)
public void print (Object obj) f print (obj.toString()) g
...
g

The implementation of the method print() when used with an object as argument is
an example of parameteric polymorphism combined with an abstract, or deferred method.
Because all objects are subclasses of Object, any value can be used as argument with this
method. When executed, the procedure uses the function toString() to convert the object
into a string representation. The implementation of this method in class Object simply
returns the name of the class of the receiver. However, this method is overridden in many
classes to provide more meaningful output. The function executed will be determined by
the dynamic, run-time type of the argument, not by the static type Object. In this fashion,
whatever text the programmer has provided using the toString() method will be the output
produced.

14.3 Piped Input and Output


In some types of applications a common situation is for one portion of a program to be
producing values which are being consumed by a dierent portion of the same program.
Such an arrangement is called a producer/consumer relationship. Java provides an elegant
way of organizing this type of program, through the use of multiple threads of execution
and pipes. The producer and consumer each run in their own thread of execution, and
communicate through a pipe.
A pipe is a buered data area, which is used for both reading and writing. A pipe can
hold only a limited number of values. Either reading from or writing to a pipe can cause a
thread to be temporarily suspended. A write will be suspended if the current pipe buer is
full, while a read operation will be suspended if the buer is empty. In both cases, execution
will continue when the condition is resolved, for example when a subsequent read frees up
space in the buer, or a subsequent write adds a new element.
Each pipe is manifest by a matched pair of stream pointers, an PipedOutputStream and
a PipedInputStream. The second value created (either input or output pipe) is passed the
rst value as argument, and the connection is thereby made between the two:
14.3. PIPED INPUT AND OUTPUT 249

PipedInputStream in = new PipedInputStream()


PipedOutputStream out = new PipedOutputStream (in)

Values can subsequently be written to the piped output stream as if it were any other type
of output stream, and these values can then be read, in the same order they were inserted,
from the corresponding input stream.
We can illustrate the use of pipes by means of a program designed to nd all the integers
smaller than 100,000 that are both prime numbers and Fibonocci numbers. A prime number,
you will recall, is a value with no divisors other than 1 and itself. A Fibonocci number 1 is
dened by the recurrence relation f0 = 0, f1 = 1, fn = fn;2 + fn;1 .
A separate thread of control (see Chapter 21) is created to generate both sequences of
numbers. The thread, in fact, need not even know that it is dealing with a pipe. The
Fibonocci thread simply creates a sequence of values and writes them to an output stream.
Using a DataOutputStream makes it easier to write integer values. Although it looks as if
the thread produces all the values at once, a print() statement placed inside the loop will
demonstrate that this is not so, and that production of values will be delayed until they are
required.
class FibMaker extends Thread f
private DataOutputStream out

public FibMaker (DataOutputStream o) f out = o g

public void run () f


int n = 0
int m = 1
try f
out.writeInt (m)
while (m < 100000) f
int newValue = n + m
n = m
m = newValue
System.out.println("writing new Fibonocci " + newValue)
out.writeInt (newValue)
g
out.close()
1 Tradition has it that Fibonocci numbers describe the population growth of rabbits. Rabbits take two
years to mature. Once mature, they give birth each year to a single o spring. Thus, each new year the
number of rabbits is the number of rabbits in the previous year plus the number of mature rabbits in the
previous year, each of which has given birth. Hardly realistic, but the sequence of numbers produced by
this relation has some fascinating properties.
250 CHAPTER 14. INPUT AND OUTPUT STREAMS

g catch (IOException e) f return g


g
g

A similar thread creates prime numbers:


class PrimeMaker extends Thread f
private DataOutputStream out

public PrimeMaker (DataOutputStream o) f out = o g

public void run () f


int newValue = 1
try f
while (newValue < 100000) f
newValue = newValue + 1
boolean isPrime = true
for (int i = 2 i  i <= newValue i++)
if (newValue % i == 0) f
isPrime = false
break // no use checking further
g
if (isPrime) f
System.out.println("writing new prime " + newValue)
out.writeInt (newValue)
g
g
out.close()
g catch (IOException e) f return g
g
g

The main program shows how these are connected. The thread for each generator, as
well as the pipes, are created in the methods makeFibs() and makePrimes(). Note how all the
pipe mechanism is encapsulated in these two routines, and the remainder of the program
simply views input as coming from a DataInputStream. The main program simply reads
values as long as they are available, comparing them and outputting those that match.
class PipeTest f
static public void main (String  ] args)
f PipeTest world = new PipeTest(System.out) g
14.3. PIPED INPUT AND OUTPUT 251

private PipeTest (PrintStream out) f


DataInputStream fibs = makeFibs()
DataInputStream primes = makePrimes()
try f
int x = fibs.readInt()
int y = primes.readInt()
while (x < 100000) f
if (x == y) f
out.println ("integer " + x + " is both fib and prime")
x = fibs.readInt()
g
else if (x < y)
x = fibs.readInt()
else
y = primes.readInt()
g
g catch (IOException e) f System.exit(0) g
g

private DataInputStream makeFibs () f


try f // create the Fibonocci number generator
PipedInputStream in = new PipedInputStream()
PipedOutputStream out = new PipedOutputStream (in)
Thread fibThread = new FibMaker (new DataOutputStream(out))
fibThread.start()
return new DataInputStream (in)
g catch (IOException e) f return null g
g

private DataInputStream makePrimes () f


try f // create the prime number generator
PipedInputStream in = new PipedInputStream()
PipedOutputStream out = new PipedOutputStream (in)
Thread primeThread = new PrimeMaker (new DataOutputStream(out))
primeThread.start()
return new DataInputStream (in)
g catch (IOException e) f return null g
g

g
252 CHAPTER 14. INPUT AND OUTPUT STREAMS

An examination of the output will show that values are being generated on demand,
rather than being all computed at once.
writing new Fibonocci 1
writing new Fibonocci 2
writing new prime 2
writing new prime 3
writing new prime 5
writing new prime 7
writing new Fibonocci 3
writing new Fibonocci 5
writing new prime 11
writing new prime 13
writing new prime 17
writing new Fibonocci 8
writing new Fibonocci 13
writing new Fibonocci 21
integer 2 is both fib and prime
integer 3 is both fib and prime
integer 5 is both fib and prime
integer 13 is both fib and prime
writing new Fibonocci 34
...
writing new prime 19
writing new prime 23
...
writing new prime 31
writing new Fibonocci 233
...
integer 89 is both fib and prime
...
integer 233 is both fib and prime

14.4 Chapter Summary


Although the number of dierent classes can initially make the input/output facilities of
Java seem confusing, the structure of these classes is very simple and a good illustration of
the use of inheritance and polymorphism.
Input streams can be divided into those based on a physical input source (reading in-
put from a le, for example), and those based on adding new functionality to a logical
14.4. CHAPTER SUMMARY 253

input stream. An example of the latter is the class LineNumberInputStream, which adds the
ability to count line numbers to an existing input stream. The design of the subclasses of
FilterInputStream combine both the techniques of inheritance and composition.
Although not specically an input stream, the class StreamTokenizer provides a way to
break a stream into individual tokens. A similar facility for strings will be described in
Chapter 18.
Like input streams, the various subclasses of output stream dier in the physical location
to which the output is directed.
Pipes provide a mechanism for structuring programs that include both the production
and consumption of a given resource. The producer writes values to the pipe, while the
consumer reads values. The pipe facility will automatically suspect either the producer or
the consumer tasks, if values are not available or the pipe buer becomes full.

Study Questions
1. Describe the functions that are common to all subclasses of InputStream.
2. What are the dierent types of physical locations from which an input stream can
read?
3. Describe out a FilterInputStream combines both inheritance and composition.
4. What new functionality is provided by the class LineNumberInputStream?
5. What task is performed by a StreamTokenizer?
6. Describe the dierent targets to which an output stream can write.
7. In what ways is a pipe dierent from a le? In what ways are they the same?

Exercises
1. Create a new subclass of FilterInputStream that reads values from an input stream and
coverts all upper case characters to lower case. How would you test your program?
2. Using DataInputStream.readLine() and a StringTokenizer, write an application that will
count and display the number of lines, words and characters in a le. The name of
the le should be taken from the argument list for the application.
3. Write the class description for SequenceOutputStream, which is the output analog to
SequenceInputStream. The constructor for this class will take two output streams as
argument. Each write to the SequenceOutputStream will thereafter be translated into
a write on each of the argument streams.
254 CHAPTER 14. INPUT AND OUTPUT STREAMS

4. Extend the class you developed in the preceding exercise so that the constructor can
take an enumeration of output streams as argument.
5. Write a subclass of FilterInputStream that looks for positive integer digit characters,
such as \4231", and replaces them with the textual equivalent, in this example the
words \four thousand two hundred and thirty one". Do this in several steps:
(a) Convert the input stream into a PushbackInputStream.
(b) As long as the source input stream is not a digit character, return it.
(c) When a digit character is encountered, read the number until the end of digit is
found.
(d) Push the terminating non-digit character back into the input.
(e) Translate the number into its textual equivalent, stored in a String. Create a
StringBuerInputStream to read characters from this string.
(f) For subsequent requests, read from the string buer input stream until no fur-
ther characters remain, then revert to reading from the original push back input
stream.
Chapter 15

Design Patterns
Like most complex structures, good computer programs are often formed by imitating the
structure of older, similar programs that have already proven successful. The concept of a
design pattern is an attempt to capture and formalize this process of imitation. The basic
idea is to characterize the features of a proven solution to a small problem, summarizing the
essential elements and omitting the unnecessary detail. A catalog of design patterns is a
fascinating illustration of the myriad ways that software can be structured so as to address
dierent problems. Later, patterns can give insight into how to approach new problems that
are similar to those situations described by the pattern.
This chapter will introduce the idea of design patterns by describing several that are
found in the Java library. The terminology used in describing the patterns is adapted
from the book Design Patterns: Elements of Reusable Object-Oriented Software, by Erich
Gamma, Richard Helm, Ralph Johnson and John Vlissides Gamma 1995]. This was one of
the rst books to describe the concept of design patterns and provide a systematic cataloging
of patterns. Many more patterns than are described here can be found in this book, as well
as in the recent literature on design patterns.
The format used in describing each pattern is to rst characterize the problem the
pattern is addressing. Then, the essential features of the solution are summarized. In some
cases this is followed by a discussion that examines some of the context for the problem, or
contrasts alternative design possibilities. This is followed by a more detailed description of
the pattern as it is manifest in the Java Library. Finally, a sentence or two summarizes the
situations where the pattern is applicable.

15.1 Adapter
problem: How do you use an object that provides appropriate behavior but uses a dierent
interface than is required in some situation?
255
256 CHAPTER 15. DESIGN PATTERNS

solution: Dene an adapter class that acts as an intermediary. The adapter does little
work itself, but merely translates commands from one form into the other.
discussion: International travelers frequently overcome the problem of diering electrical
plug and voltage standards by using adapters for their appliances. These adapters
allow an electrical appliance that uses one type of plug to be modied so that it can
be used with a dierent type of plug. The software equivalent is similar. An adapter is
concerned mostly with changes in the interface to an object, and less with the actual
functionality being provided.

Client Adapter Worker

example: An example of adapters in the Java library are the \wrapper" classes, Boolean,
Integer, and so on. These adapt a primitive type (such as boolean or int) so that they
can be used in situations where an Object is required. For example, wrappers are
necessary to store primitive values as elements in a Vector.
Another form of adapter is the class MouseAdaptor used in the pin ball game described
in Chapter 7, as well as in the Solitare program presented in Chapter 9. Here the
adapter reduces the interface, by implementing default behavior for methods that are
unneeded in the current application. The client can therefore concentrate on the one
method that is used in the program.
An adapter can be used whenever there is the need for a change in interface, but no,
or very little, additional behavior beyond that provided by the worker.

15.2 Composition
problem: How do you permit the creation of complex objects using only simple parts?
solution: Provide a small collection of simple components, but also allow these components
to be nested arbitrarily. The resulting composite objects allow individual objects and
compositions of objects to be treated uniformly. Frequently, an interesting feature of
the composition pattern is the merging of the is-a relation with the has-a relation.
example: A good example of composition in the Java library is the creation of design
layouts through the interaction of Components and Containers. There are only ve
simple types of layouts provided by the standard library, and of these ve only two,
border layouts and grid layouts, are commonly used. Each item in a layout is a
Component. Composition occurs because Containers are also Components. A container
15.2. COMPOSITION 257

holds its own layout, which is again one of only a few simple varieties. Yet the container
is treated as a unit in the original layout.
The structure of a composite object is often described in a tree-like format. Consider,
for example, the layout of the window shown in Figure 13.8 of Chapter 13. At the
application level there are four elements to the layout. These are a text area, a simple
blank panel, and two panels that hold composite objects. One of these composite
panels holds three scroll bars, while the second is holding a grid of sixteen buttons.
Color = 40,60,50]

(
h
(((( Hhhhh
((((  HH hhhh

Color =

 PP  X
HX XXX

 PP
P   HH X
:::

By nesting panels one within another, arbitrarily complex layouts can be created.
Another example of composition is the class SequenceInputStream, which is used to
catenate two or more input streams so that they appear to be a single input source
(see Section 14.1.2). A SequenceInputStream is-a InputStream (meaning it extends the
class InputStream). But a SequenceInputStream also has-a InputStream as part of its
internal state. By combining inheritance and composition, the class permits multiple
sequences of input sources to be treated as a single unit.
This pattern is useful whenever it is necessary to build complex structures out of a few
simple elements. Note that the merging of the is-a and has-a relations is characteristic
of the wrapper pattern (Section 15.9), although wrappers can be constructed that are
not composites.
258 CHAPTER 15. DESIGN PATTERNS

15.3 Strategy
problem: How do you allow the algorithm that is used to solve a particular problem to be
easily and dynamically changed by the client?
solution: Dene a family of algorithms with a similar interface. Encapsulate each algo-
rithm, and let the client select the algorithm to be used in any situation.
discussion: If a complex algorithm is embedded in a larger application, it may be dicult
to extract the algorithm and replace it with another, alternative version. If several
alternative algorithms are included in the same object, both the complexity and the
code of the resulting object may be increased unnecessarily. By separating problem
and solution, it is easier for the client to select the solution (algorithm) appropriate
for any particular situation.
example: An example of the use of the Strategy pattern is the creation of layout managers
in the AWT. Rather than coding in the component library the details of how items
are laid out on the screen, these decisions are left to the layout manager. An interface
for LayoutManager is dened, and ve standard layout managers are provided. The
ambitious programmer is even allowed, should he or she choose, to dene a new object
that satises the LayoutManager interface.

holds
Container LayoutManager

inherits implements

Application GridLayout

The activities of the design component (such as a Panel or a Window) is independent


of the particular layout manager that is being used. This both simplies the container
component and permits a much greater degree of exibility in the structure of the
resulting layout than would be possible if layout decisions were an intrinsic part of the
container.
This pattern is useful whenever it is necessary to provide a set of alternative solutions
to a problem, and the algorithms used to address the problem can be encapsulated
with a simple interface.
15.4. OBSERVER 259

15.4 Observer
problem: How do you allow two or more independent and loosely coupled objects to change
in synchrony with each other?
solution: Maintain a list of objects that are tied, or dependent, on another object. When
the target object changes, the dependents are notied that they should update them-
selves.
discussion: It is easy to maintain tightly coupled objects in synchrony. For example, if a
new class is dened as a subclass of an existing parent class, modications of the parent
that are made via method invocations can be monitored by simply overriding the
methods. It is much more dicult to keep objects in step with each other when links
are formed and broken dynamically at run time, or when no obvious class relationship
exists between the separate elements.
example: There are two good examples of the use of the observer pattern in the Java
library. The rst we have seen in many earlier case studies, such as the cannon
world examined in Chapter 6. Each of the user interface components that permits
interaction, such as buttons, scroll bars, and check boxes, maintains a collection of
listener objects. This list is dynamic listeners for any component can be easily added
or removed at run time. Furthermore, the structure of the listeners is not specied,
they are only required to satisfy the necessary interface. When the component changes
state (the button is pressed, the slider moved, the Checkbox changed), each of the
listeners is notied that a change has occurred. It is up to the listener to decide what
action should be taken as a result of the change.
The idea behind listeners is also found in a more general facility that can be used
by programmers for situations that do not involve user interaction. The library class
Observable represents objects that can be \observed", the equivalent of the components
in the AWT mechanism. Programmers can either subclass a new class from Observable,
or simply create an Observable eld within a class. Other objects can implement the
Observer interface. These correspond to \listener" objects. An instance of Observer
registers itself with the object being observed.
At any time, the Observable object can indicate that it has changed, by invoking
the message notifyObservers(). An optional argument can be passed along with this
message. Each observer is passed the message update(Observable, Object), where the
rst argument is the Observable that has changed, and the second is the optional
argument provided by the notication. The observer takes whatever action is necessary
to bring the state into synchrony with the observed object.
The Observer pattern is applicable whenever two or more objects must be loosely
coupled, but must still maintain synchronization in some aspect of their behavior or
state.
260 CHAPTER 15. DESIGN PATTERNS

15.5 Flyweight
problem: How can one reduce the storage costs associated with a large number of objects
that have similar state?
solution: Share state in common with similar objects, thereby reducing the storage required
by any single object.
example: With the exception of primitive values, all objects in Java are an instance of
some class. With each class it is necessary to associate certain information. Examples
of information is the name of the class (a String), and the description of the interface
for the class. If this information was duplicated in each object the memory costs would
be prohibitive. Instead, this information is dened once by an object of type Class,
and each instance of the class points to this object.
The objects that share the information are known as yweights, since their memory
requirements are reduced (often dramatically) by moving part of their state to the
shared value. The yweight pattern can be used whenever there are a large number
of objects that share a signicant common internal state.

15.6 Abstract Factory


problem: How to provide a mechanism for creating instances of families of related objects
without specifying their concrete representations.
solution: Provide a method that returns a new value that is characterized only by an
interface or parent class, not by the actual type produced.
discussion: There are several instances where the value returned by a function in the
standard library is characterized by either an abstract class or an interface. Clearly
the actual value being returned is a dierent type, but normally the client using the
function is not concerned with the actual type, but only the behavior described by the
characterizing attributes.
example: Two examples out of the many found in the Java library will be described.
Each of the collection classes Vector, Hashtable and Dictionary dene a method named
elements() that is described as returning a value of type Enumeration. As Enumeration
is only an interface, not a class, the value returned is clearly formed as an instance of
some other class. Almost always, the client has no interest in the actual type being
yielded by elements(), and is only interested in the behavior common to all values that
satisfy the Enumeration interface.
A similar situation occurs with the classes Font and FontMetric. The class FontMetric
is used to describe the characteristics of a Font, such as the height and width of
15.7. FACTORY METHOD 261

characters, the distance characters extend above or below the baseline, and so on.
A FontMetric is an abstract class, one that cannot be instanciated directly by the
programmer using the new command. Instead, a value of type FontMetric is returned
by a Graphics object in response to the message getFontMetrics. Clearly, the graphics
object is returning a value derived from a subclass of FontMetric, but the particular
value returned is normally of no concern to the client.
A similar facility is used by class Applet, which can return an AppletContext that
describes the current execution environment.
The abstract factory pattern should be used whenever the type of the actual value
to be created cannot be predicted in advance, and therefore must be determined
dynamically.

15.7 Factory Method


problem: You have a method that returns a newly created object, but want subclasses to
have the ability to return dierent types of object.
solution: Allow the subclass to override the creation method and return a dierent type
of object.
discussion: This pattern is very similar to the abstract factory, only specialized for the
situation where new abstractions are formed using inheritance.
example: The method clone() is a good example of a factory method. This method returns
a copy of an object, provided the object supports the Cloneable interface. The default
method in class Object raises an exception, indicating that the cloneable interface is
not supported. Subclasses that wish to permit clones must override this method, and
return a dierent type of value.
Note that the value returned by a factory method must be the same for all classes.
For the Cloneable interface this type is Object. Any class that permits cloning will still
return a value of type Object in response to the message clone(). This value must then
be cast to the appropriate type.
The factory method pattern is useful when there is a hierarchy of abstractions formed
using inheritance, and part of the behavior of these abstractions is the creation of new
objects.

15.8 Iterator
problem: How to provide a way to access elements of an aggregate object sequentially
without exposing the underlying representation.
262 CHAPTER 15. DESIGN PATTERNS

solution: Provide a mediator object for the sole purpose of sequential access. This mediator
can be aware of the representation of the aggregate, however the client using the object
need not be aware of these details.
example: The Enumeration interface for container access actually addresses two related
problems. It provides a uniform means of accessing elements from many dierent
types of container, and it hides the details of the underlying container representation.
It is the second aspect that makes the Enumeration a good example of the iterator
pattern.
Consider, for example, an enumeration that is generating elements from a Hashtable.
Internally, a hash table is implemented as an array, each element of the array being a
list. Values that hash into the same locations are found on the same list.
0 - 23 - 31

1
2 - 72 - 9 - 5 - 12

3
4
5 - 25 - 1
6 - 3

The programmer who uses a hash table and wishes to iterate over the values should not
be concerned with the representation, such as moving from one list to the next when
the elements in one hash location have been exhausted. The hash table enumeration
hides these diculties behind a simple interface. The programmer sees only the two
methods hasMoreElements() and nextElement(). With these, a loop can be written that
does not even hint at the complex actions needed to access the underlying elements.
HashTable htab = new HashTable()
...
for (Enumeration e = htab.elements() e.hasMoreElements() ) f
Object val = e.nextElement()
...
g

The fact that the method elements returns a value that is not directly an Enumeration,
but is rather a value from another class that implements the Enumeration interface, is
an example of the Abstract Factory pattern (Section 15.6).
15.9. DECORATOR (FILTER OR WRAPPER) 263

The iterator pattern is useful whenever an aggregate object is created that can hold
an arbitrary number of values, and it is necessary to provide access to values without
exposing the underlying representation.

15.9 Decorator (Filter or Wrapper)


problem: How can you attach additional responsibilities to an object dynamically?
solution: By combining the is-a and has-a relations, create an object that wraps around
an existing value, adding new behavior without changing the interface.
discussion: Inheritance is one technique for providing new functionality to an existing
abstraction. But inheritance is rather heavy handed, and is often not exible enough to
accommodate situations that must dynamically change during the course of execution.
A decorator wraps around an existing object, and satises the same requirements (for
example, is subclassed from the same parent class or implements the same interface).
The wrapper delegates much of the responsibility to the original, but occasionally adds
new functionality.

Buered
InputStream

InputStream

example: The class InputStream provides a way to read bytes from an input device, such
as a le. The class BueredInputStream is a subclass of InputStream, adding the ability
to buer the input so that it can be reset to an earlier point and values can be reread
two or more times. Furthermore, a BueredInputStream can take an InputStream as
argument in its constructor.
Because a BueredInputStream both is an InputStream and has an input stream as
part of its data, it can be easily wrapped around an existing input stream. Due
to inheritance and substitutability, the BueredInputStream can be used where the
original InputStream was expected. Because it holds the original input stream, any
actions unrelated to the buering activities are simply passed on to the original stream.
264 CHAPTER 15. DESIGN PATTERNS

A decorator, or wrapper class, is often a exible alternative to the use of subclassing.


Functionality can be added or removed simply by adding or deleting wrappers around
an object.

15.10 Proxy
problem: How do you hide details such as transmission protocols to remote objects?
solution: Provide a proxy that acts as a surrogate or placeholder for another object.
discussion: The idea of a proxy is that one object is standing in place of another. The
rst object receives requests for the second, and generally forwards the requests to the
second, after processing them in some fashion.
example: An example proxy in the Java Library is the RMI, or Remote Method Invocation
system. The RMI is a mechanism that can be used to coordinate Java programs
running on two or more machines. Using the RMI, a proxy object is created that runs
on the same machine as the client. When the client invokes a method on the proxy,
the proxy transmits the method across the network to the server on another machine.
The server handles the request, then transmits the result back to the proxy. The proxy
hands the result back to the client. In this fashion, the details of transmission over
the network are handled by the proxy and the server, and are hidden from the client.

Client Proxy  Q Server


Q 

15.11 Bridge
problem: How to decouple an abstraction from its implementation so that the latter can
vary independently.
solution: Remove implementation details from the abstraction, placing them instead in an
object that is held as a component in the abstraction.
example: Most of the component classes in the AWT make use of the bridge pattern. Fun-
damentally, this is because the actions necessary to implement a graphical component
vary in great detail from one platform to another. For example, the actions needed
to display a window are dierent depending upon whether the underlying display
is X-Windows/Motif, Windows-95, or the Macintosh. Rather than placing platform
specic details in the class Window, instead each window maintains a component of
15.12. CHAPTER SUMMARY 265

type WindowPeer. The interface WindowPeer has dierent implementations, depend-


ing upon the platform on which the application is being executed. This separation
allows a Java program that depends only on the class Window to be executed in any
environment for which there is a corresponding peer.
The Bridge pattern is in many ways similar to the Strategy pattern described earlier.
Dierences are that bridges are almost always hidden from the client (for example, the
average Java programmer is generally unaware of the existence of the peer classes), and
are generally dictated by environmental issues rather than re ecting design decisions.

15.12 Chapter Summary


An emerging new area of study in object-oriented languages is the concept of design patterns.
A design pattern captures the salient characteristics of a solution to a commonly observed
problem, hiding details that are particular to any one situation. By examining design
patterns, programmers learn about techniques that have proven to be useful in previous
problems, and are therefore likely to be useful in new situations.

Further Reading
The most important reference for design patterns is the book of the same name Gamma 1995],
by Gamma, Helm, Johnson and Vlissides (commonly known as the Gang of Four, or GOF).
Another recent book on patterns is by Richard Gabriel Gabriel 1996].

Study Questions
1. In what ways is an adapter similar to a proxy? In what ways are they dierent?
2. In what way is the composition design pattern similar to the idea of composition
examined in Chapter 10?
3. In what ways is a strategy similar to a bridge? In what ways are they dierent?
4. In what ways is an iterator similar to an adapter?

Exercises
1. What design pattern is exhibited by the class PrintStream (see Section 14.2)? Explain
your answer.
266 CHAPTER 15. DESIGN PATTERNS
Part V

Understanding the Java World

267
Chapter 16

Exception Handling
From the start, Java was designed with the understanding that errors occur, that unexpected
events happen, and that programmers should always be prepared for the worst. Part of this
outlook is the inclusion in the language of a simple yet powerful mechanism for handling
exceptions.
An exception is an event that occurs during the execution of a program that disrupts
the ow of instructions, and prevents the program from continuing along its normal course.
An example that is easy to understand is the exception that occurs when a le cannot be
opened. The programmer developing a le processing application starts out with a structure
that perhaps looks something like the following:
ask the user for the name of a file
open the file
read the contents
do something with the contents
close the file

The programmer probably develops the application using some simple test cases, and
might not even think about the possibility that the le cannot be opened. What happens
if the user enters incorrect values, a name that is not a valid le name? In most languages,
the likely answer is that the program will fail in totally unexpected and inexplicable ways.
In Java, the programmer cannot simply forget to think about this possibility, because
the language will not allow the programmer to write a statement that opens a le without
providing the code that will be executed should a failure occur. The le opening method is
declared as a function that can potentially throw an exception, and the compiler will refuse
to recognize any use of the method that does not handle the exception.
As we saw earlier in the case study described in Chapter 7, a method that can potentially
raise an exception must be invoked within the framework of a try/catch block. This is
269
270 CHAPTER 16. EXCEPTION HANDLING

written as a nested statement following the keyword try, and an associated set of statements
following the keyword catch:
try f
File fd = new File(filename)
g catch (fileOpenFailed e)
f System.err.println("Cannot open file " + filename) g

The exception mechanism is in fact dealing with an actual object, which is an instance
of class Throwable. When an error occurs, the value of this object is assigned to the variable
e in the statements shown above.
Multiple statements can be nested within the try block. In fact, often almost the entire
application will be held within a surrounding block.
try f
File fd = new File(filename)
processFile (fd)
fd.close()
g catch (fileOpenFailed e)
f System.err.println("Cannot open file " + filename) g

Multiple exceptions can also be tested, by writing a series of catch clauses


try f
File fd = new File(filename)
processFile (fd)
fd.close()
g catch (FileNotFoundException e)
f System.err.println("Cannot find file " + filename) g
catch (FileOpenFailed e)
f System.err.println("Cannot open file " + filename) g

Exceptions are a useful programming construct because the location where an error is
detected is usually not the place where the appropriate solution is known. For example,
suppose you are the programmer developing the le processing library routines. That is,
you are the person developing the code for the class File. Certainly you are aware of the
fact that the string passed to the constructor for your class might not represent a valid le
name. But what should you do in this situation? Without knowledge of the surrounding
application, there is no good answer.
One common solution is to return a special value, such as the value null, is a command
cannot be processed. But constructors are not permitted to return a null value, furthermore
there might be multiple reasons for a failure. Does a null value returned by a le open
16.1. INFORMATION TRANSMITTED TO THE CATCH BLOCK 271

operation mean the le does not exist, or that it exists but cannot be read? Finally, what
if a null value is perfectly legal? How would one indicate an error in that case?
The exception mechanism not only allows the programmer who detects the message the
ability to return precise information, it also places responsibility for dealing with the error
on the shoulders of the programmer who knows the appropriate action to take.

16.1 Information Transmitted to the Catch Block


You will note that a catch block looks something like a procedure declaration. Like a
procedure, the catch block denes a variable that is given a value when the exception is
handled. Each of the various exceptional conditions is in fact a class name, a subclass of
Throwable. The variable that is thrown often contains useful information pertaining to the
exception, such as the name of the le that cannot be opened, or the value of an illegal
array index.

16.2 Catching Multiple Errors


There need not be a one-to-one correspondence between methods that can throw an excep-
tion and the try block that handles the error. The Java language only requires that the
statement that can throw the exception must be somewhere handled. For example, if two
or more les are being opened, they can be surrounded by a single try/catch statement. An
error opening either le will be handled in the same way.
try f
File fileOne = new File (filenameOne)
File fileTwo = new File (filenameTwo)
processFile (fileOne, fileTwo)
fileOne.close()
fileTwo.close()
g catch (FileNotFoundException e)
f System.err.println("Cannot find file " + e) g
g catch (FileOpenFailed e)
f System.err.println("Cannot open file " + e) g

16.3 Exceptions Thrown in the Standard Library


Exceptions come in several varieties, which are all subclasses of the class Throwable. The
two major subclasses of Throwable are Error and Exception.
272 CHAPTER 16. EXCEPTION HANDLING

Errors represent \hard" failures, such as the virtual machine detecting an error in the
bytecode representation. Processing will immediately halt once such an error is detected.
Fortunately, it is unlikely that a typical Java program will ever see or throw such an error.
Most programmers will write code to throw and catch objects that derive from the class
Exception. Once again, these can be divided into two categories, RuntimeException and all
others.
The subclasses of RuntimeException represent conditions that arise within the Java vir-
tual machine itself during the processing of the bytecodes that represent a program. Ex-
amples include the use of a variable that has not been initialized (NullPointerException), an
improper cast conversion, or an arithmetic operation that over ows. Because the source for
such an error could potentially be almost any Java statement, the compiler does not insist
that programmer test for or catch these errors. (Think about what a program would look
like if every variable reference needed to be surrounded by a try statement to detect null
pointer possibilities).

16.4 Throwing Exceptions


A method that will in some situations throw an exception must declare so in the method
heading. For example, suppose one is developing a stack abstraction (see Chapter 20) making
use of an array. Popping from an empty stack might be a condition that would trigger an
exception. To throw an exception, a new value is created (using the new operator). This
value must be a subclass of Throwable.
class Stack f
private int index
private Vector values
...

Object pop() throws Exception f


if (index < 0)
throw new Exception("pop on empty stack")
Object result = values.elementAt(index)
index--
return result
g
g

It is possible to convey more precise information by rst creating a subclass of Exception,


then throwing an instance of this class. The class Exception has two constructor forms, and
subclasses generally follow the same pattern.
16.5. PASSING ON EXCEPTIONS 273

class StackUnderflowException extends Exception f


StackUnderflowException () f super() g
StackUnderflowException (String gripe) f super(gripe) g
g

class Stack f
private int index
private Vector values
...

Object pop() throws StackUnderflowException f


if (index < 0)
throw new StackUnderflowException()
Object result = values.elementAt(index)
index--
return result
g
g

16.5 Passing On Exceptions


Occasionally it is useful for a method to not handle an exceptional condition, and to simply
pass the exception back to the caller. This can be accomplished by simply adding the
exception type to the method header. An example occurs in the concordance program
examined in Chapter 20.
class Concordance f

public void readLines (DataInputStream input) throws IOException f


String delims = " \t\n.,!?:"
for (int line = 1 true line++ ) f
String text = input.readLine()
if (text == null) return
text = text.toLowerCase()
Enumeration e = new StringTokenizer(text, delims)
while (e.hasMoreElements())
enterWord ((String) e.nextElement(), new Integer(line))
g
g
...
274 CHAPTER 16. EXCEPTION HANDLING

Here the IOException could be thrown by the method readLine. However, this method
really had no better way of dealing with this particular error than did the readLine routine
itself. Thus, the error is simply passed back to the caller of readLines, who could take the
appropriate action.

16.6 Chapter Summary


The exception handling mechanism in Java is a powerful technique for increasing the relia-
bility and robustness of Java programs. Exceptions handle unexpected conditions that can
disrupt the normal ow of control in the execution of a program. By properly declaring
and catching exception, programmers are provided with a way to gracefully recover from
potentially error producing situations.
Chapter 17

Utility Classes
The Java library provides a number of small utility classes that are useful in a wide variety
of situations. This chapter will discuss several of these, including Point, Dimension, Date,
Math, Random, Toolkit, and the data eld named System.

17.1 Point
A Point represents a location in two-dimensional space. The point is described by a pair of
integer values, called the x and y values. Points are used in conjunction with a number of the
AWT painting operations and with layout managers. An important feature to remember is
that in the AWT coordinate system, the y coordinates increase as locations move downward,
rather than decreasing as is true in classical geometry.
The following summarizes the characteristics of the class Point
class Point f
// constructor
public Point (int x, int y)

// public accessible data elds


public int x
public int y

// operations
public void move (int x, int y) // move to given location
public void translate (int x, int y) // change by oset
public boolean equals (Object p) // compare two points
public String toString () // convert to string

275
276 CHAPTER 17. UTILITY CLASSES

17.2 Dimension
A dimension is used to represent a rectangular size. Dimensions are characterized by a width
and a height. Dimensions are used in many AWT methods, and returned as the result of
methods that need to characterize a size. The following summarizes the features of the class
Dimension:
class Dimension f
// constructors
public Dimension ()
public Dimension (int w, int h)
public Dimension (Dimension d) // make copy
// public accessible data elds
public int width
public int height

// operations
public String toString()
g

17.3 Date
The class Date is used to represent both data and time values. Two dates can be compared
to determine if one comes after the other. Once set, any of the elds in a date can be
changed. The following summarizes the methods provided by the class Date:
class Date f
// constructors
public Date () // return current date and time
public Date (int year, int month, int day)
public Date (int year, int month, int day, int hours, int minutes)
public Date (int year, int month, int day,
int hours, int minutes, int seconds)

// eld access methods


17.3. DATE 277

pubic int getDate () // returns day of month, 1-31


public int getDay () // returns day of week, 0-6
public int getHours () // returns hour, 0-23
public int getMinutes () // returns minute of hour, 0-59
public int getMonth () // returns month of year, 0-11
public int getSeconds () // returns second of minute 0-59
public int getYear () // returns year number - 1900
public int getTimesoneOffset () // returns oset from GMT

// epoch methods
public long getTime () // returns milliseconds since epoch
public void setTime (long lval) // set time from epoch

// eld setting methods


public void setDate (int date)
public void setHours (int hour)
public void setMinuites (int minutes)
public void setMonth (int month)
public void setSeconds (int seconds)
public void setYear ()

// comparison methods
public boolean after (Date day)
public boolean before (Date day)
public boolean equals (Object day)

// output
public String toString ()
g

When provided with no constructors, the class Date returns the current time and day.
Otherwise, the date is set from the argument values given. The year value is given as the
year minus 1900, that is, 97 represents 1997, and 112 represents 2012.
Notice that the method equals overrides the similarly named method inherited from class
Object. Thus, the argument to this method must be simply Object. However, the methods
after and before, which are dened here for the rst time, can be restricted to working only
with Date objects.

17.3.1 After the Epoch


Recent dates and times can be compactly represented by a single long value that represents
the number of milliseconds since January 1, 1970, which is termed the start of the epoch.
278 CHAPTER 17. UTILITY CLASSES

This quantity is returned by the method getTime. Most often these values are used in pairs,
to obtain the amount of time used to perform a certain operation:
Date start = new Date() // starting time
int j = 0
for (int i = 0 i < 100000 i++)
j = j + 1

Date end = new Date() // ending time


System.out.println("That took " + (end.getTime() - start.getTime())
+ " milliseconds")

Epoch values for dates prior to January 1, 1970 are returned as negative numbers.

17.4 Math
The class Math provides a number of constants, as well as useful mathematical functions.
All values and functions are declared as static. Thus, these elements are accessible without
creating an instance of the class. The class can be summarized as follows:
final class Math f
// constants
public static final double E // 2.71828 ...
public static final double PI // 3.1415926 ...
// trigonometric operations, angles in radians
public static double sin (double num)
public static double cos (double num)
public static double tan (double num)
public static double asin (double num)
public static double acos (double num)
public static double atan (double num)
public static double atan2 (double s, double c)

// rounding operations
public static double ceil (double num)
public static double floor (double num)
public static double rint (double num)
public static int round (float num)
public static long round (double num)
17.5. RANDOM 279

// exponential and powers


public static double exp (double y) // e raised to x
public static double pow (double x, double y) // x raised to y
public static double log (double x) // log base e
public static double sqrt (double x) // x raised to 1/2

// other operations
public static int abs (int num)
public static long abs (long num)
public static float abs (float num)
public static double abs (double num)
public static int max (int x, int y)
public static float max (float x, float y)
public static double max (double x, double y)
public static int min (int x, int y)
public static float min (float x, float y)
public static double min (double x, double y)
public static double random () // value between 0 and 1
g

The method random() returns a value that is larger than or equal to 0.0, and strictly
smaller than 1.0, uniformly distributed over the range. The following, for example, could
be used to return a random number between 1 and 10 (inclusive of both endpoints):
int val = (int) Math.floor(Math.random()  10 + 1)

More extensive random number operations are provided by class Random.

17.5 Random
The function Math.random() can be used to generate random oating point values larger
than or equal to 0.0 and smaller than 1.0 with a uniform distribution. The class Random
provides more general facilities, allowing not only the generation of random integers, but
also the ability to reset the random number generator with a seed value. This latter feature
provides a way to recreate the same random sequence of values many times, a property that
is often useful in testing programs, as well as other situations. The facilities provided by
Random can be summarized as follows:
class Random f
// constructors
280 CHAPTER 17. UTILITY CLASSES

public Random ()
public Random (long seed)

// operations
public void setSeed (long seed)
public double nextDouble ()
public float nextFloat ()

// integer value, can be either positive or negative


public int nextInt ()
public long nextLong ()

// alternative distribution
public double nextGaussian ()
g

All methods use a uniform distribution, with the exception of nextGaussian, which uses
a Gaussian distribution. Other distributions can often be constructed from these. The
following function, for example, takes as argument an integer array of weights, and computes
a random number with weighted distribution. It sums the array of weights, computes a
random integer between 0 and the sum, then locates the integer in the array of weights:
static public int weightedDistribution (int  ] weights) f
int sum = 0 // compute sum of weights
for (int i = 0 i < weights.length i++)
sum += weightsi]
// compute random value less than sum
int val = (int) Math.floor(Math.random()  sum + 1)
// nd point in distribution
for (int i = 0 i < weights.length i++) f
val -= weightsi]
if (val < 0)
return i
g
return 0 // should never happen
g

Given an array of weights (1 3 2), for example, the value 0 would be returned 1/6 of the
time, the value 1 returned 1/2 of the time, and the value 2 returned 1/3 of the time.
17.6. TOOLKIT 281

17.6 Toolkit
The class Toolkit is mostly used to create the peer objects used in providing the device
independent aspects of the AWT (see Section 15.11). However, in addition to creating the
windows, buttons, menus and other features of a graphical interface, the class also provides
a few utilities useful to the programmer.
Toolkit is an abstract class, specialized for each type of platform on which a Java program
can be executed. The implementation of Toolkit appropriate to the current environment is
found by executing the method Toolkit.getDefaultToolkit().
The method getFontList() returns an array of string values, containing the names of the
fonts available on the current system.
The method getImage() takes as argument either a string or a URL (see Section 22.4.1).
If a string argument is used it should contain the name of a le. The image is loaded from
the le or the URL address, and returned as a value of type Image.
The methods getScreenSize() and getScreenResolution() together return the size of the
screen. The rst returns the number of pixels in the screen, both hight and width in a value
of type Dimension. The second returns the number of dots-per-inch. Dividing one by the
other will yield the physical size of the screen.

17.7 System
The class System, and the instance data eld of the same name provided by class Frame,
give access to several system-wide resources. The most commonly used values are the input
and output streams for the standard input, standard output and error output. These are
found at System.in, System.out and System.err, respectively.
The function System.exit(int) immediately halts the currently running program, yielding
to the operating system the integer status value given in the argument.
The method currentTimeMillis() returns the current time in milliseconds. The value is
returned as a signed long value, representing the number of milliseconds since January 1,
1970 (see Section 17.3.1). Since the value is returned an a long, over ow will occur sometime
in the year 292280995.
282 CHAPTER 17. UTILITY CLASSES
Chapter 18

Strings and related classes


The handling of strings in Java is similar enough to the handling of strings in other languages
to seem natural to the programmer learning the language, yet sucient dierent in subtle
points to be a potential source of trouble. In this chapter we will describe strings, string
buers, string tokenizers, and related classes.
The most important fact to remember is that in Java a String is an immutable value,
it cannot be changed. One should think of a string as a constant, like a double precision
value. Just as a variable declared as holding a double can be assigned a new value, a variable
assigned as holding a String can be assigned a new string, but this is not the same as changing
the string value. An second class, StringBuer, is closer, for example, to the C concept of
a string as simply an array of character values. A string buer can be subscripted, and
individual elements modied. The following shows, for example, how to change hope into
cope in both C and in Java.
C version Java Version
char * str = "hope" String str = "hope"
str0] = 'c' StringBuer strbuf(str)
strbuf.setCharAt(0, 'c')
should come up with a better example

18.1 Operations on Strings


The most common way to create a string is with a literal:
String name = "John Smith"

The various constructors for string also allow a string to be created from another string,
from an array of characters or bytes, or from a string buer:
283
284 CHAPTER 18. STRINGS AND RELATED CLASSES

char data] = f q , e , d g
String quod = new String(data)
// quod erat demonstrandum

The addition operator, +, is overloaded with a new meaning when either the left or the
right argument is a string. In this case, the non-string argument (if any) is converted into a
string and a string catenation is performed. This is most often used to produce formatted
output:
System.out.println(" The answer is: " + answer)

Note that the addition operator groups left to right, so that the meaning of the addition
in the following two expressions is very dierent:
System.out.println("Catch-" + 2 + 2) // catch-22
System.out.println(2 + 2 + "warned") // be forewarned
The String class denes a number of methods for returning portions of a string, converting
values to a string, and comparing strings. These can be summarized as follows:
final class String f // declared nal so it cannot be subclassed
// constructors
public String (String src)
public String (char  ] charArray)
public String (byte  ] byteArray)
public String (StringBuffer buffer)

// methods for creating new strings


// catenate string with argument string
public String concat (String str)
// replace old characters with new
public String replace (char oldChar, char newChar)
// return subportion of string
public String substring (int offset)
public String substring (int offset, int endIndex)
// covert case of all letters
public String toLowerCase ()
public String toUpperCase ()
// return reference of current string
public String toString ()
18.1. OPERATIONS ON STRINGS 285

// trim leading and trailing whitespace


public String trim ()
// create string from primitive data type
public static String valueOf (boolean bool)
public static String valueOf (char ch)
public static String valueOf (char  ] charArray)
public static String valueOf (int i)
public static String valueOf (long l)
public static String valueOf (float f)
public static String valueOf (double d)
public static String valueOf (Object obj)

// comparison methods
// compare ordering, return negative, zero or positive
public int compareTo (String str)
// compare for equality
public boolean equals (String str)
public boolean equalsIgnoreCase (String str)
// test front or end of string
public boolean endsWith (String str)
public boolean startsWith (String str)
// nd rst occurrence of char or string
public int indexOf (char c)
public int indexOf (char c, int startingOffset)
public int indexOf (String str, int startingOffset)
public int lastIndexOf (char c)
public int lastIndexOf (char c, int startingOffset)
public int lastIndexOf (String str, int startingOffset)
g

There are a few subtle points that can trap the unwary programmer. Note that the
identity operator, ==, tests whether two variables refer to exactly the same value. This is
not the same as testing whether two string values have exactly the same character represen-
tation. In general, one should always use equals() to test the equality of objects, including
strings, and not use the == operator.
The method compareTo() returns an integer result. This value is negative if the current
string is lexicographically smaller than the argument, zero if they are equal, and positive if
the string is larger than the argument. The exact integer value returned is implementation
dependent, and should not be counted upon in any program.
The static method String.valueOf (Object obj) is a good example of a polymorphic func-
tion. The argument can be any type of object, including a null object. If it is not null, the
method toString is used to convert the object into a string value:
286 CHAPTER 18. STRINGS AND RELATED CLASSES

final class String f


...
public static String valueOf (Object obj) f
if (obj == null)
return "null"
else
return obj.toString()
g
...
g

The method toString is dened in class Object (where it returns the class name of the
receiver as a string), and redened in many classes. The value yielded by toString(), and
hence by valueOf(), will be whatever method is appropriate for the dynamic, run-time type
of the argument.
This property of the == operator can be used to demonstrate one subtle dierence
between creating a new string using the String constructor, and creating a string using the
valueOf operator. One case generates a copy of the original string, while the other simply
returns a reference to the original.
String one = "One"
String two = new String(one)
String three = String.valueOf(one)
System.out.println(" is one == two " + (one == two)) // returns false
System.out.println(" is one == three " + (one == three)) // returns true

18.2 String Buers


A StringBuer is similar to the C language concept of a string as an array of character
values. A string buer, like a vector, has a buer of positions that may be larger than the
number of character values it currently holds. This makes the representation useful when a
sequence of insertion operations must be performed on the same string value. The methods
provided by class StringBuer can be described as follows:
final class StringBuffer f
// constructors
public StringBuffer (int capacity) // initially null string
public StringBuffer (String str)
18.3. STRING TOKENIZERS 287

// methods that change contents, return reference to ourselves


public StringBuffer append (boolean bool)
... // and all other primitive data types
public StringBuffer insert (int offset, boolean bool)
... // and all other primitive data types
public StringBuffer reverse ()
public void setCharAt (int index, char c)

// methods to access values


public char charAt (int index)

// misc methods
public int length ()
public void setLength (int length)
public void ensureCapacity ()
public String toString ()
g

Note that the methods append, insert and reverse both change the current string buer
and return a reference to the updated string buer. This is dierent from the transformation
methods in class String, which leave the receiver string unchanged, but return a new string
in which the transformations have been applied.
The append operator is used internally by the Java compiler to implement the + operator.
An statement such as:
System.out.println("answer: " + answer)

is compiled as if were written as:


System.out.println(new StringBuffer("answer: ").append(answer).toString())

18.3 String Tokenizers


The class StringTokenizer is useful for breaking a string into a sequence of tokens. Tokens
are dened by a set of delimiter characters. Common delimiters includes spaces, tabs,
and punctuation such as periods or commas. The class StringTokenizer implements the
Enumeration protocol (see Section 20.2), and can therefore be manipulated in an enumeration
style loop.
The methods provided by StringTokenizer can be described as follows:
288 CHAPTER 18. STRINGS AND RELATED CLASSES

class StringTokenizer implements Enumeration f


// constructors
public StringTokenizer (String str)
public StringTokenizer (String str, String delims)
public StringTokenizer (String str, String delims, boolean returnDelims)

// enumeration protocol
public boolean hasMoreElements ()
public Object nextElement ()
public String nextToken ()

// return number of remaining tokens


public int countTokens ()
g

The enumeration protocol requires that the method nextElement() return a value of
type Object. In order to avoid the consequent casting of this value to a String, the equiv-
alent method nextToken() can be used instead. The concordance program described in
Section 20.7.1 illustrated the use of a string tokenizer in breaking a line into a sequence of
words:
class Concordance f

...
public void readLines (DataInputStream input) throws IOException f
String delims = " \t\n.,!?:"
for (int line = 1 true line++ ) f
String text = input.readLine()
if (text == null) return
text = text.toLowerCase()
Enumeration e = new StringTokenizer(text, delims)
while (e.hasMoreElements())
enterWord ((String) e.nextElement(), new Integer(line))
g
g
...
g

The class StreamTokenizer (Section 14.1.3) provides a similar facility to breaking an input
stream into tokens.
18.4. PARSING STRING VALUES 289

18.4 Parsing String Values


While a StringTokenizer provides one method for breaking a string into component parts, the
result is a sequence of string tokens. If the string tokens represent primitive data values, the
programmer requires a method to change the string representation into the primitive value.
This functionality is provided by the wrapper classes, Boolean, Integer, Double, Float, and
Long. One form of the constructor in each of these classes takes a String as argument, and
parses the string to ascertain the underlying value. The original value can then be obtained
from the wrapper class, as shown in the following example:
String dstr = "23.7"
Double dwrap = new Double(dstr) // parse the string
double dval = dwrap.doubleValue()

The wrapper classes also provide the reverse that is, a way to change a primitive value
into a string. Often there are a variety of dierent formats available. These facilities are
summarized in the following table:
class conversion from string conversion to string
Boolean new Boolean(str) toString()
Boolean.valueOf(Boolean bval)
Double new Double(str) toString()
Double.valueOf(double dval)
Float new Float(str) toString()
Float.valueOf(Float fval)
Integer new Integer(str) toString()
Integer.parseInt(String str, int radix) toBinaryString()
Integer.valueOf(int ival) toOcatalString()
toHexString()
Most of the conversion methods will throw a NumberFOrmatException if the characters
do not represent a value in the correct format.

Cross References
Study Questions
Exercises
290 CHAPTER 18. STRINGS AND RELATED CLASSES
Chapter 19

Understanding Graphics
The Java language provides one of the richest collections of graphical commands of any
general-purpose programming language. In this chapter we will explain some of the basic
aspects of the classes associated with graphical operations in Java. Topics discussed include
colors, fonts, images, and animation.

19.1 Color
The class Color is used to represent a color value. We know from optics that all colors can
be formed by combinations of red, green and blue. Color televisions use this principle, dis-
playing colors as combinations of red, green and blue dots. In order to more easily represent
colors in a computer, each of the three amounts of red, green and blue is represented by an
integer value between 0 and 255. (There is nothing magic about the value 255, it was simply
chosen so that each quantity could be stored in an eight-bit byte). For each quantity, the
larger the value, the brighter the component. A value of (0,0,0) is therefore black, while a
value of (255, 255, 255) is white.1 While in theory this encoding can represent 2512 dierent
colors (28  28  28 ), in practice most display devices can handle only a limited range. When
the exact color is unavailable, the actual color produced by a display device will usually be
the closest matching color.
The Java library class Color allows colors to be specied using three integer values. The
class also denes predened constants for a number of commonly used colors. These are
described in Figure 19.1. The constructor for color takes as argument the three integer values
representing the red, green and blue components. These values can subsequently be retrieved
from a color using the methods getRed, getGreen and getBlue. The methods brighter and
1 There is an alternative system that represents each color by a triple of oating point values that represent
the Hue, Saturation and Brightness of the color. The HSB system is not used as much as the RGB system,
and will not be described here. The class Color provides methods for converting between the two systems.

291
292 CHAPTER 19. UNDERSTANDING GRAPHICS
Predened Colors
Color.black Color.magenta
Color.blue Color.orange
Color.cyan Color.pink
Color.darkGray Color.red
Color.gray Color.white
Color.green color.yellow
Color.lightGray
Description of methods in class Color
new Color (int, int, int) constructor
brighter() create brighter version of color
darker() create darker version of color
getBlue() return blue component of color
getGreen() return green component of color
getRed() return red component of color
toString() return string representation of color
Figure 19.1: Methods for class Color

darker return brighter and darker versions of the current color. Finally, the method toString
returns a string representation of the current color. A program that illustrates the use of
many of the features of class Color is described in Section 13.5.

19.2 Rectangles
There are two classes in the Java library used for manipulating polygon shapes. The class
Rectangle represents a rectangular area on a two-dimensional plane. The class Polygon
represents more general polygon shapes.
We have used the class Rectangle already in the cannon world program described in
Chapter 6. In that program, every graphical object, each ball as well as each target, was
placed on top of a rectangle that represented the location of the item. The application used
the fact that a rectangle not only records a position on the two dimensional surface, but
can also be easily moved to a new position, and can tell whether or not it intersects with
another rectangle.
A new rectangle can be created in a variety of ways. If no arguments are provided with
the constructor, then an empty rectangle whose northwest corner is the origin is created.
If two integers are specied, they are taken to be a width and height for a rectangle with
northwest corner at the origin. If four integer arguments are specied, the rst two are the
locations of the northwest corner, and the remaining arguments are the width and height.
Rectangle r = new Rectangle ()
19.2. RECTANGLES 293

Rectangle rTwo = new Rectangle (3, 4) nn width 3, height 4


Rectangle rThree = new Rectangle (7, 6, 3, 4) nn corner 7,6

Operations that test the state of a rectangle can be summarized as follows:


r.equals (rTwo) tell whether two rectangles are equal
r.inside (int, int) tell whether a point is inside the rectangle
r.intersects (rTwo) tell whether two rectangles intersect
r.isEmpty() tell whether rectangle has nonempty extent
The size of a rectangle can be changed in a variety of ways. The add operation takes
two integers that represent a point, and increases the size of the rectangle until it includes
the point. The union operation does the same with another Rectangle argument returning
the smallest rectangle that encloses both. The grow operation takes two integer arguments,
and moves each the horizontal sizes by the rst argument amount, and the vertical sides by
the second, which can be either positive or negative. The method intersection calculates the
intersection of two rectangles. The setLocation method moves a rectangle to a given location,
while the translate method moves a rectangle by a given amount. The setSize method sets
a rectangle size. Both the size and location of a rectangle can be changed by the function
reshape, which takes four integer argument in the same form as the constructor.

19.2.1 Rectangle Sample Program


A sample program, shown in Figure 19.2, illustrates the use of many Rectangle methods.
Two rectangles are initially placed on the window. Each step all rectangles are either resized
or moved, and a test is performed to see if any two rectangles intersect. If so, then a new
rectangle is generated with the same size as their intersection.
The method to move rectangles generates two random numbers. Using these, the rect-
angle is either changed in size, or moved. Tests are performed to ensure the rectangle does
not become negative in size, or move o the board.
private void moveAllRectangles() f
for (Enumeration e = rects.elements() e.hasMoreElements() ) f
Rectangle r = (Rectangle) e.nextElement()
int i = rand(5)
int j = rand(5)
if ((i + j) % 5 == 0) f
r.grow (i, j)
if (r.height < 0)
r.setSize (r.width, - r.height)
if (r.width < 0)
r.setSize (- r.width, r.height)
294 CHAPTER 19. UNDERSTANDING GRAPHICS

class RectTest extends Frame f


public static void main (String  ] args)
f Frame window = new RectTest() window.show() g

private Vector rects = new Vector()


private Random rnd = new Random()
private static int FrameWidth = 400
private static int FrameHeight = 400

public RectTest () f
setSize (400, 400)
setTitle ("Rectangle test")
rects.addElement (new Rectangle(4, 5))
rects.addElement (new Rectangle(100, 100, 6, 7))
g

public void paint (Graphics g) f


g.setColor(Color.green)
for (Enumeration e = rects.elements() e.hasMoreElements() ) f
Rectangle r = (Rectangle) e.nextElement()
g.fillRect (r.x, r.y, r.width, r.height)
g
moveAllRectangles()
spawnNewRectangles()
repaint()
g

private int rand (int max) f return rnd.nextInt() % max g

private void moveAllRectangles() f ... g

private void spawnNewRectangles() f ... g


g

Figure 19.2:
19.3. FONTS 295

g
else f
r.translate (i, j)
if (r.x < 0)
r.setLocation (r.x + FrameWidth, r.y)
if (r.y < 0)
r.setLocation (r.x, r.y + FrameWidth)
if (r.x > FrameWidth)
r.setLocation (r.x - FrameWidth, r.y)
if (r.y > FrameWidth)
r.setLocation (r.x, r.y - FrameWidth)
g
g
g

The routine to test for intersections uses a doubly nested enumeration loop. If it locates
two intersecting rectangles, a new rectangle is created and placed in the upper corner. Only
one new rectangle is created each move, in order to slow the process of lling the window.
private void spawnNewRectangles() f
for (Enumeration e = rects.elements() e.hasMoreElements() ) f
Rectangle r1 = (Rectangle) e.nextElement()
for (Enumeration f = rects.elements() f.hasMoreElements() ) f
Rectangle r2 = (Rectangle) f.nextElement()
if (r1.intersects(r2) && ! r1.equals(r2)) f
Rectangle nr = r1.intersection(r2)
nr.setLocation (0, 0)
rects.addElement(nr)
return
g
g
g
g

19.3 Fonts
A Font object describes the characteristics used to display printed text. Features of a font
that the programmer can modify are the style, size, and font family (or logical name).
The logical font name describes the family of font styles being used. There are six rec-
ognized logical font names, Dialog, Helvetica, TimesRoman, Courier, DialogInput and Symbol.
296 CHAPTER 19. UNDERSTANDING GRAPHICS

Figure 19.3: Example text printed in dierent fonts

The last is used to represent non-alphabetic symbols, and is seldom used. Courier is a
font with xed-width characters, much like a typewriter. Helvetica and Times Roman are
variable width fonts, which are the type normally used for printed text. An example of text
printed in each of the rst ve fonts is shown in Figure 19.3.
A font style describes the thickness and slant of the characters. In the Java library there
are two characteristics that describe a style. These are the Bold attribute and the Italic
attribute. Each is represented by an integer value, which can be combined. Thus, there
are four dierent representations for each font family: plain (neither bold nor italic), bold,
italic, and both bold and italic.
A font point size represents the size of the individual characters. Points are units used
in typesetting. One point is approximately 1/72 of an inch.
The following chart describes the most commonly used methods recognized by instances
of class Font.
Font(String name, int style, int size) construct new font
Font.PLAIN constant used to describe plain fonts
Font.BOLD constant used to describe bold fonts
Font.ITALIC constant used to describe italic fonts
getName() return logical name
getSize() return point size
getStyle() return style characteristics
isBold() determine if font is bold
isItalic() determine if font is italic
isPlain() determine if font is plain
toString() return string representation of font
19.3. FONTS 297

An instance of class Font can be created by providing the logical font name, a style, and
a size. The name must be one of the six names recognized family names (Dialog, Helvetica,
TimesRoman, Courier, DialogInput and Symbol). The style is formed using the class constants.
A font that is both bold and italic can be created by adding the two values Font.BOLD and
Font.ITALIC. The size can be any integer value, although not all sizes can be represented by
all font families on any particular output device. In general, the size used in a display will
be the closest available size to the one specied.

19.3.1 Font Metrics


A separate class, FontMetrics, is occasionally needed to get more detailed information about
a font or about a string printed in a given font. For example, a font metric can be used
to determine the width (in pixels) of a text that is to be printed in a particular font. Font
metrics is an abstract class, and therefore you cannot directly construct instances of the
class. The most common way to access a font metric object is by means of the method
getFontMetrics provided by a object of type Graphics, as in the following example:
public void paint (Graphics g) f
...
g.setFont (aFont)
FontMetric fm = g.getFontMetrics()
...
g

To determine the width of an individual character or a string the methods fm.charWidth(char)


or fm.stringWidth(String) can be used. The result is described in pixel units. Various other
characteristics of a font are accessible using a font metric, for example fm.getHeight() will
return the height (maximum character size) of the font. There are other methods, However
these are rarely used.

19.3.2 Font Example Program


A small test program will illustrate both the use of methods in class Font, as well as how
to program using check boxes. The application will have a bank of six check boxes on the
bottom of the window, corresponding to the six logical font family names. Two buttons on
the right (the east) will set either bold, or italic, or both, while a text edit eld will allow the
size to be set. An example text that illustrates the selected characteristics is then displayed
in the middle of the application window. The main program is shown in Figure 19.4. The
two banks of check boxes are created by the two routines makeStyles and makeNames.
The method makeStyles creates a grid layout of 4 horizontal panels. The rst two are
lled by instances of a class StyleBox that will allow a style to be set. The third is a SizeBox
298 CHAPTER 19. UNDERSTANDING GRAPHICS

class FontTest extends Frame f


static public void main (String  ] args)
f Frame window = new FontTest() window.show() g

private int style = 0


private int size = 15
private String fontName = "Helvetica"

public FontTest () f
setTitle("Font Test") setSize(600, 150)
add("East", makeStyles())
add("South", makeNames())
g

private void display () f repaint() g

public void paint (Graphics g) f


Font f = new Font(fontName, style, size)
g.setFont(f)
FontMetrics fm = g.getFontMetrics()
g.drawString(f.toString(), 5, 10 + 2  fm.getHeight())
g

private Panel makeStyles() f ... g

private class StyleBox extends Checkbox implements ItemListener ...

private class SizeBox extends TextField implements ActionListener ...

private class NameBox extends Checkbox implements ItemListener ...

private Panel makeNames() f ... g


g

Figure 19.4: stu


19.3. FONTS 299

that will set the size of the example text. The nal element is a quit button using the class
ButtonAdaptor dened earlier in Chapter 13.
private Panel makeStyles() f
Panel p = new Panel()
p.setLayout(new GridLayout(4,1))
p.add (new StyleBox(Font.ITALIC, "italic"))
p.add (new StyleBox(Font.BOLD, "bold"))
p.add (new SizeBox())
p.add (new ButtonAdaptor("Quit")f
public void run()fSystem.exit(0)gg)
return p
g

A StyleBox uses a trick we have seen earlier in Chapter 13. The class both extends the
library class Checkbox and implements the class ItemListener. Thus, the class can encapsulate
within itself both the creation of a Checkbox item and the task of listening when the
Checkbox has been selected. When each box is selected it either turns on or o the given
modier bits, depending upon the state of the box. Notice that when checkbooks are created
in this fashion, they are independent of each other, and can be set or unset individually.
(For example, both the bold and italic check box can be set, in order to display bold italic
output).
private class StyleBox extends Checkbox implements ItemListener f
private int modifiers

public StyleBox (int m, String name)


f super (name)
addItemListener (this) modifiers = m g

public void itemStateChanged (ItemEvent e) f


if (getState())
style j= modifiers // turn on modiers
else
style &=  modifiers // turn o modiers
display()
g
g

A SizeBox is a type of TextField, that when edited changes the size value in the application
class.
300 CHAPTER 19. UNDERSTANDING GRAPHICS

private class SizeBox extends TextField implements ActionListener f


public SizeBox () f super ("" + size) addActionListener(this) g

public void actionPerformed (ActionEvent e) f


String sz = getText()
size = (new Integer(sz)).intValue()
display()
g
g

The method makeNames() illustrates the creation of a dierent type of check box. In
this form, only one of the collection can be set at any one time. Setting one item will
automatically unset all others. Such a group are sometimes referred to as radio buttons,
as they operation in a fashion similar to the buttons on a car radio. To create a radio
button collection, a CheckBoxGroup is created. This check box group is then passed with
the constructor to each check box that will be part of the group.
private class NameBox extends Checkbox implements ItemListener f
public NameBox (String name, CheckboxGroup cg)
f super(name, cg, false) addItemListener(this) g

public void itemStateChanged (ItemEvent e)


f fontName = getLabel() display() g
g

private Panel makeNames() f


Panel p = new Panel()
p.setLayout(new GridLayout(1,6))
CheckboxGroup cg = new CheckboxGroup()
p.add (new NameBox("Courier", cg))
p.add (new NameBox("Dialog", cg))
p.add (new NameBox("DialogInput", cg))
p.add (new NameBox("Helvetica", cg))
p.add (new NameBox("TimesRoman", cg))
p.add (new NameBox("Symbol", cg))
return p
g

When any button is pressed, the fontName eld will be changed, and the display method
activated.
19.4. IMAGES 301

19.4 Images
An image is really nothing more than a two dimensional collection of pixel values. Most
commonly images are created by some video device, just as a digital camera, and stored in
a le in a standard format, just as JPEG or GIF. A picture stored in such a format can be
read into a Java program by rst creating a URL (see Section 22.4.1), then using the toolbox
routine getImage. The following program illustrates this technique. The rst command line
argument is assumed to be a URL for a le stored in one of the standard formats. This
image is read from the le, and displayed as the value of the application window.
import java.awt.
import java.net.

class ImageTest extends Frame f


public static void main (String  ] args) f
Frame world = new ImageTest (args0])
world.show()
g

private Image image = null

public ImageTest (String fileName) f


setSize (300, 300)
setTitle ("Image Test")
try f // read image from URL given in argument
URL imageAddress = new URL (fileName)
image = getToolkit().getImage(imageAddress)
g catch (Exception e) f image = null g
g

public void paint (Graphics g) f


if (image != null)
g.drawImage (image, 0, 0, this)
g
g

19.4.1 Animation
Animation is simply the process of displaying a sequence of still pictures one after another.
Just as with a movie, the eye is fooled into linking the pictures together, giving the appear-
ance of smooth motion. The simplest animation program is shown below. Here a series of
302 CHAPTER 19. UNDERSTANDING GRAPHICS

GIF les are read into an array of images. The paint routine selects one of these for display,
then updates an index value so that the next image in sequence will be selected by the
following display. Calling repaint ensures that the next image will then be displayed.
class AnnTest extends Frame f

public static void main (String  ] args) f


Frame world = new AnnTest ()
world.show()
g

private Image ] imageArray


private int index = 0

public AnnTest () f
setSize(300, 300)
setTitle("Simple Animation")
imageArray = new Image  17 ]
for (int i = 0 i < 17 i++) f
String name = "T" + (i+1) + ".gif"
try f
URL address = new URL (name)
imageArrayi] = getToolkit().getImage(address)
g catch (Exception e) f imageArrayi] = null g
g
g

public void paint (Graphics g) f


if (imageArrayindex] != null)
g.drawImage(imageArrayindex++], 0, 0, this)
if (index >= imageArray.length)
index = 0
try f
Thread.sleep(200)
g catch (Exception e) f g
repaint()
g
g

In order to slow the animation down, the paint routine will sleep for 200 milliseconds after
drawing the image. On some platforms the animation will show an annoying icker. This
is because the procedure update, called by repaint, will redraw the screen in the background
19.5. GRAPHICS CONTEXTS 303

color before calling paint. This can sometimes be eliminated by simply overriding the method
update to avoid redrawing the background:
// override update to simply paint window
public void update (Graphics g) f
paint(g)
g

19.5 Graphics Contexts


As we have seen in almost all of our example programs, the majority of graphics in Java are
generated using an object of class Graphics. Such an object is termed the graphics context.
As we noted in Chapter 6, graphics contexts maintain a coordinate system in which the 0,0
location is the upper left corner, and values increase as they move down and to the right.
Graphics contexts also maintain both a foreground and a background color, in a fashion
we have already seen used with windows. The table shown in Figure 19.5 summarizes the
most commonly used method provided by class Graphics. We have used many of these in
our earlier case studies.
Drawing operations in graphics contexts can be performed in one of two dierent modes.
The common mode is paint mode, established by calling the method setPaintMode. An
alternative is xor mode. The unique property of xor mode is that if the same object is
drawn twice, the second drawing command erases the object, restoring the image to the
state it has prior to the rst command. This property is similar to the logical exclusive-or
(xor) command, hence the name. The common use for this property is to draw cursors, or
other graphics images that track the position of the mouse as it moves across a window. We
will see an example of this in the program described in the next section.

19.6 A Simple Painting Program


An example program will illustrate the use of many of the graphics context commands.
The program will also show how one can create a static image that can be generated as a
sequence of graphical commands, rather than all at one time.
The window for the application (Figure 19.6) has a row of buttons across the top.
Pressing one of these buttons selects the type of gure to be drawn. Pressing the mouse
in the canvas window then begins a drawing operation. As the mouse moves while still
being pressed, a \ghost" image of the gure tracks the mouse location. When the mouse is
released, the selected gure is drawn.
The source code for the painting application is shown in Figure 19.7. A Panel is used to
hold the three buttons. The buttons themselves are dened as subclasses of an inner class
304 CHAPTER 19. UNDERSTANDING GRAPHICS

clearRect clear a rectangular region


copyArea copy a rectanguare region to another location
draw3DRect paint a 3 dimensional rectangle
drawArc draw elliptical arc
drawBytes display bytes as text
drawChars display characters as text
drawImage display pixel image
drawLine draw a line from one point to another
drawOval draw oval outline
drawPloygon draw polygon outline
drawRect draw rectangle
drawRoundRect draw rounded rectangle
drawString draw string at given location
llArc paint a lled elliptical arc
llOval paint a lled oval shape
llRect paint a lled rectangular area
llRoundRect paint a lled rounded rectangle area
getColor retrieve the current foreground color
getFont retrieve the font used to print text
getFontMetrics retrieve font metrics object for current font
setColor set the current foreground color
setFont set the font for future printing operations
setPaintMode set drawing to paint mode
setXORMode set drawing to XOR mode

Figure 19.5: Operations provided by graphics context


19.6. A SIMPLE PAINTING PROGRAM 305

Figure 19.6: Window for Painting Application

Shape, which will do double duty as both a button listener class, and a recording of the
currently selected shape. When pressed, the button will simply save its value in the variable
currentShape. In addition to representing a button, each shape will know how to draw itself
in a rectangular region (Figure 19.8). The class Shape, as well as the drawing method, have
been declared abstract. This means that it is not possible to create an instance of the class
without rst subclassing. Furthermore, each subclass must implement the method draw.
The rst time the application is asked to draw its window, it copies the Image associated
with the application component into a variable named image. Thereafter, to render the
application window it is only necessary to draw the contents of this image.
Most applications that interact with the mouse need to recognize the mouse moving
down. Of these, a few (such as the current application) need to dierentiate between the
mouse going down and the mouse going up. Fewer still need to also track the position of
the mouse as it is moving. Because the latter action is relatively infrequent, the Java event
library separates this task into a dierent listener. A MouseListener tracks only mouse presses
and releases. A MouseMotionListener also tracks mouse movements, either while pressed or
not. The mouse class we created for this class implements both interfaces, by extending the
MouseAdaptor, and implementing the MouseMotionListener interface (Figure 19.9).
When the mouse is initially pressed, the location of the mouse press is saved in a pair if
local integer variables. A second pair of integer variables records the new location, and is
initially the same as the source location. As the mouse moves, the method mouseDragged is
306 CHAPTER 19. UNDERSTANDING GRAPHICS
class Paint extends Frame f
public static void main (String  ] args)
f Frame world = new Paint() world.show() g

private Image image = null


private Shape currentShape = null

public Paint () f
setTitle("Paint")
setSize(400, 300)

// create panel of buttons


Panel p = new Panel()
p.setLayout(new GridLayout(1,3))
p.add(new Rectangle())
p.add(new Oval())
p.add(new Line())
add("North", p)
// add mouse event listener
MouseKeeper k = new MouseKeeper()
addMouseListener(k)
addMouseMotionListener(k)
g

public void paint (Graphics g) f


if (image == null) // will happen only once
image = createImage(400, 300)
if (image != null)
g.drawImage(image, 0, 0, this)
g

private abstract class Shape


extends Button implements ActionListener f ... g
private class Rectangle extends Shape f ... g
private class Oval extends Shape f ... g
private class Line extends Shape f ... g

private class MouseKeeper


extends MouseAdapter implements MouseMotionListener f ... g
g

Figure 19.7: Source For Painting Application


19.6. A SIMPLE PAINTING PROGRAM 307
private abstract class Shape
extends Button implements ActionListener f

public Shape (String name) f


super(name)
addActionListener (this)
g

public abstract void draw (Graphics g, int a, int b, int c, int d)

public void actionPerformed (ActionEvent e)


f currentShape = this g
g

private class Rectangle extends Shape f

public Rectangle () f super("Rectangle") g

public void draw (Graphics g, int a, int b, int c, int d) f


int w = c - a // width
int h = d - b // height
g.drawRect(a, b, w, h)
g
g

private class Oval extends Shape f


public Oval () f super("Oval") g

public void draw (Graphics g, int a, int b, int c, int d) f


int w = c - a int h = d - b
g.drawOval(a, b, w, h)
g
g

private class Line extends Shape f


public Line () f super("Line") g

public void draw (Graphics g, int a, int b, int c, int d)


f g.drawLine(a, b, c, d) g
g

Figure 19.8: Shape subclasses


308 CHAPTER 19. UNDERSTANDING GRAPHICS

private class MouseKeeper


extends MouseAdapter implements MouseMotionListener f
private int startx, starty // upper right corner
private int lastx, lasty // current position

public void mousePressed (MouseEvent e) f


lastx = startx = e.getX()
lasty = starty = e.getY()
g

private void drawShape (Graphics g) f


if (currentShape != null)
currentShape.draw(g, startx, starty, lastx, lasty)
g

public void mouseDragged (MouseEvent e) f


Graphics g = image.getGraphics()
g.setXORMode(Color.white)
// erase old image
drawShape(g)
// draw new rectangle
lastx = e.getX() lasty = e.getY()
drawShape(g) repaint()
g

public void mouseReleased (MouseEvent e) f


Graphics g = image.getGraphics()
// erase old image
g.setXORMode(Color.white)
drawShape(g)
// now paint new image
g.setPaintMode()
lastx = e.getX() lasty = e.getY()
drawShape(g) repaint()
g

public void mouseMoved (MouseEvent e) f g


g

Figure 19.9: The Mouse Listener for the Painting Application


19.6. A SIMPLE PAINTING PROGRAM 309

repeatedly executed. This method sets the graphical display mode to XOR, then repaints the
current shape using the old coordinate locations. The eect will be the erase the previous
image. The ending point locations are then set to the current mouse position, and the shape
redrawn. The eect is that the shape is continually erased and redrawn as the mouse is
moved. When the mouse is nally released, the method mouseReleased is executed. This
erases the old image one last time, changes the mode to paint, and draws the shape in its
nal location.

Exercises
1. Add buttons to the painting program described in Section 19.6 to create lled rectan-
gles and ovals, as well as the outline rectangles and ovals it currently produces.
2. Add a panel with three sliders for selecting colors, similar to the panel described in
Chapter 13. Use the colors selected by these sliders for the ll color in the gures you
added in the previous question.
3. By adding a KeyListener object, add the ability to enter text and have it displayed at
the current mouse location.
4. Add menu items to change the font and style of text values.
310 CHAPTER 19. UNDERSTANDING GRAPHICS
Chapter 20

Collection Classes
Collections are classes designed for holding groups of objects. Almost all nontrivial programs
need to maintain one or more collections of objects. Although the Java library provides only
a few dierent forms of collection, the features provided by these classes are very general,
making them applicable to a wide variety of problems. In this chapter we will rst describe
some of the basic concepts common to the collection classes, then summarize the basic
collections provided by Java, and nally describe a few container types that are not provided
by the standard library, but which can be easily constructed by the programmer.

20.1 Elements Types and primitive value Wrappers


With the exception of the array data type, all the collections provided by the Java library
maintain their values in variables of type Object. There are two important consequences of
this feature:
 Since primitive types, such as integers, booleans, characters and oating point values,
are not subclasses of Object, they cannot be directly stored in these collections.
 When values are removed from the collection, they must be cast back to their original
type.
One way to circumvent the rst restriction is through the use of wrapper classes. A
wrapper class maintains a primitive data value, but is itself an object, and can thus be
stored in a container. Methods are typically provided to both to construct an instance of
the wrapper class from a primitive value, and to recover the original value from the wrapper.
The following, for example, stores two integers into instances of class Integer, then recovers
the original values so that an arithmetic operation can be performed:

311
312 CHAPTER 20. COLLECTION CLASSES

Integer wrapper
new Integer(int value) build Integer from int
Integer(String value) parse integer in String
intValue() value of Integer as int
toString() return decimal string representation of Integer
toBinaryString() return binary representation
toOctalString() return octal representation
toHexString() return hex representation
Character wrapper
new Character(char value) convert char to Character
charValue() return char value of Character
isLetter() determine if character is letter
isDigit() true if character is digit
Boolean wrapper
new Boolean (boolean value) convert boolean to Boolean
booleanValue () retrieve boolean value from Boolean
toString() generate string representation of boolean
Double wrapper
new Double(double) convert double to Double
new Double(String) construct Double from string
doubleValue() return double value of Double

Figure 20.1: Wrapper Classes and Selected Behaviors

Integer a = new Integer(12)


Integer b = new Integer(3)
// must recover the int values to do arithmetic
int c = a.intValue()  b.intValue()

In addition, many wrapper classes provide other useful functionality, such as the ability
to parse string values. A common way to covert a string containing an integer value literal
into an int, for example, is to use an Integer as a middle step:
String text = "123" // example string value
Integer val = new Integer(text) // rst convert to Integer
int ival = val.intValue() // then convert Integer to int

Figure 20.1 summarizes the most common wrapper classes and a few of their more useful
behaviors.
20.2. ENUMERATORS 313

20.2 Enumerators
All collections can be envisioned as a linear sequence of elements, however the particular
means used to access each element diers from one collection type to another. For example,
a vector is indexed using an integer position key, while a hash table can use any type of
object as a key. It is frequently desirable to abstract away these dierences, and access the
elements of the collection in sequence without regard to the technique used to obtain the
underlying values. This facility is provided by the Enumeration interface, and its various
implementations. The Enumeration interface species two methods:
hasMoreElements() A boolean value that indicates whether
or not there are any more elements to be enumerated
nextElement() Retrieves the next element in the enumeration
These two operations are used together to form a loop. The following, for example,
shows how all the elements of a hash table could be printed:
for (Enumeration e = htab.elements() e.hasMoreElements() ) f
System.out.println (e.nextElement())
g

The methods hasMoreElements() and nextElement should always be invoked in tandem.


That is, nextElement should never be invoked unless hasMoreElements has rst determined
that there is another element, and nextElement should never be invoked twice without an
intervening call on hasMoreElements. If it is necessary to refer more than once to the value
returned by nextElement, the result of the nextElement call should be assigned to a local
variable:
for (Enumeration e = htab.elements() e.hasMoreElements() ) f
Object value = e.nextElement()
if (value.equals(Test))
System.out.println ("found object " + value)
g

With the exception of the array, all the collections provided by the Java library provide
a function that generates an enumeration. In addition, several other classes that are not
necessarily collections also support the enumeration protocol. For example, a StringTokenizer
is used to extract words from a string value. The individual words are then accessed using
enumeration methods. In Section 20.6.1 we will describe how the programmer can create a
new type of enumeration.
314 CHAPTER 20. COLLECTION CLASSES

20.3 The Array


The most basic collection form in Java is the array. As we have noted in earlier chapters,
the creation and manipulation of an array value is dierent in Java than in many other
programming languages. The Java language makes a separation between (a) declaring a
variable of array type, (b) dening the size of the array and allocating space, and (c)
assigning values to the array. In many other languages the rst and second tasks are merged
together. Merging these concepts makes it dicult to, for example, write functions that will
operate on arrays of any size. These problems are largely eliminated in Java's approach to
arrays.
An array variable is declared by indicating the type of elements the array will contain,
and a pair of square brackets that indicate an array is being formed. The following, for
example, is from the solitaire game case study examined in Chapter 9.
static public CardPile allPiles  ]

The brackets can be written either after the variable name, or after the type. The
following, for example, is an equivalent declaration:
static public CardPile  ] allPiles

Note that the array is the only collection in the Java library that requires the programmer
to specify the type of elements that will be held by the container. An important consequence
of this is that the array is the only collection that can maintain non-object types, such as
integers, booleans, or characters. Other containers hold their values as instances of class
Object, and can therefore only hold primitive values if they are surrounded by wrapper
classes (Integer, Double, and so on).
An array value is created, as are all values, using the new operator. It is only when the
array value is created that the size of the array is specied.
allPiles = new CardPile  13 ] // create array of 13 card piles
Arrays can also be created as an initialization expression in the original declaration. The
initialization expression provides the values for the array, as well as indicating the number
of elements.
int primes  ] = f2, 3, 5, 7. 11, 13, 17, 19g

Elements of an array are accessed using the subscript operator. This is used both to
retrieve the current value held at a given position, and to assign a position a new value:
primes3] = primes2] + 2
20.4. THE VECTOR COLLECTION 315

Legal index values range from zero to one less than the number of elements held by
the array. An attempt to access a value with an out of range index value results in an
IndexOutOfBoundsException being thrown.
The integer eld length describes the number of elements held by an array. The following
loop shows this value being used to form a loop to print the elements in an array:
for (int i = 0 i < primes.length i++)
System.out.println("prime " + i + " is " + primesi])

Arrays are also cloneable (see Section 11.3.1). An array can be copied, and assigned to
another array value. The clone operation creates a shallow copy.
int numbers  ]
numbers = primes.clone() // creates a new array of numbers

20.4 The Vector collection


The Vector data abstraction is similar to an array of objects, however it provides a number
of high level operations not supported by the array abstraction. Most importantly, a vector
is expandable, meaning it grows as necessary as new elements are added to the collection.
Thus, the programmer need not know the eventual collection size at the time the vector is
created. To use this collection the Vector class must be imported from java.util.Vector. A
new vector is created using the new operator.
import java.util.Vector
...
Vector numbers = new Vector ()

Because objects held by a Vector must be subclasses of Object, a vector can not be used
to hold primitive values, such as integers or oats. However, wrapper classes can be used
to convert such values into objects.
Table 20.1 summarizes the most useful operations provided by the Vector data abstrac-
tion. The class as been carefully designed so that it can be used in a variety of dierent
ways. The following sections describe some of the more common uses.

20.4.1 Using a Vector as an array


Unlike an array, a vector is not created with a xed size. A vector can be given a specic
size either by adding the appropriate number of elements (using the addElement operation)
316 CHAPTER 20. COLLECTION CLASSES

Size Determination
size() returns number of elements in collection
isEmpty() returns true if collection is empty
capacity() return current capacity of vector
setSize() set size of vector, truncating or expanding as necessary
Element Access
contains() determines whether a value is in this vector
rstElement() returns rst element of collection
lastElement() returns last element of collection
elementAt(index) returns element stored at given position
Insertion and Modication
addElement(value) add new value to end of collection
setElementAt(value, index) change value at position
insertElementAt(value, index) insert value at given index
Removal
removeElementAt(index) remove element at index, reducing size of vector
removeElement(value) remove all instances of value
removeAllElements() delete all values from vector
Search
indexOf(value) return index of rst occurrence
lastIndexOf(value) return index of last occurrence
Miscellaneous
clone() return shallow copy of vector
toString() return string representation of vector

Table 20.1: Operations provided by the Vector data type


20.4. THE VECTOR COLLECTION 317

or by using setSize. In the latter case a null value will be stored in any index locations that
have not previously been assigned a value:
Vector aVec = new Vector () // create a new vector
aVec.setSize (20) // allocate 20 locations, initially undened
Once sized, the value stored at any position can be accessed using the method elementAt,
while positions can be modied using setElementAt. Note that in the latter, the index of
the position being modied is the second parameter, while the value to be assigned to the
location is the rst parameter. The following illustrates how these functions could be used
to swap the rst and nal elements of a vector:
Object first = aVec.elementAt(0) // store rst position
aVec.setElementAt(aVec.lastElement(), 0) // store last in location 0
aVec.setElementAt(first, aVec.size()-1) // store rst at end

20.4.2 Using a Vector as a stack


A stack is a data structure that allows elements to be inserted and removed at one end, and
always removes the last element inserted. A stack of papers on a desk is a good intuitive
picture of the stack abstraction.
Although the Java standard library includes a Stack data abstraction (see Section 20.5),
it is easy to see how a Vector can be used as a stack. The characteristic operations of a stack
are to insert or remove an item from the top of the stack, peek at (but do not remove) the
topmost element, test the stack for emptiness. The following shows how each of these can
be performed using operations provided by the vector class:
push an value on the stack aVec.addElement (value)
peek at the topmost element of the stack aVec.lastElement()
remove the topmost element of the stack aVec.removeElementAt(aVec.size() - 1)
As we will note in Section 20.5, the Stack abstraction is slightly more robust, since it
will throw more meaningful error indications if an attempt is made to remove an element
from an empty stack.

20.4.3 Using a Vector as a queue


A queue is a data structure that allows elements to be inserted at one end, and removed from
the other. In this fashion, the element removed will be the rst element that was inserted.
Thinking about a line of people waiting to enter a theater provides a good intuition.
In a manner analogous to the way that the vector can be used as a stack, the vector
operations can also be used to simulate a queue:
318 CHAPTER 20. COLLECTION CLASSES
push an value on the queue aVec.addElement (value)
peek at the rst element in the queue aVec.rstElement()
remove the rst element of the queue aVec.removeElementAt(0)
There is one important dierence between this abstraction and the earlier simulation
of the stack. In the stack abstraction, all the operations could be performed in constant
time, independent of the number of elements being held by the stack.1 Removing the rst
element from the queue, on the other hand always results in all elements being moved, and
is therefore always requires time proportional to the number of elements in the collection.

20.4.4 Using a Vector as a set


A set is usually envisioned as an unordered collection of values. Characteristic operations
on a set include testing to see if a value is being held in the set, and adding or removing
values from the set. The following shows how these can be implemented using the operations
provided by the Vector class:
see if value is held in set aVec.contains(value)
add new element to set aVec.addElement(value)
remove element from set aVec.removeElement(value)
The equals method is used to perform comparisons. Comparisons are necessary to de-
termine whether or not a value is held in the collection, and whether a value is the element
the user wishes to delete. For user dened data values the equals method can be overridden
to provide whatever meaning is appropriate (see Section 11.4).
Operations such as union and intersection of sets can be easily implemented using a
loop. The following, for example, places in setThree the union of the values from setOne
and setTwo.
Vector setThree = new Vector()
setThree = setOne.clone() // rst copy all of set one
// then add elements from set two not already in set one
for (Enumeration e = setTwo.elements() e.hasMoreElements() ) f
Object value = e.nextElement()
if (! setThree.contains(value))
setThree.addElement(value)
g

For sets consisting of positive integer values, the BitSet class (Section 20.6 is often a
more ecient alternative.
1 The assertion concerning constant time operation of stack operations is true with one small caveat. An
insertion can, in rare occasions, result in a reallocation of the underlying vector bu er, and thus require
time proportional to the number of elements in the vector. Thus is usually, however, a rare occurrence.
20.5. THE STACK COLLECTION 319

20.4.5 Using a Vector as a list


Characteristic operations of a list data abstraction are the abilities to insert or remove
elements at any location, and the ability to nd the location of any value.2
rst element aVec.rstElement()
last element aVec.lastElement()
insert to front of list aVec.insertElementAt(value, 0)
insert to end of list aVec.addElement(value)
see if value is in collection aVec.contains(value)
remove rst element aVec.removeElementAt(0)
remove last element aVec.removeElementAt(aVec.size() - 1)
nd location of element aVec.indexOf(value)
remove value from middle aVec.removeElementAt(index)
Again, this use of the Vector abstraction to simulate a list diers from the classical
description of the list abstraction in the algorithmic execution time of certain operations. In
particular, the insertion or removal from the front of the collection may result in the entire
set of values being moved, thereby requiring time proportional to the size of the collection.
This is only a critical concern when the size of the collections is large or when this is a
frequent operation. A more direct implementation of a list is described in Section 20.9.

20.5 The Stack collection


Section 20.4.2 described how a stack could be simulated using a Vector. However, the Java
standard library also provides the Stack as a data value. The names of the methods used
to operate on this data type are slightly dierent from the Vector operations described in
Section 20.4.2, and the structure is slightly more robust. Stack operations can be described
as follows:
create a new stack Stack aStack = new Stack()
push an value on to the stack aStack.push (value)
peek at the topmost element of the stack aStack.peek()
remove the topmost element of the stack aStack.pop()
number of elements in the collection aStack.size()
test for empty stack aStack.empty()
position of element in stack aStack.search(value)
The pop operation both removes and returns the topmost element of the stack. Both
the pop and the peek operations will throw an EmptyStackException if they are applied to
an empty stack.
2 The list referred to here is the traditional data abstraction known by that name. The Java library
unfortunately uses the class name List to refer to a graphical component that allows the user to select a
value from a series of string items.
320 CHAPTER 20. COLLECTION CLASSES

The search method returns the index of the given element starting from the top of the
stack that is, if the element is found at the top of the stack search will return 0, if found one
element down in the stack search will return 1, and so on. Because the Stack data structure
is built using inheritance from the Vector class it is also possible to access the values of
the stack using their index. However, the positions returned by the search operation do
not correspond to the index position. To discover the index position of a value the Vector
operation indexOf can be used.
In Chapter 10 we described some of the advantages and disadvantages of creating the
stack using inheritance from class Vector.

20.6 The BitSet collection


A BitSet is abstractly a set of positive integer values. The BitSet class diers from the other
collection classes in that it can only be used to hold integer values. Like an array or a
Vector, each element is given an index position. However, the only operations that can be
performed on each element are to set, test or clear the value. A BitSet is a compact way
to encode either a collection of positive integer values, or a collection of boolean values (for
example, on/o settings).
A create a BitSet the user can specify the number of positions the set will represent.
However, like the Vector, the bit set is extensible, and will be enlarged automatically if a
position outside the range is accessed.
// create a BitSet that initially contains 75 elements
BitSet bset = new BitSet(75)

The following table summarizes the operations used to set or test an individual position
in the bit set:
set a bit position bset.set (index)
test a bit position bset.get (index)
clear a bit position bset.clear (index)
The get method returns a boolean value, which is true if the given bit is set, and false
otherwise. Each of these operations will throw an IndexOutOfBoundsException if the index
value is smaller than zero.
A BitSet can be combined with another BitSet in a variety of ways:
Form bitwise union with argument set bset.or(setTwo)
Form bitwise intersection with argument set bset.and(setTwo)
Form bitwise symmetric dierence with argument set bset.xor(setTwo)
The method toString returns the string representation of the collection. This consists of
a comma-separated list of the indices of the bits in the collection that have been set.
20.7. THE DICTIONARY INTERFACE AND THE HASHTABLE COLLECTION 321

20.6.1 Example Program: Prime Sieve


A program that will generate a list of prime numbers using the sieve of Erasthones can
be used to illustrate the manipulation of a bit set. The constructor for the class Sieve
(Figure 20.2) takes an integer argument n, and creates a bit set of n positions. These are
initially all set to one, using the member function set. The sieve algorithm then walks
through the list, using get to nd the next set value. A loop then walks through the
remainder of the collection, throwing out (via the clear() member function) values which
are multiples of the earlier value. When we are nished, any value not crossed out must be
prime.
The remaining two functions illustrate how a new enumeration can be created. The
value index will maintain the current \position" in the list, which will change as values are
enumerated. The function hasMoreElements loops until a prime value is found, or until the
size of the bit set is exceeded. The rst results in a true value, the latter a false one. The
method nextElement simply makes an object out of the integer value. A small test method
is also included in the class, to illustrate how this class could be used.

20.7 The Dictionary interface and the Hashtable collection


A dictionary is an indexed collection, similar to an array or a Vector. However, unlike an
array, the index values need not be integer. Instead, any object type can be used as in index
(called a key), and any object value can be stored as the element selected by the key. To
place a new value into the collection the user provides both the key and value. To access
an element in the collection the user provides a key, and the associated value is returned.
In the Java library the class Dictionary is an abstract class that denes the behavior of
the dictionary abstraction, but does not provide an implementation. This interface can be
described as follows:
retrieve value associated with given key dict.get(key)
place value into collection with given key dict.put(key, value)
remove value from collection dict.remove(key)
see if collection is empty dict.isEmpty()
return number of elements in collection dict.size()
return enumeration for collection values dict.elements()
return enumeration of collection keys dict.keys()
The get method will return null if the given value is not found in the collection. Otherwise,
the value is returned as an Object, which must then be cast into the appropriate type. The
remove method returns the value of the association being deleted, again returning null if the
key is not a legal index element. There are two enumeration generating methods, one to
return an enumeration of keys, and one to return an enumeration of values.
The class Hashtable provides an implementation of the Dictionary operations. A hash
table can be envisioned as an array of collections, called buckets. To add an element to
322 CHAPTER 20. COLLECTION CLASSES

import java.util.

class Sieve implements Enumeration f


private BitSet primes
private int index = 2

public Sieve (int n) f


primes = new BitSet(n)
// rst set all the bits
for (int i = 1 i < n i++)
primes.set(i)
// then erase all the non-primes
for (int i = 2 i  i < n i++)
if (primes.get(i))
for (int j = i + i j <= n j += i)
primes.clear(j)
g

public boolean hasMoreElements () f


index++
int n = primes.size()
while (! primes.get(index))
if (++index > n)
return false
return true
g

public Object nextElement() f return new Integer(index) g

// test program for prime sieve algorithm


public static void main (String  ] args) f
Sieve p = new Sieve(100)
while (p.hasMoreElements())
System.out.println(p.nextElement())
g
g

Figure 20.2: Prime Sieve program


20.7. THE DICTIONARY INTERFACE AND THE HASHTABLE COLLECTION 323

the collection, an integer value, called the hash value, is rst computed for the given key.
The method hashCode is used for this purpose. This method is dened in class Object,
and is therefore common to all object values. It is overridden in various classes to provide
alternative algorithms. Using this integer, one of the buckets is selected and the key/value
pair inserted into the corresponding collection.
In addition to the methods matching the Dictionary specication, the hash table provides
the method clear(), which removes all values from the container, contains(value), which
determines whether an element is contained in the collection, and containsKey(key), which
tests to see if a given key is in the collection.
The default implementation of the hashCode method, in class Object, should be applicable
in almost all situations, just as the default implementation of equals is usually adequate. If
a data type that is going to be used as a hash table key overrides the equals method, it is a
good idea to also override hashCode, so that two objects that test equal to each other will
also have the same hash value.

20.7.1 Example Program: A Concordance


A concordance is a listing of words from a printed text, each word being followed by the
lines on which the word appears. A class that will create a concordance will illustrate how
the Dictionary data type is used, as well as how dierent collection classes can be combined
with each other.
In the program shown in Figure 20.3, the primary data structure is a dictionary, im-
plemented using the Hashtable class. The keys for this dictionary will be the individual
words in the input text. The value associated with each key will be a set of integer values,
representing the line numbers on which the word appears. A Vector will be used to represent
the set, using the techniques described in Section 20.4.4.
The method readLines reads the input line by line, maintaining a counter to indicate
the line number. The method readLine, provided by the class DataInputStream, returns
a null value when end of input is encountered, at which time the method returns. (This
method is also the potential source for the IOException, which can be thrown if an error
occurs during the read operation. In our program we simply pass this exception back to
the caller). Otherwise, the text is converted to lower case, using the method toLowerCase
provided by the String class, then a StringTokenizer is created to split the text into individual
words. A StringTokenizer is a form of Enumeration, and so an enumeration loop is used to
enter each word into the concordance.
The private method enterWord is used to place each new word in the concordance. First,
the value associated with the key (the word) is determined. Here the program handles the
rst of two exceptional conditions that might arise. If this is the rst time the word has
been seen, there will be no entry in the dictionary, and so result of calling get will be a
null value. In this case a new and empty Vector is created, and inserted into the dictionary
using the word as key. Using the Vector in the fashion of a set, the method contains is
invoked to determine if the line has already been placed in the collection. (This is the
324 CHAPTER 20. COLLECTION CLASSES
import java.util.
import java.io.

class Concordance f
private Dictionary dict = new Hashtable()

public void readLines (DataInputStream input) throws IOException f


String delims = " \t\n.,!?:"
for (int line = 1 true line++ ) f
String text = input.readLine()
if (text == null) return
text = text.toLowerCase()
Enumeration e = new StringTokenizer(text, delims)
while (e.hasMoreElements())
enterWord ((String) e.nextElement(), new Integer(line))
g
g

public void generateOutput (PrintStream output) f


Enumeration e = dict.keys()
while (e.hasMoreElements() ) f
String word = (String) e.nextElement()
Vector set = (Vector) dict.get(word)
output.print (word + ": ")
Enumeration f = set.elements()
while (f.hasMoreElements())
output.print (f.nextElement() + " ")
output.println (" ")
g
g

private void enterWord (String word, Integer line) f


Vector set = (Vector) dict.get(word)
if (set == null) f // word not in collection
set = new Vector() // make new set
dict.put (word, set)
g
if (! set.contains(line)) set.addElement(line)
g
g

Figure 20.3: The class Concordance


20.7. THE DICTIONARY INTERFACE AND THE HASHTABLE COLLECTION 325

second exceptional condition, which will occur if the same word appears two or more times
on one line.) If not, the line is then added to the list.
Finally, once all the input has been processed, the method generateOutput is used to
create the printed report. This method uses a doubly-nested enumeration loop. The rst
loop enumerates the keys of the Dictionary, generated by the keys method. The value asso-
ciated with each key is a set, represented by a Vector. A second loop, using the enumerator
produced by the elements method, then prints the values held by the vector.
An easy way to test the program is to use the system resources System.in and System.out
as the input and output containers, as in the following:

static public void main (String  ] args) f


Concordance c = new Concordance()
try f
c.readLines(new DataInputStream(System.in))
g catch (IOException e) f return g
c.generateOutput (System.out)
g

20.7.2 Properties
The Java run-time system maintains a special type of hash table, termed the properties list.
The class Properties, a subclass of Hashtable, holds a collection of string key/value pairs.
These represent values the describe the current executing environment, such as the user
name, operating system name, home directory, and so on. The following program can be
used to see the range of properties available to a running Java program:

public static void main (String  ] args) f


Dictionary props = System.getProperties()
Enumeration e = props.keys()
while (e.hasMoreElements()) f
Object key = e.nextElement()
Object value = props.get(key)
System.out.println("property " + key + " value " + value)
g

g
326 CHAPTER 20. COLLECTION CLASSES

20.8 Why are there no ordered collections?


If one considers the \classic" data abstractions found in most data structures textbooks, a
notable omission from the Java library are data structures that maintain values in sequence.
Examples of such abstractions are ordered lists, ordered vectors, or binary trees. Indeed,
there is not even any mechanism provided in the Java library to sort a vector of values.
Rather than being caused by oversight, this omission re ects some fundamental properties
of the Java language.
All of the Java collections maintain their values in variables of type Object. The class
Object does not dene any ordering relation. Indeed, the only elements that can be com-
pared using the < operator are the primitive numeric types (integer, long, double, and so
on). One could imagine dening in class Object a method lessThan(Object), similar to the
method equals(Object). However, while there is a clear default interpretation for the equal-
ity operator (namely, object identity), it is dicult to imagine a similar meaning for the
relational operator that would be applicable to all objects. Certainly it could not provide
a total ordering on all objects. What, for example, would be the result of comparing the
String "abc" and the integer 37? In short, ordered collections are not found in the Java
library because there is no obvious general mechanism to dene what it means to order two
values.
One could imagine that an alternative to placing the method lessThan in class Object
would be to create an Ordered interface, such as the following:
interface Ordered f
public boolean compare (Ordered arg)
g

One could then create a collection in which all values need to implement the Ordered
interface, rather than simply being Object. However, there are two major objections to
this technique. The rst is that since the argument is only known to be an object that
implements the Ordered interface, one must still decide how to compare objects of dierent
types (a Triangle and an Orange, for example). The second problem is that by restricting the
type of objects the collection can maintain to only those values that implement the Ordered
relation, one severely limits the utility of the classes.
Another possibility is to imagine an interface for an object that is used to create com-
parisons. That is, the object takes both values as arguments, and returns their ordering.
Such an interface could be written as follows:
interface ComparisonObject f
public boolean Compare (Object one, Object two)
g
20.8. WHY ARE THERE NO ORDERED COLLECTIONS? 327

To manipulate an ordered collection, one would then create an implementation of this


interface for the desired elements. The following, for example, would be a comparison class
for Integer objects:
class IntegerComparison implements ComparisonObject f
public boolean Compare (Object one, Object two) f
if ((one instanceof Integer) && (two instanceof Integer)) f
Integer ione = (Integer) one
Integer itwo = (Integer) two
return ione.intValue() < itwo.intValue()
g
return false
g
g

The following program illustrates how such an object could be used. The static method
sort is an implementation of the insertion sort algorithm. The main method creates a vector
of integer values, then creates a comparison object to be passed as argument to the sort
algorithm. The sorting algorithm orders the elements in place, using the comparison object
to determine the relative placement of values.
class VectorSort f
public static void sort (Vector v, ComparisonObject test) f
// order a vector using insertion sort
int n = v.size()
for (int top = 1 top < n top++) f
for (int j = top-1 j >= 0 &&
test.Compare(v.elementAt(j+1), v.elementAt(j)) j--) f
// swap the elements
Object temp = v.elementAt(j+1)
v.setElementAt(v.elementAt(j), j+1)
v.setElementAt(temp, j)
g
g

public static void main (String  ] args) f


Vector v = new Vector()
Random r = new Random()
for (int i = 0 i < 10 i++)
v.addElement(new Integer(r.nextInt()))
328 CHAPTER 20. COLLECTION CLASSES

// sort the vector


sort (v, new IntegerComparison())

for (Enumeration e = v.elements() e.hasMoreElements() )


System.out.println(e.nextElement())
g
g

20.9 Building your Own Containers


Although the containers in the Java library are exible, they nevertheless cannot handle
all situations in which a collection class is needed. It is therefore sometimes necessary to
create new collection classes. We will illustrate how this can be done by creating a class
that implements the idea of a linked list. The major advantage of the linked list over a
vector is that insertions or removals to the beginning or the middle of a linked list can
be performed very rapidly (technically, in constant time). In the vector these operations
require the movement of all the elements in the collection, and can therefore be much more
costly if the collection is large.
The LinkedList class abstraction is shown in Figure 20.4.3 The actual values are stored in
instances of class Link, which is a nested inner class. In addition to a value, links maintain
references to the previous and next element in the list. A private internal value rstLink will
reference the rst link. A link with an empty value is used to mark the end of the list. The
private internal value lastLink points to this value.
Values can be inserted either to the front or the back of the list. An enumeration value
can also be used to insert new elements into the middle of a list. The value is inserted
immediately before the element referred to by the enumeration. All three methods make
use of a common insertion routine provided by the inner class Link. This method is also
used to maintain the count of the number of elements in the list. The classes Link and
ListEnumeration are shown in Figure 20.5.
The class ListEnumeration implements the Enumeration protocol, and is used for iter-
ating over list elements. Note that the Enumeration protocol assumes that the methods
hasMoreElements and nextElement will work in tandem, and does not specify which of the
two will actually advance the internal reference to the next element. Implementations of
the Enumeration protocol use a variety of dierent schemes. This is why, for example, one
should never invoke nextElement twice without an intervening call on hasMoreElements. In
the LinkedList class, however, we assume that having examined the current value (the value
3 It would have been preferable to call this class List. However, has we noted in an earlier footnote, the
Java library already has a List class, that implements a graphical component used for selecting one string
item out of many alternatives.
20.9. BUILDING YOUR OWN CONTAINERS 329
class LinkedList f
private Link firstLink
private Link lastLink
private int count = 0

public LinkedList ()
f firstLink = lastLink = new Link(null, null, null) g

private class Link f ... g

private class ListEnumeration implements Enumeration f ... g

public boolean isEmpty () f return firstLink == lastLink g

public int size () f return count g

public Object firstElement () f return firstLink.value g

public Object lastElement () f return lastLink.prev.value g

public void addFront (Object newValue) f firstLink.insert(newValue) g

public void addBack (Object newValue) f lastLink.insert(newValue) g

public void addElement (Enumeration e, Object newValue) f


ListEnumeration le = (ListEnumeration) e
le.link.insert (newValue)
g

public Object removeFront () f return firstLink.remove() g

public Object removeBack () f return lastLink.prev.remove() g

public Object removeElement (Enumeration e) f


ListEnumeration le = (ListEnumeration) e
return le.link.remove ()
g

public Enumeration elements () f return new ListEnumeration() g


g

Figure 20.4: The Linked List Class


330 CHAPTER 20. COLLECTION CLASSES
class LinkedList f
private class Link f
public Object value
public Link next
public Link prev

public Link (Object v, Link n, Link p)


f value = v next = n prev = p g

public void insert (Object newValue) f


Link newNode = new Link (newValue, this, prev)
count++
if (prev == null) firstLink = newNode
else prev.next = newNode
prev = newNode
g

public Object remove () f


if (next == null)
return null // cannot remove last element
count--
next.prev = prev
if (prev == null) firstLink = next
else prev.next = next
return value
g
g

private class ListEnumeration implements Enumeration f


public Link link = null

public boolean hasMoreElements () f


if (link == null) link = firstLink
else link = link.next
return link.next != null g

public Object nextElement () f return link.value g


g
...
g

Figure 20.5: The Inner Classes in LinkedList


20.9. BUILDING YOUR OWN CONTAINERS 331

yielded by nextElement) the programmer may wish to either insert a new value or remove
the current value. Thus, in this case the task of advancing to the next value is given to the
method hasMoreElements.

Study Questions
1. What are collection classes used for?
2. Because the standard library collection classes maintain their values as an Object,
what must be done to a value when it is removed from a collection?
3. What is a wrapper class?
4. What is an enumerator?
5. What is the protocol for the class Enumerator? How are these methods combined to
form a loop?
6. How is the Java array dierent from arrays in other languages?
7. What does it mean to say that the Vector data type is expandable?
8. How does the use of the Stack data type dier from the use of a Vector as a stack?
9. What concept does the class BitSet represent?
10. What is the relationship between the classes Dictionary and Hashtable?
11. Why are there no ordered collections in the Java library?

Exercises
1. Assume two sets are implemented using vectors, as described in Section 20.4.4. Write
a loop that will place the intersection of the two sets into a third set.
2. Assume two sets are implemented using vectors, as described in Section 20.4.4. Write
a loop that will place the symmetric dierence of the two sets into a third set. (The
symmetric dierence is the set of elements that are in one or the other set, but not
both).
3. Add the following methods to the LinkedList class described in Section 20.9:

setElement(Enumeration e, Object v) change value at given location


includes(Object v) test whether value is in collection
nd(Object v) return enumeration if value is in collection, or null
332 CHAPTER 20. COLLECTION CLASSES

4. Modify the LinkedList class of Section 20.9 so that linked lists support the cloneable
interface. (The cloneable interface is described in Section 11.3.1).
5. Write an OrderedList class. This class will be like a linked list, but will maintain a
comparison object, as described in Section 20.8. Using this object, elements will be
placed in sequence as they are inserted into the container.
Chapter 21

Multiple Threads of Execution


Most users of a computer are familiar with the idea of multitasking. A user of a multitasking
computer might be editing a spreadsheet, for example, at the same time the computer is
printing a report or receiving a fax. The appearance is that the computer is doing more than
one activity at the same time. Whether this is actually true depends upon the hardware
involved. Unless you are using a machine with multiple processors, in fact what is actually
occurring is that the operating system is rapidly cycling between the dierent activities,
allowing each to execute for a small amount of time.
Multithreading is the same concept applied to an individual program. Each program is
giving the ability to execute a number of tasks at the same time. Each of these computational
tasks is called a thread. Unlike separate computer applications, multiple threads exist in
the same executing environment (technically speaking, we say they have the same context).
This means they share the same data variables, access the same procedures, and so on. As
with multithreading, this parallel behavior is usually just a charade in fact the operating
system is still executing only one thread at any one time, and cycling among all the dierent
threads to give each an equal share.

21.1 Creating Threads


Recall the BallWorld application from Chapter 5 (Figure 5.1). This program created a
bouncing ball, then moved the ball around on the window 2000 times before halting. You
may recall that this is all the program did. It did not allow any sort of user interaction, and
if you tried to stop the program before 2000 steps you could not.
In practice it is desirable to have programs that are more user friendly. Part of this is
being able to interact with an application while it is running. One way to do this is to place
the execution of the program in a separate thread, leaving the main program thread free to
listen for user activity.
333
334 CHAPTER 21. MULTIPLE THREADS OF EXECUTION

In Java, threads are created using the class Thread. There are two common ways that
this class is used. One way is to subclass from Thread, and override the method run, which is
the class that is executed to perform the task assigned to a thread. We could, for example,
modify the BallWorld application (Figure 5.1) and create a bouncing ball thread as follows:
public class ThreadedBallWorld extends Frame f
...
private class BallThread extends Thread f
public void run () f
while (true) f
aBall.move()
if ((aBall.x() < 0) jj (aBall.x() > FrameWidth))
aBall.setMotion (-aBall.xMotion(), aBall.yMotion())
if ((aBall.y() < 0) jj (aBall.y() > FrameHeight))
aBall.setMotion (aBall.xMotion(), -aBall.yMotion())
repaint()
try f
sleep(50)
g catch (InterruptedException e) f g
g
g
g
...
g

When started, the thread continually moves the ball, changing the direction of motion
when the sides of the window are hit. Each time the ball moves the window is repainted,
and the thread sleeps for 50 milliseconds.
The revised ball world application is shown in Figure 21.1. In the constructor for the
application ThreadedBallWorld a Ball is created, and then a BallThread is produced to control
the movement of the ball. Execution of the new thread is initiated by the call to start for the
thread object. To illustrate that the user can now interact with the application as the ball
moves, we have added a mouse listener object. When the mouse is clicked in the application
window, the ball is moved to the mouse location.
Platforms dier on how they assign time to threads. On some systems threads are run
until they sleep or until they yield to another thread. On other systems threads are given
a xed amount of time to execute, then halted automatically if they have not yet given up
control when their time unit is nished. In order to provide maximum portability, threads
should either sleep occasionally (as in done here) or periodically invoke the method yield
that is inherited from class Thread. Either of these actions will halt the current thread,
allowing other threads a chance to perform their actions.
21.1. CREATING THREADS 335

public class ThreadedBallWorld extends Frame f

public static void main (String  ] args)


f ThreadedBallWorld world = new ThreadedBallWorld (Color.red)
world.show () g

private static final int FrameWidth = 600


private static final int FrameHeight = 400
private Ball aBall
private int counter = 0

private ThreadedBallWorld (Color ballColor) f


// constructor for new ball world
// resize our frame
setSize (FrameWidth, FrameHeight)
setTitle ("Ball World")
addMouseListener (new BallListener())

// initialize object data eld


aBall = new Ball (10, 15, 5)
aBall.setColor (ballColor)
aBall.setMotion (3.0, 6.0)
Thread ballThread = new BallThread()
ballThread.start()
g

private class BallThread extends Thread f ... g

private class BallListener extends MouseAdapter f


public void mousePressed (MouseEvent e)
f aBall.moveTo(e.getX(), e.getY()) g
g

public void paint (Graphics g) f aBall.paint (g) g


g

Figure 21.1: Ball World application using Threads


336 CHAPTER 21. MULTIPLE THREADS OF EXECUTION

It is sometimes not convenient to create a thread class by subclassing from Thread. For
example, it can happen that the tread class is already inheriting from some other class.
Since in Java a class can not extend from two classes, the designers of the language have
provided an alternative technique. Instead of inheriting from Thread, the action portion of
a thread can be declared as implementing the interface Runable. The ball thread could be
rewritten in this form as follows:

public class ThreadedBallWorld extends Frame f


...
private class BallThread implements Runable f
public void run () f
while (true) f
aBall.move()
if ((aBall.x() < 0) jj (aBall.x() > FrameWidth))
aBall.setMotion (-aBall.xMotion(), aBall.yMotion())
if ((aBall.y() < 0) jj (aBall.y() > FrameHeight))
aBall.setMotion (aBall.xMotion(), -aBall.yMotion())
repaint()
try f
Thread.sleep(50)
g catch (InterruptedException e) f g
g
g
g
...
g

Note the format used for the call on sleep. Because the new class is not tied to Thread,
the method sleep is not be automatically inherited. Instead, the class name Thread must be
explicitly provided in this case.
Starting a thread in this case involves creating two objects. The rst is the instance
of Runable, which describes the actions to be performed. The second is the Thread object,
which understands how to schedule the actions for execution. The Runable object is passed
as argument to the Thread, as in the following:

Thread ballThread = new Thread(new BallThread())


ballThread.start()
21.1. CREATING THREADS 337

21.1.1 Synchronizing Threads


Two or more threads frequently need to share the same data elds. If two threads attempt
to modify the same data area it is possible that erroneous values can be produced. Assume
we have two threads that share an integer data eld, and assume that they each want to
increment the value by 10. Internally an addition statement is divided into several separate
steps. For example, the statement:
a = a + 10
is executed as something similar to the following:
push the value of a on a stack
push the value 10 on the stack
add the two values on the stack
pop the top of stack, assign value to a

Assume that these same sequence of instructions are performed by two threads, and the
original value of a is 50. Assume further than the thread that is running the rst process is
stopped immediately after the rst push instruction above. The rst thread will push the
value 50 on its stack, then stop temporarily. The second thread will push the value 50 on its
stack, increment by ten, then store the resulting value of 60 on its stack. Suppose now the
rst thread is restarted. It will continue with its actions, pushing 10 on the stack, adding
the two values, and assigning the sum, 60, back to a. The end result will be that the value
a will ultimately hold 60, not the value 70 that it should have held.
Although it might seem that a problem such as this might be rare, the speed with which
computers execute means that a situation such as that described here, if possible at all, is
not at all uncommon. Such errors are also exceedingly dicult to uncover, since they occur
in such a rare situation. Fortunately, the designers of Java have foreseen this and provided a
simple and elegant solution. A method that is declared as synchronized cannot be executed
by two processes at the same time. That is, if one thread is executing a synchronized method
and a second thread tries to execute any other synchronized thread with the same object, the
second thread will be halted until the rst thread completes. The solution to the problem
just described is therefore to place the increment operation inside a method that is declared
as synchronized:
public synchronized void increment () f
// only one thread can execute this
// at any time
a = a + 10
g

We will illustrate the use of a synchronized method in the case study presented in
Section 21.2. There are many more advanced features of threads, however they are generally
338 CHAPTER 21. MULTIPLE THREADS OF EXECUTION

not needed in most programs and will not be described here.

21.2 Case Study: A Tetris Game


An arcade game is a good illustration of the need for multiple threads of control. In a typical
arcade game, the user is constantly entering information, and the game itself is constantly
moving. In a conventional single threaded programming language, one or the other of these
tasks must be selected as the primary structure. Either the main program is a large loop
that listens for the users commands, and every now and then moves the game forward, or
it is a large loop that moves the elements of the game, and every now and then listens for
the users instructions. Either form will end up being clumsy and dicult to debug.
A much more elegant solution is possible in the language Java. Each of these two tasks
can be assigned its own process. Each thread is executed in parallel with other threads. On
thread is simply listening for the users commands, while the other thread is charged with
moving the pieces.

' $ ' $

Process one: Process two:


Listen Move
for game
user pieces
commands

& % & %
Of course, there must be some sort of communication between the two threads, and this
is where most of the complexity in multithreaded programming occurs. In the Tetris game
presented here, the communication consists only of one integer value, which maintains the
next command to be performed.
In addition to demonstrating multiple threads of control, the Tetris game presented in
this section will illustrate how to read keyboard events, and an important technique for
preventing ickering in graphical displays.
21.2. CASE STUDY: A TETRIS GAME 339

Figure 21.2: Application window for Tetris game

21.2.1 The Tetris Game Class


Tetris is a popular arcade game. Playing pieces of several dierent forms fall from the sky,
and can merge together when the reach the ground. The player can move a piece left or
right, or rotate a piece as it falls to the ground. The player scores by arranging pieces as
they fall so as to form a completely lled row in the ground. Figure 21.2 shows a snapshot
of a game in execution.
The primary class for the Tetris game is shown in Figure 21.3. As with all applications,
execution begins with a static procedure named main. The main procedure creates an
instance of the class Tetris, which is a type of Frame, and places this window on the display
using the method show.
The constructor for the Tetris class sizes the application window, gives it a name, adds
a listener for key presses, then creates and starts the second process. The game playing
process will be an instance of a second class, named PieceMover. We will examine the
structure of this class shortly. Once created, the thread of execution that will play the game
340 CHAPTER 21. MULTIPLE THREADS OF EXECUTION

class Tetris extends Frame f

public static void main(String ] args)


f Tetris world = new Tetris() world.show() g

// data elds
public static final int FrameWidth = 300
public static final int FrameHeight = 400
private GamePlay player

public Tetris () f
setSize (FrameWidth, FrameHeight)
setTitle("Tetris")

addKeyListener (new keyDown())


player = new GamePlay(this)
player.start()
g

// interface point between two threads


private int currentCommand = Piece.Down

public synchronized int getCommand(int nextCommand) f


int oldCommand = currentCommand
currentCommand = nextCommand
return oldCommand
g

private class keyDown extends KeyAdapter f ... g

public void update (Graphics g)


f player.paint(sg) g
g

Figure 21.3: The Controller class for the Tetris Game


21.2. CASE STUDY: A TETRIS GAME 341

is initiated by the issuing of the command start.


Communication Between Threads
Communication between the two threads of control is accomplished via a single integer
variable. This value represents the next command to be executed. This variable is both set
and accessed by means of the procedure getcommand. Note that this procedure is declared
as synchronized. This means that only one of the two threads can be executing the procedure
at any one time. If the other process attempts to execute the procedure, it will be held up
until the rst process completes. (In truth, the synchronization here is largely unnecessary,
since the only penalty for simultaneous access will be that one process may received an out
of date value. Nevertheless, the use of the synchronized designation also helps to document
the interface between the two routines).
Handing Keyboard Events
Keyboard events are handled in a fashion that is similar to the mouse click events we have
examined previously. A component maintains a collection of key listeners, which must each
implement the KeyListener interface:
interface KeyListener extends EventListener f
public void keyTyped (KeyEvent e)
public void keyPressed (KeyEvent e)
public void keyReleased (KeyEvent e)
g

As with mouse presses, the programmer can elect to either treat a key press and the key
release as independent events, or simply examine one event for both actions. Implementing
the KeyListener interface requires dening a procedure for all three methods. As normally
one is only interested in one of these, an adapter class, named KeyAdaptor, is provided that
implements a null procedure for each of the three functions. To create a listener one can
subclass from this adapter, and redene the meaning of one of the methods. This is how
key presses are handled in our application:
class Tetris extends Frame f
...
private class keyDown extends KeyAdapter f
public void keyPressed (KeyEvent e) f
char key = e.getKeyChar()
switch (key) f
case g : player.newGame() break
case j : getCommand(Piece.Left) break
342 CHAPTER 21. MULTIPLE THREADS OF EXECUTION

case k : getCommand(Piece.Rotate) break


case l : getCommand(Piece.Right) break
case q : System.exit(0)
g
g
g
...
g

Avoiding Flicker
As the game is being played, the screen is constantly being updated. This update process
consists largely of drawing each individual square of the display. If this is done as separate
graphical commands, the result can be an annoying icker. There are several common ways
to avoid this. Perhaps one of the easiest is the technique shown here. The method update()
inherited from class Component normally rst redraws the entire screen in the background
color, then invokes paint to refresh the screen. If rather than overriding paint, one overrides
update, then the redrawing in the background color is avoided.
One often confusing feature to note is that although the update procedure invokes the
paint procedure in the class of the player object (namely, the class PieceMover) the resulting
actions are not performed by the piece mover thread. Updating the image is performed by
the rst thread, and is independent of the piece mover thread, which continues to run on
its own.

21.2.2 The PieceMover Thread


The class PieceMover is shown in Figure 21.4. The fact that the class extends class Thread,
and the presence of the method named run, indicate that this class can potentially be
executed as an independent thread.
The constructor for the class will be executed when the instance of PieceMover is created,
as in Figure 21.3. The constructor sets the values of local variables, including creating an
array of colors for the playing area. The playing area will be 15 elements wide by 30 elements
deep. Because we think of the playing area as a graphical object, it will be easier to describe
this by placing the horizontal movement rst and the vertical movement second.
The thread starting point is a procedure named run. This method will be executed when
the thread is started. Note that the method the programmer writes is called run, while the
method used to initiate the thread is called start (the call on start in this case appears in
Figure 21.3). The start method performs some other actions, such as setting up a schedule
for thread execution, before invoking run. In our simple example, the run method will simply
repeatedly create and drop playing pieces.
As the playing pieces move, they will indicate the change by altering the colors stored in
the color table. Thus, the paint method simply cycles through the table, and displays each
21.2. CASE STUDY: A TETRIS GAME 343

class PieceMover extends Thread f

private Tetris controller


private Color table]]
private Piece currentPiece
private int score = 0

public PieceMover (Tetris t) f


controller = t
table = new Color15]30]
currentPiece = null
newGame()
g

// thread starting point


public void run ()
f while (dropPiece()) f g g

// other methods
public void newGame () f
for (int i = 0 i < 30 i++)
for (int j = 0 j < 15 j++)
tablej]i] = Color.white
g

public void paint (Graphics g) f ... g

public boolean dropPiece () f ... g

private void moveDown (int start) f ... g

private void checkScore() f ... g


g

Figure 21.4: The PieceMover Class


344 CHAPTER 21. MULTIPLE THREADS OF EXECUTION

color in a small rectangle.


public void paint (Graphics g) f
for (int i = 0 i < 30 i++)
for (int j = 0 j < 15 j++) f
g.setColor(tablej]i])
g.fillRect(20+10j, 350-10i, 10, 10)
g
g.setColor (Color.blue)
g.drawString("Score " + score, 200, 200)
g

The heart of the piece movement thread is the function dropPiece. This procedure creates
a new piece, selecting the type of piece randomly from among the seven possibilities. If the
piece cannot be moved downward once initially created at the top of the scene, the game is
over, and the procedure returns false. Otherwise, a loop then reads the commands placed
in the buer shared with the controller, and acts on them, passing the command value to
the individual piece for action. After every move the controller is asked to repaint, and
control of the processor is yielded, allowing the other process time to execute. When control
is returned, the piece movement process still sleeps for 100 milliseconds, before reading the
next command and moving once more.
private boolean dropPiece ()
f
int piecetype = 1 + (int) (7  Math.random ())
currentPiece = new Piece (piecetype)
if (! currentPiece.move (Piece.Down, table))
return false
int command = controller.getCommand (Piece.Down)
while (currentPiece.move (command, table)) f
controller.repaint ()
yield ()
try f
sleep (100)
g catch (InterruptedException e) f g
command = controller.getCommand (Piece.Down)
g
// piece cannot move, check score
checkScore()
return true
g
21.2. CASE STUDY: A TETRIS GAME 345

The while loop terminates when the piece can no longer move. At this point the procedure
checkScore is called to determine if any points have been scored. Points are scored by
completely lling a row. If such a row is found, the controller is notied that a score has
been made, and the entire image is moved downward.
private void checkScore()
f
for (int i = 0 i < 30 i++) f
boolean scored = true
for (int j = 0 j < 15 j++)
if (tablej]i] == Color.white)
scored = false
if (scored) f
score += 10
moveDown(i)
i = i - 1 // check row again
g
g
g

private void moveDown (int start)


f
for (int i = start i < 30 i++)
for (int j = 0 j < 15 j++)
if (i < 29)
tablej]i] = tablej]i+1]
else
tablej]i] = Color.white
g

The procedure moveDown copies the colors from the row above into the current row,
copying white values into the topmost row.

21.2.3 The Game Piece class


An individual game piece is composed of a number of blocks. Each block is identied only
by an x and y coordinate, maintained in a pair of parallel arrays. A piece also has its own
color. The constructor for the class Piece (Figure 21.5) takes an integer that represents the
piece type, and initializes the locations appropriately. Only the initialization of the \S"
shaped piece is shown, the others are all similar.
Other than the constructor, the only publicly accessible procedure for the game piece is
the function named move. This function tries to move the piece according to the command
346 CHAPTER 21. MULTIPLE THREADS OF EXECUTION

class Piece f
// data elds
private int x]
private int y]
private Color color
// moves
public static final int Down = 1
public static final int Left = 2
public static final int Rotate = 3
public static final int Right = 4

// constructor
public Piece (int type)
f
switch(type) f
case 1: // s shaped piece
x = new int4]
y = new int4]
x0] = 7 y0] = 29
x1] = 7 y1] = 28
x2] = 8 y2] = 28
x3] = 8 y3] = 27
color = Color.green
break
...
g
g
...
g

Figure 21.5: The class Piece


21.2. CASE STUDY: A TETRIS GAME 347

given. The function returns false if the command is a move down and the piece cannot
move, which indicates that no further actions are possible.
class Piece f
...
public boolean move (int command, Color ]] table)
f
erase(table)
boolean canDoIt = false
switch (command) f
case Down: canDoIt = testDown(table) break
case Right: canDoIt = testMoveRight(table) break
case Left: canDoIt = testMoveLeft(table) break
case Rotate: canDoIt = testRotate(table) break
g
if (canDoIt)
switch (command) f
case Down: moveDown() break
case Right: moveRight() break
case Left: moveLeft() break
case Rotate: moveRotate() break
g
draw(table)
if (command == Down)
return canDoIt
return true
g
g

Erasing and redrawing the piece are performed by transferring colors to the color table:
class Piece f
...
private void erase (Color ]] table)
f
for (int i = 0 i < x.length i++)
tablexi]]yi]] = Color.white
g

private void draw (Color ]] table)


f
for (int i = 0 i < x.length i++)
348 CHAPTER 21. MULTIPLE THREADS OF EXECUTION

tablexi]]yi]] = color
g
g

Before any command is performed, a test is conducted to see if the spaces the blocks
will occupy subsequent to the command are open. The following demonstrates one of these
routines, the others are similar. If the target locations are open, the corresponding move
routine performs the actual transformation.
class Piece f
...
private boolean testPosition (int x, int y, Color]] table)
f
if ((x < 0) jj (x > 14))
return false
if ((y < 0) jj (y > 29))
return false
if (tablex]y] != Color.white)
return false
return true
g

private boolean testDown (Color ]] table)


f
for (int i = 0 i < x.length i++)
if (! testPosition(xi], yi]-1, table))
return false
return true
g

private void moveDown ()


f
for (int i = 0 i < x.length i++)
yi] = yi] - 1
g
g

The only tricky procedure is the rotation. One square, the square at position 1, is des-
ignated the square around which rotation will be performed. The dierences in coordinate
from this square are computed, and the updated coordinates determined.
class Piece f
21.2. CASE STUDY: A TETRIS GAME 349

...
private boolean testRotate (Color ]] table)
f
for (int i = 0 i < x.length i++) f
int dx = xi] - x1]
int dy = yi] - y1]
int nx = x1] + dy
int ny = y1] - dx
if (! testPosition(nx, ny, table))
return false
g
return true
g
g

Cross References
The ball world case study appears originally in Chapter 5. Placing balls in their own thread
of control is also shown in the pin ball game application described in Chapter 7.

Study Questions
1. What is the dierence between multitasking and multithreading?
2. Describe the two ways to create a new thread.
3. What are the two ways described in this chapter to temporarily halt the execution of
a thread?
4. When used in a method heading, what does the keyword synchronized mean?

Exercises
1. Add a keyboard listener to the ball world application shown in Figure 21.1. When the
user types the keys u or d, change the ball direction to move upwards or downwards,
respectively. When the user types q quit the application.
2. Change the mouse down procedure so that when the user presses the mouse a new
ball is created and set in motion. To do this you will need to create a collection of ball
objects (see Chapter 5). Each new object should execute in its own separate thread.
350 CHAPTER 21. MULTIPLE THREADS OF EXECUTION

3. The Tetris game can be made to \speed up" by reducing the amount of time the piece
mover sleeps between commands. Change the code so that this time is reduced by 5
milliseconds for every 100 points the user scores.
4. Change the program so that instead of scoring 10 points for each lled row, the player
gets additional points for multiple rows scored with the name piece. For example, a
second row will get 20 points, a third row 30 points, and a fourth row 40 points.
5. Change the program to score 50 extra points if an entirely lled row is all one color.
Chapter 22

Applets and Web Programming


As we noted in Chapter 2, although Java is a general purpose programming language that
can be used to create almost any type of computer program, much of the excitement sur-
rounding Java has been generated by its employment as a language for creating programs
intended for execution across the world-wide-web. Programs written for this purpose must
follow certain conventions, and dier slightly from programs designed to be executed di-
rectly on a computer, such as the ones we have developed up to now. In this chapter we
will examine these dierences, and describe how to create programs for the web.

22.1 Applets and HTML


Applications written for the world wide web are commonly referred to as applets. Applets are
attached to documents distributed over the world-wide-web. These documents are written
using the HTML (HyperText Markup Language) protocol. A web browser that includes a
Java processor will then automatically retrieve and execute the Java program. Two HTML
tags are used to describe the applet as part of an HTML document. These are the <applet>
tag and the <param> tag. A typical sequence of instructions would be the following:
<applet codebase="http://www.sun.com" code=Main width=300 height=200>
<param name=name1 value="value1">
You do not have a Java enabled browser
</applet>

The <applet> tag indicates the address of the Java program. The codebase parameter
gives the URL web address where the Java program will be found, while the code parameter
provides the name of the class. The height and width attributes tell the browser how much
space to allocate to the applet.
351
352 CHAPTER 22. APPLETS AND WEB PROGRAMMING

Just as users can pass information into an application using command line arguments,
applets can have information passed into them using the <param> tags. Within an applet
the values associated with parameters can be accessed using the method getParameter().
Any code other than a <param> tag between the beginning and end of the <applet>
tag is displayed only if the program cannot be loaded. Such text can be used to provide the
user with alternative information.

22.2 Security Issues


Applets are designed to be loaded from a remote computer (the server) and then executed
locally. Because most users will execute the applet without examining the code, the potential
exists for malicious programmers to develop applets that would do signicant damage, for
example erasing a hard drive. For this reason, applets are much more restricted than
applications are in the type of operations they can perform.
1. Applets are not permitted to run any local executable program.
2. Applets cannot read or write to the local computers le system.
3. Applets can only communicate with the server from which they originate. They are
not allowed to communicate with any other host machine.
4. Applets can learn only a very restricted set of facts about the local computer. For
example, applets can determine the name and version of the operating system, but
not the users name or e-mail address.
In addition, dialog windows that an applet creates are normally labeled with a special
text, so the user knows they were created by an Java applet and are not part of the browser
application.

22.3 Applets and Applications


All the applications created prior to this chapter that made use of graphical resources have
been formed as subclasses of class Frame. This class provided the necessary underpinnings
for creating and managing windows, graphical operations, events, and the other aspects of
a stand-alone application.
A program that is intended to run on the web has a slightly dierent structure. Rather
than subclassing from Frame, such a program is subclassed from Applet. Like Frame, the
class Applet provides the necessary structure and resources needed to run a program on the
web. The class Applet is a subclass of Panel (see Section 13.4) and thus inherits all the
graphical component attributes of that class.
22.4. OBTAINING RESOURCES USING AN APPLET 353

Rather than starting execution with a static function named main, as applications do,
applets start execution at a function named init, which is dened in class Applet but can
be overridden by users. The procedure init is one of four routines dened in Applet that is
available for overriding by users. These four can be described as follows:
init() Invoked when an applet is rst loaded for example, when a web page containing the
applet is rst encountered. This method should be used for one-time initialization.
This is similar to the code that would normally be found in the constructor for an
application.
start() Called to begin execution of the applet. Called again each time the web page con-
taining the applet is exposed. This can be used for further initialization, or restarting
the applet when the page on which it appears is made visible after being covered.
stop() Called when a web page containing an applet is hidden. Applets that do extensive
calculations should halt themselves when the page on which they are located becomes
covered, so as to not occupy system resources.
destroy() Called when the applet is about to be terminated. Should halt the application
and free any resources being used.
For example, suppose a web page containing an applet as well as several other links
is loaded. The applet will rst invoke init(), then start(). If the user clicks on one of the
links, the web page holding the applet is overwritten, but it is still available for the user
to return to. The method stop() will be invoked to temporarily halt the applet. When the
user returns to the web page, the method start(), but not init(), will once again be executed.
This can happen many times before the user nally exits altogether the page containing the
applet, at which time the method exit() will be called.
Figure 22.1 shows portions of the painting application described in Section 19.6, now
written as an applet rather than as an application. In place of the main procedure, the applet
contains an init procedure. The init takes the place both of main and of the constructor
for the application class. Other aspects of the applet are the same. Because an applet
is a subclass of Panel, events are handed in exactly the same fashion as other graphical
components. Similarly, an applet repaints the window in exactly the same fashion as an
application. Because an applet is a panel, it is possible to embed components and construct
a complex graphical interface (see Chapter 13). Note, however that the default layout
manager for an Applet is a ow layout, rather than the border layout that is default to
applications.

22.4 Obtaining Resources using an Applet


The class applet provides a number of methods that can be used to load resources from the
server machine. The method getImage(URL), for example, takes a URL, and retrieves the
354 CHAPTER 22. APPLETS AND WEB PROGRAMMING

import java.applet.
import java.awt.
import java.awt.event.

public class PaintApplet extends Applet f

private Image image = null


private Shape currentShape = null

public void init () f


// change our layout manager
setLayout(new BorderLayout())
// create panel for buttons
Panel p = new Panel()
p.setLayout(new GridLayout(1,3))
p.add(new Rectangle())
p.add(new Oval())
p.add(new Line())
add("North", p)
MouseKeeper k = new MouseKeeper()
addMouseListener(k)
addMouseMotionListener(k)
g

...

Figure 22.1: Painting program written as applet


22.4. OBTAINING RESOURCES USING AN APPLET 355

image stored in the given location. The URL must specify a le in jpeg or gif format. The
method getAudioClip(URL) similarly returns an audio object from the given location. The
audioClip can subsequently be asked to play itself. A shorthand method play(URL) combines
these two features.
The method getCodeBase() return the URL for the codebase specied for the applet
(see the earlier discussion on HTML tags). Since Java programs are often stored in the
same location as associated documents, such as gif les, this can be useful in forming URL
addresses for related resources.
The method getParameter() takes as argument a String, and returns the associated value
(again, as a string) if the user provided a parameter of the given name using a <param>
tag. A null value is returned if no such parameter was provided.

22.4.1 Universal Resource Locators


Resources, such as Java programs, gif les, or data les are specied using a Universal
Resource Locator, or URL. A URL consists of several parts, including a protocol, a host
computer name, and a le name. The following example shows these parts:
http://www.mymachine.org/leName.html
URLs can be created using the class URL. The address is formed using a string, or using
a previous URL and a string. The latter form, for example, can be used to retrieve several
les that reside in the same directory. The directory is rst specied as a URL, then each
le is specied as a URL with the le name added to the previous URL address. The
constructor for the class URL will throw a MalformedURLException if the associated object
cannot be accessed across the internet.
The class URL provides a method openStream, which returns an inputStream value (see
Chapter 14). Once you have created a URL object, you can use this method to read from
the URL using the normal InputStream methods. In this way, reading from a URL is as easy
as reading from a le, or any other type of input stream. The following program reads and
displays the contents of a web page. The URL for the web page is taken from the command
line argument.
import java.net.
import java.io.

class ReadURL f
public static void main (String  ] args) f
try f
URL address = new URL(args0])
DataInputStream in =
new DataInputStream(address.openStream())
String line = in.readLine()
356 CHAPTER 22. APPLETS AND WEB PROGRAMMING

while (line != null) f


System.out.println(line)
line = in.readLine()
g
in.close()
g
catch (Exception e) f System.out.println("got exception " + e) g
g
g

If you run the program, you should see the HTML commands and textual content dis-
played for the web page given as argument.

22.4.2 Loading a New Web Page


Applets used with web browsers can instruct the browser to load a new page. This feature
is frequently used to simulate links or buttons on a web page, or to implement image maps.
The method appletContext.showDocument(URL) takes a URL as argument, the instructs the
web browser to display the indicated page.

22.5 Combining Applications and Applets


The class Applet pays no attention to any static methods that may be contained within
the class denition. We can use this fact to create a class that can be executed both as
an applet and as an application. They key idea is that an Applet is a panel. We can nest
within the applet class an inner class that creates the Frame necessary for an application.
The only component of the window created for this frame will be the panel constructed
by the applet. The main program, which is ignored by the applet, will when executed as
an application create an instance of the applet. The applet can then create an instance of
Frame for the application, placing itself in the center of the window. The constructor for the
Frame executes the methods init() and start() required to initialize the applet. The following
shows this technique applied to the painting applet described earlier:
import java.applet.
import java.awt.
import java.awt.event.

public class PaintApplet extends Applet f

// executed for applications


// ignored by applet class
22.5. COMBINING APPLICATIONS AND APPLETS 357

public static void main (String  ] args) f


Frame world = new PaintApplet().application()
world.show()
g

private Frame application()


f return new AppletFrame (this) g

private class AppletFrame extends Frame f


public AppletFrame (Applet p) f
setTitle("Paint Application")
setSize (400, 300)
p.init() p.start()
add("Center", p)
g
g

... // remainder as before


g

Once should trace carefully the sequence of operations being performed here, and the
order that objects are created. Since the Frame is nested within the Applet, it is only possible
to create the frame (in the method application) after the applet has already been created.

Exercises
1. Convert the pin ball game described in Chapter 7 to run as an applet, rather than as
an application.
2. Convert the Tetris game described in Chapter 21 to run as an applet, rather than as
an application.
358 CHAPTER 22. APPLETS AND WEB PROGRAMMING
Glossary
abstract A keyword applied to either a class or a method. When applied to a class, the
keyword indicates that no instances of the class can be created, and the class is used
only as a parent class for subclassing. When applied to a method within an abstract
class, it indicates that the method must be overridden in subclasses before any instance
of the subclass can be created.
abstract class Syn. deferred class, abstract superclass. A class that has been declared
using the abstract keyword. Classes can be declared to be abstract even if they do not
contain any abstract methods.
abstract method A method that has been declared using the abstract keyword. Abstract
methods can only appear in classes that have themselves been declared as abstract.
abstraction A technique in problem solving in which details are grouped into a single
common concept. This concept can then be viewed as a single entity and nonessential
information ignored.
access specier A keyword (private, protected, or public) that controls access to data mem-
bers and methods within user-dened classes.
accessor function A function that is used to access the values of an instance variable.
By restricting access through a function, the programmer can ensure that instance
variables will be read but not modied (see mutator).
ad hoc polymorphism Syn. overloading. A procedure or method identier that denotes
more than one procedure.
agent Syn. object, instance. A nontechnical term sometimes used to describe an object
in order to emphasize its independence from other objects, and the fact that it is
providing a service to other objects.
argument signature An internal encoding of a list of argument types the argument sig-
nature is used to disambiguate overloaded function invocations, with that function
359
360 GLOSSARY

body being selected that matches most closely the signature of the function call. See
parameteric overloading.
automatic storage management A policy in which the underlying run-time system is
responsible for the detection and reclamation of memory values no longer accessible,
and hence of no further use to the computation. See garbage collection.
base class Syn. ancestor type, superclass, parent class. A class from which another class
is derived.
binding The process by which a name or an expression is associated with an attribute,
such as a variable and the type of value the variable can hold.
binding time The time at which a binding takes place. Early or static binding generally
refers to binding performed at compile time, whereas late or dynamic binding refers
to binding performed at run time.
bytecode The assembly language for an imaginary Java virtual machine. So-called because
most instructions can be encoded in a form that is one or two bytes in length.
cast A unary expression that converts a value from one type to another.
child class Syn. subclass, derived class. A class dened as an extension of another class,
which is called the parent class.
class Syn. object type. An abstract description of the data and behavior of a collection of
similar objects. The representatives of the collection are called instances of the class.
Class The class that maintains behavior related to class instance and subclass creation. See
metaclass.
class hierarchy A hierarchy formed by listing classes according to their class-subclass
relationship. See hierarchy.
client-side computing In a network environment, a program that is executed on the client
side rather than on the server side of the network. The Java programming language
is intended to perform client-side computing and so is more ecient than programs
that must wait for execution on the (generally more overloaded) server machine.
cohesion The degree to which components of a single software system (such as members
of a single class) are tied together. Contrast with coupling.
collaborator Two classes which dependend upon each other for the execution of their
behaviors are said to be collaborators.
collection classes Classes used as data structures that can contain a number of elements.
Examples include Vector, Stack, Hashtable and arrays.
GLOSSARY 361

composition The technique of including user-dened object types as part of a newly de-
ned object, as opposed to using inheritance.
constructor A method used to create a new object. The constructor handles the dual tasks
of allocating memory for the new object and ensuring that this memory is properly
initialized. The programmer denes how this initialization is performed. In Java a
constructor has the same name as the class in which it appears.
contravariance A form of overriding in which an argument associated with a method in
the child class is restricted to a less general category than the corresponding argument
in the parent class. Contrast with covariance. Neither covariant nor contravariant
overriding is common in object-oriented languages.
coupling The degree to which separate software components are tied together. Contrast
with cohesion.
covariance A form of overriding in which an argument associated with a method in the
child class is enlarged to a more general category than the corresponding argument in
the parent class. Contrast with contravariance. Neither covariant nor contravariant
overriding is common in object-oriented languages.
CRC card An index card that documents the name, responsibilities, and collaborators for
a class, used during the process of system analysis and design.
data hiding An encapsulation technique that seeks to abstract away the implementation
details concerning what data values are maintained for an object to provide a particular
service.
data member See instance variable.
deferred class See abstract class.
derived class Syn. descendant type, subclass, child class. A class that is dened as an
extension or a subclass of another class, which is called the base class.
descendant type Syn. subclass, child class. See derived class.
early binding See binding time.
encapsulation The technique of hiding information within a structure, such as the hiding
of instance data within a class.
event driven execution A style of programming where the program largely responds to
user generated events, such as a mouse click or a keypress.
362 GLOSSARY

exception An unusual condition that prevents the normal sequence of instructions from
going onwards. An example would be attempting to use a uninitialized value (a null
value) as the target of a message passing expression.
exception handler The portion of a Java program devoted to handling the processing of
exceptions.
extends A keyword used in forming a new class as a subclass of an existing class, or a new
interface as an extension of an existing interface.
nal A keyword used in forming either a nal class or a nal method within a class.
nal class A class declared using the keyword nal. This keyword indicates that the class
cannot be used as a base class for inheritance.
nal method A method declared using the keyword nal. This keyword indicates that the
method cannot be overridden in subclasses.
nalizer A method with the name nalize, no arguments, and no return type. This method
will be invoked automatically by the run-time system prior to the object in which it
is declared being recycled by garbage collection.
function member See method.
garbage collection A memory management technique whereby the run-time system de-
termines which memory values are no longer necessary to the running program, and
automatically recovers and recycles the memory for dierent use.
has-a relation The relation that asserts that instances of a class possess elds of a given
type. See is-a relation.
hierarchy An organizational structure with components ranked into levels of subordination
according to some set of rules. In object-oriented programming the most common
hierarchy is that formed by the class-subclass relationship.
immediate superclass The closest parent class from which a class inherits. The superclass
relationship is a transitive closure of the immediate superclass relationship.
immutable value A value that is not permitted to change once it has been set. Variables
that hold such values are sometimes called \single-assignment" variables. In Java
immutable values can be identied via the keywords nal and static.
implements A keyword used to indicate that a class provides the behavior described by an
interface.
GLOSSARY 363

information hiding The principle that users of a software component (such as a class)
need to know only the essential details of how to initialize and access the component,
and do not need to know the details of the implementation. By reducing the degree
of interconnectedness between separate elements of a software system, the principle of
information hiding helps in the development of reliable software.
inheritance The property of objects by which instances of a class can have access to
data and method denitions contained in a previously dened class, without those
denitions being restated. See ancestor class.
inheritance graph An abstract structure that illustrates the inheritance relationships
with a collection of classes.
inner class A class denition that appears inside another class. Inner classes are allowed
access to both the data elds and methods of the surrounding class. Inner classes are
used frequently in building listener objects for handling events.
instance Syn. object.
instance variable An internal variable maintained by an instance. Instance variables rep-
resent the state of an object.
interaction diagram A diagram that documents the sequence of messages that ow be-
tween objects participating in a scenario.
interface A description of behavior. Classes that claim to implement the interface must
provide the services described by the interface.
Internet A world-wide collection of machines that have agreed to communicate with each
other using a common protocol.
is-a relation The relation that asserts that instances of a subclass must be more specialized
forms of the superclass. Thus, instances of a subclass can be used where quantities of
the superclass type are required. See has-a relation.
interpreter A computer program that simulates the actions of the imaginary Java virtual
machine. The interpreter examines and executes the bytecode representation of a Java
program.
iterator A class that is used mainly to provide access to the values being held in another
class, usually a container class. The iterator provides a uniform framework for access-
ing values, without compromising the encapsulation of the container.
JIT An acronym for Just In Time compiling. A technique whereby immediately before
a program is execute the device independent bytecode representation of a Java pro-
gram is converted into machine code for a specic platform. The machine code rep-
resentation will often execute much faster than an interpreter running the bytecode
representation.
364 GLOSSARY

late binding See binding time.


listener An object that waits for an event to occur, then executes certain actions to respond
to the event.
message Syn. message selector, method designator, method selector, selector. The tex-
tual string that identies a requested action is a message-passing expression. During
message passing, this string is used to nd a matching method as part of the method-
lookup process.
message passing The process of locating and executing a method in response to a message.
See method lookup.
message selector Syn. method designator, method selector, selector. The textual string
that identies a message in a message-passing expression. During message passing,
this string is used to nd a matching method as part of the method-lookup process.
method A procedure or function associated with a class (or object type) and invoked in a
message-passing style.
method declaration The part of a class declaration specic to an individual method.
method designator Syn. message selector. A method name identier used as a procedure
or function name in a message-passing expression. The method designator is used to
search for the appropriate method during message sending. In general, you cannot
determine from the program text which method a method designator will activate
during execution.
method lookup The process of locating a method matching a particular message, gener-
ally performed as part of the message-passing operation. Usually, the run-time system
nds the method by examining the class hierarchy for the receiver of the message,
searching from bottom to top until a method is found with the same name as the
message.
method selector See message selector.
mutator A method that is used to modify the value of an instance variable. By requiring
such modications to be mediated through a function, a class can have greater control
over how its internal state is being modied.
native method A method that is implemented in another language, such as C or assembly
language. See primitive.
object See instance.
GLOSSARY 365

object-oriented programming A style of design that is centered around the delegation of


responsibilities to independent interacting agents, and a style of programming charac-
terized by the use of message passing and classes organized into one or more inheritance
hierarchies.
overload Used to describe an identier that denotes more than one object. Procedures,
functions, methods, and operators can all be overloaded. A method that is overridden
can also be said to be overloaded. See parameteric overloading.
override The action that occurs when a method in a subclass with the same name as a
method in a superclass takes precedence over the method in the superclass. Nor-
mally, during the process of binding a method to a message (see message passing), the
overriding method will be the method selected.
paradigm An illustrative model or example, which by extension provides a way of orga-
nizing information. The object-oriented paradigm emphasizes organization based on
behaviors and responsibilities.
parameteric overloading Overloading of function names in which two or more procedure
bodies are known by the same name in a given context, and are disambiguated by the
type and number of parameters supplied with the procedure call. (Overloading of
functions, methods, and operators can also occur).
parent class Syn. superclass, ancestor class. An immediate superclass of a class.
Parnas's principles Principles that describe the proper use of modules, originally devel-
oped by the computer scientist David Parnas.
persistent object An object that continues to exist outside of the execution time of pro-
grams that manipulate the object.
polymorphic Literally \many shapes." A feature of a variable that can take on values of
several dierent types, or when used with functions that describe a function that has
at least one polymorphic argument. The term is also used for a function name that
denotes several dierent functions. See pure polymorphism, ad hoc polymorphism.
polymorphic variable A variable that can hold many dierent types of values. Object-
oriented languages often restrict the types of values to being subclasses of the declared
type of the variable.
primitive An operation that cannot be performed in the programming language and must
be accomplished with the aid of the underlying run-time system.
private method A method that is not intended to be invoked from outside an object.
More specically, the receiver for the message that invokes a private method should
always be the receiver for the method in which the invocation is taking place (see self).
Contrast with public method.
366 GLOSSARY

procedure call The transfer of control from the current point in execution to the code
associated with a procedure. Procedure calling diers from message passing in that
the selection of code to be transferred to is decided at compile time (or link time)
rather than run time.
protocol See class description protocol.
pseudo-variable A variable that is never declared but can nevertheless be used within
a method, although it cannot be directly modied (a pseudo-variable is therefore by
denition read-only). The most common pseudo-variable is used to represent the
receiver of a method. See this, and super.
public class A class that is global and can be accessed from other packages. One public
class may be declared in each compilation unit.
public method A method that can be invoked at any time from outside an object.
pure polymorphism A feature of a single function that can be executed by arguments of
a variety of types. See ad hoc polymorphism.
rapid prototyping A style of software development in which less emphasis is placed on
creation of a complete formal specication than on rapid construction of a prototype
pilot system, with the understanding that users will experiment with the initial system
and suggest modications or changes, probably leading to a complete redevelopment
of a subsequent system. See exploratory programming.
receiver The object to which a message is sent. The receiver is the object to the left of
the eld qualier (period). Within a method, the current receiver is indicated by the
variable this.
redenition The process of changing an inherited operation, to provide dierent or ex-
tended behavior.
renement A style of overriding in which the inherited code is merged with the code
dened in the child class.
replacement A style of overriding in which the inherited code is completely replaced by
the code dened in the child class.
responsibility-driven design A design technique that emphasizes the identication and
division of responsibilities within a collection of independent agents.
scope When applied to a variable identier, the (textual) portion of a program in which
references to the identier denote the particular variable.
selector See message selector.
GLOSSARY 367

shadowed name A name that matches another name in a surrounding scope the new
name eectively makes the surrounding name inaccessible. An example is a local
variable with the same name as that of a global or instance variable. Within the
procedure, the local variable will be attached to all references of the name, making
references to the surrounding name dicult. In Java access to such values can be
provided by a fully qualied name.
single-assignment variable A variable the value of which is assigned once and cannot be
redened. In Java single assignment variables can be created using the keyword nal.
static A declaration modier that, when applied to instances variables and functions, means
that the variables and functions are shared by all instances of a class, and exist even
when no instances have yet been created.
static method A method that is declared static. Since such functions exist even when no
instances have been created, they can be invoked using the class name as receiver.
strongly typed language A language in which the type of any expression can be deter-
mined at compile time.
subclass Syn. descendant type, derived class, child class.
subclass coupling The connection formed between a parent and child class. Subclass
coupling is a very weak form of coupling, since instances of the subclass can be treated
as though they were simply instances of the parent class. See coupling and cohesion.
substitutability, principle of The principle that asserts one should be able to substitute
an instance of a child class in a situation where an instance of the parent class is
expected. The principle is valid if the two classes are subtypes of each other, but not
necessarily in general.
subtype A type A is said to be a subtype of a type B if an instance of type A can be
substituted for an instance of type B with no observable eect. For example, a sparse
array class might be dened as a subtype of an array type. Subclasses need not be
subtypes, nor must subtypes be subclasses.
super When used inside a method, a synonym for self. However, when used as the receiver
for a message, the search for an appropriate method will begin with the parent class
of the class in which the current method is dened.
superclass Syn. ancestor class, base class. A class from which another class inherits
attributes.
this When used inside a method, a reference to the receiver for the message that caused
the method to be invoked.
368 GLOSSARY

virtual machine An imaginary Java machine. Java programs are translated into assembly
language instructions for this imaginary machine. To execute a Java program, an
actual computer must simulate the working of the virtual machine.
void A type name used to indicate a function returning no value{that is, a procedure.
World Wide Web A collection of machines on the Internet that have agreed to distribute
information according to a common protocol. This information is usually accessed
with a browser.
yo-yo problem Repeated movements up and down the class hierarchy may be required
when the execution of a particular method invocation is traced.
Bibliography
Actor 1987] Actor Language Manual, The Whitewater Group, Inc., Evanston, IL,
1987.
Beck 1989] Kent Beck and Ward Cunningham, \A Laboratory for Teaching
Object-Oriented Thinking," Proceedings of the 1989 OOPSLA|
Conference on Object-Oriented Programming Systems, Languages and
Applications Reprinted in Sigplan Notices, 24(10): 1{6, 1989.
Bellin 97] David Bellin and Susan Suchman Simone, The CRC Card Book,
Addison-Wesley, Reading, MA, 1997.
Budd 97] Timothy A. Budd, An Introduction to Object-Oriented Programming,
2nd Ed, Addison-Wesley, Reading, MA, 1997.
Cardelli 1985] Luca Cardelli and Peter Wegner, \On Understanding Types, Data
Abstraction, and Polymorphism," Computing Surveys, 17(4): 471{
523, 1985.
Chan 96] Patrick Chan and Rosanna Lee, The Java Class Libraries: An Anno-
tated Reference, Addison-Wesley, Reading, MA, 1996.
Cox 1986] Brad J. Cox, Object Oriented Programming: An Evolutionary Ap-
proach, Addison-Wesley, Reading, MA, 1986.
Cox 1990] Brad J. Cox, \Planning the Software Industrial Revolution," IEEE
Software, 7(6): 25{35, November 1990.
Dahl 1966] Ole-Johan Dahl and Kristen Nygaard, \Simula, An Algol-Based Sim-
ulation Language," Communications of the ACM, 9(9): 671-678,
September 1966.
Danforth 1988] Scott Danforth and Chris Tomlinson, \Type Theories and Object-
Oriented Programming," ACM Computing Surveys, 20(1): 29{72,
1988.
369
370 BIBLIOGRAPHY

Gabriel 1996] Richard P. Gabriel, Patterns of Software, Oxford University Press,


New York, 1996.
Gamma 1995] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides,
Design Patterns: Elements of Reusable Object-Oriented Software,
Addison-Wesley, Reading, MA, 1995.
Gosling 96] James Gosling, Bill Joy and Guy Steele, The Java Language Speci-
cation Addison-Wesley, Reading, MA, 1996.
Horowitz 1984] Ellis Horowitz, Fundamentals of Programming Languages, Computer
Science Press, Rockville, MD, 1984.
Ingalls 1981] Daniel H. H. Ingalls, \Design Principles Behind Smalltalk," Byte,
6(8): 286{298, 1981.
Kay 1977] Alan Kay, \Microelectronics and the Personal Computer," Scientic
American, 237(3): 230{244, 1977.
Kay 1993] Alan C. Kay, \The Early History of Smalltalk," The Second ACM
SIGPLAN History of Programming Languages Conference (HOPL-
II), ACM SIGPLAN Notices 28(3): 69-75, March 1993.
Keller 1990] Daniel Keller, \A Guide to Natural Naming," Sigplan Notices, 25(5):
95{102, May 1990.
Kim 1989] Won Kim and Frederick H. Lochovsky (Eds.), Object-Oriented Con-
cepts, Databases, and Applications, Addison-Wesley, Reading, MA,
1989.
Lindholm 97] Tim Lindholm and Frank Yellin, The Java Virtual Machine Speci-
cation, Addison-Wesley, Reading, MA, 1997.
MacLennan 1987] Bruce J. MacLennan, Principles of Programming Languages, Holt,
Rinehart & Winston, New York, 1987.
Marcotty 1987] Michael Marcotty and Henry Ledgard, The World of Programming
Languages, Springer-Verlag, New York, 1987.
Meyer 1988a] Bertrand Meyer, Object-Oriented Software Construction, Prentice-
Hall International, London, 1988a.
Micallef 1988] Josephine Micallef, \Encapsulation, Resuability and Extensibility
in Object-Oriented Programming Languages," Journal of Object-
Oriented Programming Languages, 1(1): 12{35, 1988.
BIBLIOGRAPHY 371

Milner 1990] Robin Milner, Mads Tofte, and Robert Harper, The Denition of
Standard ML, MIT Press, Cambridge, MA, 1990.
Morehead 1949] Albert H. Morehead and Georey Mott-Smith, The Complete Book
of Solitaire and Patience Games, Grosset & Dunlap, New York, 1949.
Pinson 1988] Lewis J. Pinson and Richard S. Wiener, An Introduction to Object-
Oriented Programming and Smalltalk, Addison-Wesley, Reading, MA,
1988.
Sethi 1989] Ravi Sethi, Programming Languages: Concepts and Constructs,
Addison-Wesley, Reading, MA, 1989.
Stroustrup 1988] Bjarne Stroustrup, \What is `Object-Oriented Programming?' "
IEEE Software, 5(3): 10{20, May 1988.
Taenzer 1989] David Taenzer, Murthy Ganti, and Sunil Podar, \Object-Oriented
Software Reuse: The Yoyo Problem," Journal of Object-Oriented Pro-
gramming, 2(3): 30{35, 1989.
Wegner 1986] Peter Wegner, \Classication in Object-Oriented Systems," Sigplan
Notices, 21(10): 173{182, October 1986.
Wikstr%om 1987] &
Ake Wikstr%om, Functional Programming Using Standard ML,
Prentice-Hall International, London, 1987.
Wirfs-Brock 1989b] Rebecca Wirfs-Brock and Brian Wilkerson, \Object-Oriented De-
sign: A Responsibility-Driven Approach," Proceedings of the 1989
OOPSLA|Conference on Object-Oriented Programming Systems,
Languages and Applications Reprinted in Sigplan Notices, 24(10):
71{76, October 1989.
Wirfs-Brock 1990] Rebecca Wirfs-Brock, Brian Wilkerson, and Lauren Wiener, Design-
ing Object-Oriented Software, Prentice-Hall, Englewood Clis, NJ,
1990.
Wulf 1972] William A. Wulf, \A Case Against the GOTO," Proceedings of the
Twenty-Fifth National ACM Conference, 1972 Reprinted in Edward
Yourdon (Ed) Classics in Software Engineering, Prentice-Hall, En-
glewood Clis, NJ, 1979.

You might also like