Professional Documents
Culture Documents
Copyrighted Material
A Little Java, A Few Patterns
Matthias Felleisen
Rice University
Houston, Texas
Daniel P. Friedman
Indiana University
Bloomington, Indiana
Copyrighted Material
Second printing. 2000
Felleisen, Matthias
A little Java, a few patterns. / Matthias Felleisen and Daniel P. Friedman;
drawings by Duane Bibby; foreword by Ralph E. Johnson
p. cm.
Includes index and bibliographical references.
ISBN 0-262-56115-8 (pbk : alk. paper)
1. Java (Computer program language) I. Friedman, Daniel P. II. Title.
QA 76. 73.J38F45 1998
005.13'3-dc21 97-40548
CIP
Copyrighted Material
To Helga, Christopher, and Sebastian.
To Mary, Rob, Rachel, Sara,
and to the memory of Brian.
Copyrighted Material
Contents
Foreword ix
Preface xi
Experimenting with Java xiii
1. Modern Toys 3
2. Methods to Our Madness 13
3. What's New? 43
4. Come to Our Carousel 57
5. Objects Are People, Too 69
6. Boring Protocols 85
7. Oh My! 99
8. Like Father, Like Son 117
9. Be a Good Visitor 139
10. The State of Things to Come 161
Commencement 177
Index 178
Copyrighted Material
FOREWORD
Learning to program is more than learning the syntactic and semantic rules of a programming
language. It also requires learning how to design programs. Any good book on programming
must therefore teach program design.
Like any other form of design, program design has competing schools. These schools are
often associated with a particular set of languages. Since Java is an object-oriented programming
language, people teaching Java should emphasize object-oriented design.
Felleisen and Friedman show that the functional ( input-output driven ) method of program
design naturally leads to the use of well-known object-oriented design patterns. In fact, they
integrate the two styles seamlessly and show how well they work together. Their book proves that
the functional design method does not clash with, but supports object-oriented programming.
Their success doesn't surprise me, because I've seen it in Smalltalk for many years, though
unfortunately, it seems to have remained one of the secrets of object-oriented design. I am
happy to see that Felleisen and Friedman have finally exposed it. This book will be especially
useful if you are a C++ programmer learning Java, since you probably haven't seen functional
program design before. If you know functional design, the book will gently introduce you to
pattern-based programming in Java. If you don't know it, Felleisen and Friedman will teach
you a powerful new way of thinking that you should add to your design toolbox.
Ralph E. Johnson
Champaign, Illinois
Copyrighted Material
ix
Preface
Java provides many useful features and libraries beyond its object-oriented core. While these
additional Java elements are important for professional programming, their coverage would
dist ract from the book's important goals: object-oriented programming and the use of design
patterns. For that reason, this book is not a complete introduction to Java. Still, readers
who master its contents can quickly become skilled Java programmers with the supplementary
sources listed in the Commencement.
The literature on design patterns evolves quickl y. Thus, there is quite a bit more to patterns
than an introductory book could intelligibly cover. Yet, the sim plicity of the patterns we use
and the power that they provide should encourage readers to study the additional references
about patterns mentioned at t he end of the book.
ACKNOWLEDGMENTS
We are indebted to many people for their contribu tions and assistance throughout the devel
opment of this book. Several extensive discussions with Shriram Krishnamurthi, Jon R ossie ,
Copyrighted Material
xi
and Mitch Wand kept us on track; their detailed comments deeply influenced our thinking at
critical junctures. Michael Ashley, Sundar Balasubramaniam, Cynthia Brown, Peter Drake,
Bob Filman, Robby Findler, Steve Ganz, Paul Graunke, John Greiner, Erik Hilsdale, Matthew
Kudzin, Julia Lawall, Shinn-Der Lee, Michael Levin, Gary McGraw, Benjamin Pierce, Amr
Sabry, Jonathan Sobel, and George Springer read the book at various stages of development and
their comments helped produce the final result. We also wish to thank Robert Prior at MIT
Press who loyally supported us for many years and fostered the idea of a "Little Java." The book
greatly benefited from Dorai Sitaram's incredibly clever Scheme typesetting program SJb.1EX.
Finally, we would like to thank the National Science Foundation for its continued support
and especially for the Educational Innovation Grant that provided us with the opportunity to
collaborate for the past year.
READING GUIDELINES
Do not rush through this book. Allow seven sittings, at least. Read carefully. Mark up the book
or take notes; valuable hints are scattered throughout the text. Work through the examples,
don't scan them. Keep in mind the motto "Think first, experiment later."
The book is a dialogue about interesting Java programs. After you have understood the
examples, experiment with them, that is, modify the programs and examples and see how they
behave. Since most Java implementations are unfortunately batch interpreters or compilers, this
requires work of a repetitive nature on your side. Some hints on how to experiment with Java
are provided on the following pages.
We do not give any formal definitions in this book. We believe that you can form your own
definitions and thus remember and understand them better than if we had written them out for
you. But be sure you know and understand the bits of advice that appear in most chapters.
We use a few notational conventions throughout the text to help you understand the
programs on several levels. The primary conventions concern typeface for different kinds of
words. Field and method names are in italic. Basic data, including numbers, booleans, and
constructors introduced via datatypes are set in sans serif. Keywords, e.g., class, abstract,
return and interface are in boldface. When you experiment, you may ignore the typefaces
but not the related framenotes. To highlight this role of typefaces, the programs in framenotes
are set in a typewriter face.
Food appears in many of our examples for two reasons. First, food is easier to visualize
than abstract ideas. ( This is not a good book to read while dieting. ) We hope the choice of
food will help you understand the examples and concepts we use. Second, we want to provide
you with a little distraction. We know how frustrating the subject matter can be, and a little
distraction will help you keep your sanity.
You are now ready to start. Good luck! We hope you will enjoy the experiences waiting for
you on the following pages.
Bon appetit!
Matthias Felleisen
Daniel P. Friedman
xii
Copyrighted Material
EXPERIMENTING WITH JAVA
2.To each class whose name does not end with a superscript V, V, I, or M, add a toString
method according to these rules:
a) if the class does not contain any fields, use
class Main {
public static void main(String args[ ]) {
DataType_or_Interface y new =
System.out.println( . . . . . . ); } }
experiment. T hen replace with the example expression that you would like to experiment
. . . . . .
with. For example, if you wish to experiment with the distanceToO method of ManhattanPt
as defined in chapter 2, add the following definition to the end of your file:
class Main {
public static void main(String args[ ]) {
PointD y = new ManhattanPt(2,8);
System.out.println( y.distanceToO() ); } }
lSee Arnold and Gosling [lJ for details on how they work. These hints make little sense out of context, so for
now, just follow them as you read this book.
y.
y.
y.- -
replace . . . . . . with
y.-
For example, if you wish to experiment with the methods of Pi emanM as defined in chapter 10,
add the following definition to the end of your file:
class Main {
public static void main(String args[ ]) {
PiemanI y = new PiemanM();
System.out.println (
y.addTop (new AnchovyO) + "\n" +
XIV
Copyrighted Material
A Little Java, A Few Patterns
Copyrighted Material
flo
�@�l� �
Copyrighted Material
Is 5 an integer? 1 Yes, it is.
2
Is this a number: -23? Yes, but we don't use negative integers.
Is this an integer: 5.32? 3 No, and we don't use this type of number.
Can you think of another boolean? 8 No, that's all there is to boolean.
9
What is int? A type.
10
What is boolean? Another type.
11
What is a type? A type is a name for a collection of values.
13
Can we make new types? We don't know how yet.
Modern Toys 3
Copyrighted Material
Draw the picture that characterizes the 14 Is this it?
essential relationships among the following
classes.
15
Yes. We say SeasoningV is a datatype, and Okay. But aren't all three classes introducing
Salt and Pepper are its variants. new types?
16
Yes, in a way. Now, is Yes, it is, because new SaitO creates an
new SaitO instance of Salt, and every instance of Salt is
also a SeasoningV
a Seasoningv?
17
And It's also a SeasoningV, because new PepperO
new PepperO? creates an instance of Pepper, and every
instance of Pepper is also a Seasoningv.
,.
What are abstract, class, and extends? Easy:
abstract class introduces a datatype,
class introduces a variant, and
extends connects a variant to a datatype.
19
Is there any other Seasoningv? No, because only Salt and Pepper extend
1
SeasoningV
4 Chapter 1
Copyrighted Material
20
Correct, Salt and Pepper are the only No, but boolean is a type that also has just
variants of the datatype Seasoningv. Have two values.
we seen a datatype like SeasoningV before?
21
Let's define more SeasoningVs. We can have lots of SeasoningVs.
22
And then there were four. Yes.
25
How do CartesianPt and ManhattanPt differ Each of them contains three things between
from Salt and Pepper? { and }. The X and the yare obviously the
I
coordinates of the points. But what is the
abstract class PointV {} remaining thing above the bold bar?1
Modern Toys 5
Copyrighted Material
6
The underlined occurrences of Cartesian Pt 2 How do we use these constructors?
and ManhattanPt introduce the constructors
of the respective variants.
28
When we create a CartesianPt like this: So now we have created a Cartesian Pt whose
x 2 and whose y field is 3. And
new CartesianPt(2,3), field is
because CartesianPt extends PointV, it is
we use the constructor in the definition of
also a Pointv.
Cartesian Pt.
Correct. Is this a Manhattan Pt: 29 Yes, and its x field is 2 and its y field is 3.
new Manhattan Pt(2,3)?
Isn't all this obvious? 30 Mostly, but that means we have used
constructors before without defining them.
How does that work?
31
When a class does not contain any fields, as And that's the constructor we used before,
in Salt and Pepper, a constructor is included right?
by default.
3
2
Yes, that's correct. Default constructors Good. But what is new PointVO?
never consume values, and, when used with
new, always create objects without fields.
6 Chapter 1
Copyrighted Material
34
Do the following classes define another Yes, they define a datatype and two variants.
datatype with variants?
OneMoreThan
class O n e M oreThan extends Numv {
Numv predecessor;
OneMoreTh an ( Num V _p ) {
predecessor = _Pi }
5
3
Is this a Numv: Obviously, just like new SaitO is a
Seasoningv.
new Zero()?
37
How does OneMoreThan do that? We give it new ZeroO, which is a Numv, and
it constructs a new Numv.
3
And what does it mean to construct this new 8 This new instance of OneMoreThan is a value
instance? with a single field, which is called
predecessor. In our example, the field is
new ZeroO.
39
Does predecessor always stand for an No, its type says that it stands for a N umv ,
instance of Zero? which , at the moment, may be either a Zero
or a OneMoreThan.
Modern Toys 7
Copyrighted Material
40
What is A Numv, because OneMoreThan constructs
new OneMoreThan( an instance from a Numv and we agreed
new OneMoreThan( that
new Zero()))? new OneMoreThan(
new ZeroO)
is a Numv.
41
What is That is nonsense,l because 0 is not a N u mv.
new OneMoreThan(
O)? 1 We use the word "nonsense" to refer to expressions for
which Java cannot determine a type.
Is new ZeroO the same as O? 42 No, 0 is similar to, but not the same as,
new ZeroO.
like
1?
And what is 44 4.
new OneMoreThan(
new OneMoreThan(
new OneMoreThan(
new OneMoreThan(
new Zero()))))
similar to?
45
Are there more N u mvs than booleans? Lots.
8 Chapter 1
Copyrighted Material
47
What is the difference between new Zero() Easy: new Zero() is an instance of Zero and,
and O? by implication, is a NumD, whereas 0 is an
into This makes it difficult to compare them,
but we can compare them in our minds.
48
Correct. In general, if two things are So are types just names for different
instances of two different basic types, they collections with no common instances?
cannot be the same.
The primitive types (int and boolean) are 49 What are non-basic types?
distinct; others may overlap.
0
Class definitions do not introduce primitive 5 And what is that?
types. For example, a value like new Zero()
is not only an instance of Zero, but is also a
NumD, which is extended by Zero. Indeed, it
is of any type that NumD extends, too.
Every class that does not explicitly extend 51 This must mean that everything is an Object.
another class implicitly extends the class
Object.
54
What is It looks like an instance of Base, which
newBase( means it is also a LayerD and an Object.
new Zero())?
56
They are, because everything created with Hence, we can use both
new is an Object, the class of all objects. new ZeroO
and
new SaitO
for the construction of a Base, which requires
an Object.
10 Chapter 1
Copyrighted Material
57
Is anythin g else an Object? We said that only things created with new
are O bjects . 1
58
Correct. Is this a LayerV ; 5 is not created with new, so this must be
new Base( nonsense.
5)?
59
;
V false is not created with new, so this must be
Is this a L ayer
new Base( nonsense, too.
false)?
60
Correct again! How about this Layerv: This must mean that Integer creates an
new Base( object from an into
new I nteger( 5))?
61
Guess how we create a LayerV from false? Easy now:
new Base(
new Boolean(false)).
62
Is it confusing that we need to connect int Too much coffee does that.
with Integer and boolean with Boolean?
63
Ready for more? Can't wait.
Modern Toys 11
Copyrighted Material
��{Q
��{!;�.2J�g \�
(�)l!1�:f�
Copyrighted Material
Remember points? Sure, we just talked about them. But what
are these labeled ovals about?
abstract class Point'D {
-- (� Point
----') --
}
---
y = -y; }
-- ( CartesianPt ) -
-- ( ManhattanPt )-
}
2
We will find out soon. Did you notice the big It must be for drawing the picture of the
white space on the right? classes.
What do the methods produce? i nts , which represent the distances to the
origin.
Point
int distanceToOO {
return L y'x2 + y2J \ }
C artesi anPt
int distanceToOO {
return x + Yi }
M a nhat tanPt
8
The labels remind us that we need to insert That's simple enough.
these methods into Point"D, CartesianPt, and
ManhattanPt.
9
defi ned Three times, but the first one differs from the
•
How many tImes have we the method
distance ToO? other two. It is labeled abstract, while the
others are not preceded by a special word.
14 Chapter 2
Copyrighted Material
10
Do abstract metho d s belong to th e Yes, they always do.
abstract class?
II
An abstract method in an abstract class Okay.
,
int rod uces an o bli gat ion which says that all
con crete classes that extend this abstract
class! mus t contain a matching method
definition.
12 7.
What is the value of
new ManhattanPt(3,4)
. distance ToOO?
14
What is the value of 5, because that is the value of
new CartesianPt(3,4) ";x2 + y2
. distance ToOO?
with x re pl aced by 3 and y replaced by 4.
15
What does l JXJ compute? The largest int that does not excee d the
square root of x.
16
Time for a short break? An apple a day keeps the dentist away. A
cup of coffee does not.
-- C,-_O_n_i_on_�)-
}
} -- C,-_L::.,::a=.m:..:.
b _ ) --
. _
}
-- ( Tomato )-
18
Did you notice the big space on the right? Yes, isn't it for drawing the picture of the
classes?
16 Chapter 2
Copyrighted Material
19
Construct a Shishv. How about
new SkewerO?
20
Yes, every Skewer is also a Shishv. How Here's one:
about another one? new Onion(
new Skewer() ) .
21
And a third? Here's one more:
new Onion(
new Lamb(
new Onion(
new Skewer()) ) ) .
22
Onions
Are there only on this Shishv : true, because there is neither Lamb nor
25
Is it true that true.
new Onion(
new Onion(
new Onion(
new Skewer())) )
contains only Onions?
26
And finally: false.
new Onion(
new Lamb(
new Onion(
new Skewer() ) ) ) ?
), ,;, true, false, return, and boolean. yet. Okay, you deserve a lollipop for
enduring this kind of question again.
1 A better name for these methods would be
nothingButOn1ona.
Here are the methods. 29 Yes. We said above that the labeled ovals in
the center of the blank lines in the above
abstract boolean onlyOnionsO; class definitions tell us where to put the
boxes with the corresponding labels.
Shish
: boolean onlyOnionsO {
return true; }
Skewer
boolean onlyOnionsO {
return s.onlyOnionsO; }
Onion
boolean onlyOnionsO {
return false; }
Lamb
boolean onlyOnionsO {
return false; }
Tomato
30
Good. How many methods have we defined? Five, but the first one is abstract; the
others are concrete.
18 Chapter 2
Copyrighted Material
31
Do abstract methods belong to the Yes, we said so.
abstract class?
32
Does each variant of Shishv contain a Yes, because Shishv contains an abstract
method called onlyOnions? method called onlyOnions that obligates
each variant to define a matching, concrete
method.
33
Is this always the case? Always.
34 • •
What do these concrete methods consume? Nothing, Just as the abstract method says.
35
What do these concrete methods produce? booleans, just as the abstract method says.
37
And how do we determine the value of We will need to pay attention to the method
new Onion( definitions.
new Onion(
new SkewerO»
.onlyOnions()?
38
Which definition of onlyOnions must we use This object is an instance of Onion, so we
to determine the value of need to use the definition of onlyOnions that
new Onion( belongs to the Onion variant.
new Onion(
new Skewer(»)
. onlyOnions ()?
41
Does s always stand for an Onion? No, it has type Shishv, and it can stand for
any variant of Shishv : Skewer, Onion, Lamb,
or Tomato.
..
20 Chapter 2
Copyrighted Material
45
Which definition of onlyOnions must we use This object is an instance of Onion, so we
to determine the value of need to use the definition of onlyOnions that
new Onion( belongs to the Onion variant.
new SkewerO)
.onlyOnionsO?
46
What follows the word return in the s. onlyOnions O.
onlyOnions method in Onion?
47
W hat is the field s of the object new SkewerO.
new Onion(
new Skewer())?
How do we determine the answer for 50 We need to determine one more time which
new Skewer O version of onlyOnions we must use.
.onlyOnionsO?
Skewer?
2
Then what is the answer? 5 true.
53
Why? Because true is what the onlyOnions method
in Skewer always returns.
true.
56
Which definition of only Onions must we use This object is an instanc e of Onion, so we
to determine the value of need to use the de fin iti o n
of onlyOnions that
new Onion( belongs to the Onion variant.
new Lamb(
new Skewer()))
.onlyOnionsO?
22 Chapter 2
Copyrighted Material
57
What follows the word return in the s. onlyOnions() .
onlyOnions method in Onion?
58
What is the field s of the object It is the object built from
new Onion( new Lamb(
new Lamb( new Skewer()).
new Skewer()))?
61
How do we determine the answer for We determine which version of onlyOnions
new Lamb( to use.
new Skewer())
.onlyOnions()?
62
And? We use the one that belongs to Lamb.
63
And now what is the answer? false, because false follows the word return
in the corresponding method definition in
Lamb.
65
Describe the methods (i. e., the function ) Here are our words:
only Onions in your own words. "The methods determine for a S hish v
whether its contents are edible by an onion
lover."
66
Describe how the methods (i.e., the Here are our words again:
function ) onlyOnions accomplish this. "For each layer of th e Shishv, except for
Onion, the corresponding method knows
whether it is go od or bad. The method for
Onion needs to determine whether the
remaining layers are only Onions sitting on
a Skewer."
Is 67 Yes.
new Tomato(
new SkewerO)
a Shishv?
68
Is The obj ect
new Onion( new Tomato(
new Tomato( new SkewerO)
new Skewer()))
is an instance of Shishv, so we can also wrap
a Shishv? an Onion around it.
24 Chapter 2
Copyrighted Material
Is 70 Of course, there is no Lamb on it.
new Tomato(
new Onion(
new Tomato(
new SkewerO»)
a vegetarian shish kebab?
71
And Yes, it is a vegetarian shish kebab, because it
new Onion( only contains Onions.
new Onion(
new Onion(
new Skewer())))?
72
Define the methods (i.e., the function) That's no big deal now.
is Vegetarian,
abstract boolean is Vegetarian 0;
which return true if the given object does not
contain Lamb. Shish
Hint: The method for tomatoes is the same
as the one for onions.
boolean isVegetarian 0 {
return true; }
Skewer
boolean isVegetarian 0 {
return s.isVegetarianOi }
Onion
boolean isVegetarian 0 {
return false; }
Lamb
boolean isVegetarian 0 {
return s.isVegetarianOi }
Tomato
74
Do abstract methods belong to the Yes, they always do.
abstract class?
75
Does each variant of Shishv contain a Yes, because Shishv contains an abstract
method called isVegetarian? method called isVegetarian.
What do these concrete methods consume? 77 Nothing, just as the abstract method says.
What do these concrete methods produce? 78 booleans, just as the abstract method says.
26 Chapter 2
Copyrighted Material
79
Collect all the pieces of Shishv. Here is the There are two methods per variant.
datatype.
class Skewer extends Shishv {
abstract class Shishv { boolean onlyOnionsO {
abstract boolean onlyOnions(); return true; }
abstract boolean isVegetarian(); boolean isVegetarian 0 {
} return true; }
}
boolean onlyOnionsO {
return s.onlyOnionsO; }
boolean is Vegetarian 0 {
return 8.isVegetarianO; }
}
boolean onlyOnions () {
return false; }
boolean is Vegetarian 0 {
return false; }
}
boolean onlyOnionsO {
return false; }
boolean isVegetarian 0 {
return s.isVegetarianO; }
}
0= O J }
_
-- C H_o_ld_e_
___ r _�)--
}
k = kj }
_
-- ( Shallot )-
}
-- ( Shrimp
)-
}
-- ( Radish
)-
}
28 Chapter 2
Copyrighted Material
81
W hat is different about them? They are placed onto different Holders.
82
Here are some holders. Sure, a rod is a kind of holder, and every rod
is an Object, so 0 in Holder can stand for any
I abstract class RodD {} rod. Is it necessary to draw another picture?
Think of another kind of holder. Are you 83 We could move all of the food to various
tired of drawing pictures, yet? forms of plates.
86
Is Sure, because Gold is a PlateV, PlateV is used
new Shallot( as a Holder, and radishes and shallots can be
new Radish( put on any Holder.
new Holder(
new Gold())))
a Kebabv?
87
Is Sure it is. It is basically like
new Shallot( new Shallot(
new Radish( new Radish(
new Holder( new Holder(
new Gold()))) new Dagger()))),
a vegetarian kebab? except that we have moved all the food from
a Dagger to a Gold plate.
8
Let's define the methods ( i. e., the function) 8 If you can, you may rest now.
is Veggie,
which check whether a kebab contains only
vegetarian foods, regardless of what Holder it
is on.
Kebab
30 Chapter 2
Copyrighted Material
90
The concrete methods are similar to those Except for the names of the methods and
called isVegetarian. Here are two more; fields, the definitions are the same as they
define the remaining two. were for Shishv
Holder Shrimp
Shallot Radish
92
What is It is a Kebabv, but we also know that it is an
new Shallot( instance of the Shallot variant.
new Radish(
new Holder(
new Dagger())))?
94
And what is It is also a Kebabv, because any kind of
new Shallot( Holder will do.
new Radish(
new Holder(
new Gold())))?
98
Does that mean is Veggie works for all five Yes, and all other kinds of Objects that we
kinds of Holders? could possibly think of.
new Shallot(
new Radish(
new H older(
new Dagger())))?
100
What is the holder of All the food is now on a Gold plate.
new Shallot(
new Radish(
new Holder(
new Gold() )))?
32 Chapter 2
Copyrighted Material
101
What is the holder of All the food is on an Integer.
new Shallot(
new Radish(
new Holder(
new Integer(52))))?
102
What is the value of The dagger.
new Shallot(
new Radish(
new Holder(
new DaggerO)))
·whatHolderO?
103
What is the value of The gold plate.
new Shallot(
new Radish(
new Holder(
new Gold())))
·whatHolder()?
104
What is the value of An Integer, whose underlying int is 52.
new Shallot(
new Radish(
new Holder(
new Integer(52))))
·whatHolderO?
105
What type of values do the methods (i.e., They produce rods, plates, and integers. And
the function ) of whatHolder produce? it looks like they can produce a lot more.
106
Is there a simple way of saying what type of They always produce an Object, which is also
values they produce? the type of the field of Holder.
107
Here is the abstract method whatHolder. If we add this method to KebabD, then we
must add a method definition to each of the
abstract Object whatHolder() four variants.
Kebab
109
What is the value of new SwordO.
new Holder(
new Sword())
.whatHolderO?
110
What is the value of It is b.
new Holder( b)
.whatHolderO
if b is some object?
111
Define the concrete method that goes into With these kinds of hints, it's easy.
the space labeled Holder.
Object whatHolderO {
return 0; }
Holder
112
What is the value of new Integer(52).
new Radish(
new Shallot(
new Shrimp(
new Holder(
new Integer(52)))))
.whatHolderO?
113
What is the value of new Integer(52).
new Shallot(
new Shrimp(
new Holder(
new Integer(52))))
.whatHolderO?
34 Chapter 2
Copyrighted Material
114
What is the value of new Integer(52).
ne w Shrimp(
new Holder(
new Integer(52)))
. whatHolderO?
115
Does that mean that the value of Yes, all four have the same answer:
new Radish( new Integer(52).
new Shallot(
new Shrimp(
new Holder(
new Integer(52)))))
.whatHolderO
is the same as
new Shallot(
new Shrimp(
new Holder(
new Integer(52))))
.whatHolderO,
which is the same as
new Shrimp(
new Holder(
new Integer(52)))
. whatHolder 0 ,
which is the same as
new Holder(
new Integer(52))
. whatHolderO?
116
Here is the method for Shallot. They are all the same.
Shallot Shrimp
Radish
boolean isVeggieO {
class Holder extends KebabD { return k. isVeggie 0 j }
Object OJ Object whatHolderO {
Holder( Object _0) { return k.whatHolderOj }
o OJ }
}
= _
boolean is VeggieO {
return truej } class Shrimp extends Kebabv {
Object whatHolderO { Kebabv k;
return 0; } Shrimp(KebabD _k) {
} k = kj }
_
boolean isVeggieO {
return k.isVeggieOj }
Object whatHolderO {
return k.whatHolderOj }
}
118
Are there any other Kebabv foods besides No, these are the only kinds of foods on a
Shallot, Shrimp, and Radish? Kebabv.
36 Chapter 2
Copyrighted Material
119
Can we add more foods? Sure. We did something like that when we
added Thyme and Sage to Seasoningv.
120
Let's define another Kebabv A concrete class that extends Kebabv must
define these two methods. That's what the
class Pepper extends Kebabv { abstract specifications say. We can define as
Kebabv k; many Kebabvs as we wish.
Pepper(KebabV _k) {
k = _k; } class Zucchini extends Kebabv {
Kebabv k;
boolean isVeggie 0 { Zucchini( KebabV _k) {
return k.isVeggieO; } k _k; }
=
Object whatHolderO {
return k.whatHolderO; } boolean isVeggieO {
} return k. is Veggie 0; }
Object whatHolderO {
W hy does it include isVeggie and whatHolder return k.whatHolderO; }
methods? }
121
Is it obvious how the new methods work? Totally. In both cases isVeggie just checks
the rest of the Kebabv, because green
peppers and zucchini are vegetables.
Similarly, whatHolder returns whatever
holder belongs to the rest of the Kebabv.
122
And then there were six. Yes, now Kebabv has six variants.
123
Which of these points is closer to the origin: The second one,
new ManhattanPt(1,5)?
12.
Good. Which of the following points is closer The first one, clearly. Its distance to the
to the origin: origin is 5, but the second distance is 13.
new CartesianPt(3,4)
or
new CartesianPt(12,5)?
126
What is the value of false.
new ManhattanPt(3,4)
.closerToO(new ManhattanPt(1,5»?
127
What is the value of true, obviously.
new ManhattanPt(1,5)
.closerToO(new ManhattanPt(3,4»?
128
What is the value of false, and true is the value of
new CartesianPt(12,5) new CartesianPt(3,4)
.closerToO(new CartesianPt(3,4»? .closerToO(new CartesianPt(12,5».
129
So finally, what is the value of That's nonsense.
new CartesianPt(3,4)
.closerToO(new ManhattanPt(1,5»?
38 Chapter 2
Copyrighted Material
130
Why? The method closerToO can only consume
CartesianPts, not ManhattanPts.
131
How can we fix that? We could replace
(CartesianPt p)
by
(PointD p)
in the definition of closerToO for CartesianPt.
132
Ifwe do that , can we st ill determine the Yes, because the definition of PointD
value of obl igates every variant to provide a method
p.distanceToO()? named distanceToO.
133
Why does it help to replace (CartesianPt p) EveryCartesianPt is a PointD, and every
by (PointD p)? ManhattanPt is a PointD, too.
13_
Here is the improved CartesianPt. Easy.
Y = -Yi } y = -Y; }
135
Is the definit ion of closerToO in CartesianPt Yes, they are identical.
•
What else do the two point variants have in 137 Their fields. Shouldn't we lift them, too?
common?
138
Yes. It's tricky, but here is a start. This not only lifts x and Y, it also introduces
a new constructor.
abstract class PointV {
int Xj
int Yj
PointV(int _x,int _y) {
x _Xj
=
Y -Yj }
=
boolean closerToO(PointV p) {
return
distanceToOO ::; p.distanceToOOj }
abstract int distanceToOOj
}
139
Absolutely. And we need to change how a Mimicking this change is easy. But what
CartesianPt is built. Define ManhattanPt. does sup er(_ x ,_ y) mean?
40 Chapter 2
Copyrighted Material
140
The expressions super(_x,_y) in the That's simple.
constructors CartesianPt and ManhattanPt
c reate a PointP with the appropriate fields,
and the respective constructor guarantees
that the point becomes a CartesianPt or a
ManhattanPt.
141
Do we now have everything that Yes, and those things that distinguish the
characterizes PointPs in the datatyp e? two var iants from each other reside in the
corresponding variants.
, ..
Is this a long chapter? Yes, let's have a short snack break.
Sausage(PizzaV -p) {
} �------
p = -Pi }
} -- Crust
�------
) -- }
-
-- (
----�
Cheese ) --
} --
(� _
O
_l_ i ve_ _�) --
-- ( Anchovy ) -
3
How about removing the anchovies? That would make it less salty.
Let's remove them. What is the value of It should be a cheese and olive pizza, like
new Anchovy( this:
new Olive( new Olive(
new Anchovy( new Cheese(
new Anchovy( new Crust())).
new Cheese(
new Crust())))))
.remA10?
Does remA belong to the datatype PizzaD 6 Yes, and it produces them, too.
and its variants?
44 Chapter 3
Copyrighted Material
7
Define the methods that belong to the five We didn't expect you to know this one.
variants. Here is a start.
Pizza
PizzaD remAO {
return new CrustO; }
Crust
Define the two methods that belong to Olive 8 The Olive and Sausage methods are similar
and Sausage. We've eaten the cheese already. to the Cheese method.
Cheese Olive
PizzaD remAO {
return new Sausage(p.remA()); }
Sausage
Explain why we use The cheese, the olives, and the sausages on
new Cheese . . . , the pizzas must be put back on top of the
new Olive , and
. . . pi zza that p.remAO produces.
new Sausage ...
when we define these methods.
10
The methods remA must produce a PizzaD , Yes, and now the methods of remA produce
so let's use new CrustO, the simplest PizzaD, pizzas without any anchovies on them.
for the method in Anchovy.
PizzaD remAO {
return new CrustO; }
Anchovy
What's New? 45
Copyrighted Material
11
Let's try it out on a small pizza: That's easy. The object is an Anchovy. So
new Anchovy( the answer is new CrustO.
new Crust())
.remAO·
I'
Is Absolutely, but what if we had more
new CrustO anchovies?
like
new Anchovy(
new CrustO)
without anchovy?
13
No problem. Here is an example: That's easy again. As before, the object is an
new Anchovy( Anchovy so that the answer must still be
new Anchovy( new CrustO.
new Crust()))
.remAO·
14
Okay, so what if we had an olive and cheese Well, this object is an Olive and its p stands
on top: for
new Olive( new Cheese(
new Cheese( new Anchovy(
new Anchovy( new Anchovy(
new Anchovy( new CrustO))).
new CrustO))))
.remAO?
15
Then, what is the value of It is the pizza that
46 Chapter 3
Copyrighted Material
16
What is the value of It is
new Cheese( new Cheese(p.remA()),
new Anchovy(
where p stands for
new Anchovy(
new CrustO)))
new Anchovy(
.remAO? new Anchovy(
new Crust())).
17
And what is the value of It is the pizza that
new Cheese( new Anchovy(
new Anchovy( newAnchovy(
new Anchovy( new Crust()))
new Crust())) .remAO
.remA())? produces, with cheese added on top.
18
Do we know the value of Yes, we know that it produces new CrustO.
new Anchovy(
new Anchovy(
new CrustO))
.remAO?
Does that mean that new CrustO is the 19 No, we still have to add cheese and an olive.
answer?
20
Does it matter in which order we add those Yes, we must first add cheese, producing
two toppings? new Cheese(
new Crust())
and then we add the olive.
21
So what is the final answer? It is
new Olive(
new Cheese(
new Crust())).
What's New? 47
Copyrighted Material
22
Let's try one more example: It should be a double-cheese pizza.
new Cheese(
new Anchovy(
new Cheese(
new CrustO»)
.remAO·
What kind of pizza should this make?
23
Check it out! The object is an instance of Cheese so the
value is
new Cheese(p.remAO)
wherep stands for
new Anchovy(
new Cheese(
new CrustO».
24
Doesn't that mean that the result is Yes, it puts the cheese on whatever we get
new Cheese( for
new Anchovy( new Anchovy(
new Cheese( new
Cheese(
new CrustO» new Crust()))
.remA())? .remAO·
2
What about 5 Now the object is an anchovy.
new Anchovy(
new Cheese(
new Crust(»)
.remAO?
6
2
And the answer is Yes, but we need to add cheese on top.
new CrustO?
21
Does that mean the final answer is Yes, though it's not the answer we want.
new Cheese(
new Crust(»?
48 Chapter 3
Copyrighted Material
2S
What do we want? A double-cheese pizza like
new Cheese(
new Cheese(
new Crust())),
29
Which remA method do we need to change The one in Anchovy.
to get the cheese back?
Pizzav remAO {
return p.remAO; }
Anchovy
30
Does this remA still belong to Pizzav? Yes, and it still produces them.
31
We could add cheese on top of the anchovies. Yes, that would hide their taste, too.
32
What kind of pizza is Easy, it adds cheese on top of each anchovy:
new Olive( new Olive(
new Anchovy( new Cheese(
new Cheese( new Anchovy(
new Anchovy( new Cheese(
new Crust())))) new Cheese(
.topAwC10? new Anchovy(
new Crust())))))).
1 A better name for these methods would be
topAnchovyvi thCheese.
33
Did you notice the underlines? Yes, they show where we added cheese.
What's New? 49
Copyrighted Material
3.
And what is Here we don't add any cheese, because the
new Olive( pizza does not contain any anchovies:
new Cheese( new Olive(
new Sausage( new Cheese(
new Crust()))) new Sausage(
.topAwC()? new Crust()))).
35
Define the remaining methods. We expect you to know some of the answers.
:
Pizzav topAwCO {
Pizzav topAwCO {
return new Crust(); }
return new Olive(p.topAwC()); }
Crust
Olive
Pizzav topAwCO {
return new Sausage(p.topAwC())j }
Sausage
36
Take a look at this method. With that definition, topAwC would give the
same results as remA. The method topAwC
Pizzav topAwCO { in Anchovy must put the anchovy back on
return p.topAwC(); } the pizza and top it with cheese.
Anchovy
50 Chapter 3
Copyrighted Material
37
How many layers of cheese are in the result One, because remA removes all the
of anchovies, so that topAwC doesn't add any
( new Olive( cheese.
new Anchovy(
new Cheese(
new Anchovy(
new Crust()))))
.remA())
.topAwC()?
38
How many occurrences of cheese are in the Three, because topAwC first adds cheese for
result of each anchovy. Then remA removes all the
( new Olive( anchovies:
new Anchovy( new Olive(
new Cheese( new Cheese(
new Anchovy( new Cheese(
new Crust())))) new Cheese(
. topAwC()) new Crust())))) .
.remA()?
Perhaps we should replace every anchovy 39 We just did something like that.
with cheese.
Is it true that for each anchovy in x 40 Yes, and it does more. Once all the cheese is
So 41 Aha!
x. topAwC() . remA()
is a way to substitute all anchovies with
cheese by looking at each topping of a pizza
and adding cheese on top of each anchovy
and then looking at each topping again,
including all the new cheese, and removing
the anchovies.
What's New? 51
Copyrighted Material
Here is a different description: 42 Here is a skeleton.
"The methods look at each topping of a
Pizza Cheese
Pizza!) subAbCO {
return new Ofive(p.subAbCO); }
Olive
Pizza:/) subAbCO {
return ; }
Anchovy
Pizza!) subAbCO {
return new Sausage(p.subAbC())j }
43
Does this skeleton look familiar? Yes, this skeleton looks just like those of
topAwC and remA.
Pizza:/) subAbC() {
return new Cheese(p.subAbC())j }
Anchovy
52 Chapter 3
Copyrighted Material
Collection time. 1 45 The classes are getting larger.
}
PizzaD remAO {
return new Olive(p.remA())j }
class Crust extends PizzaD { PizzaD topAwCO {
Pi zzaD remAO { return new Olive(p.topAwC()); }
return new C ru stOj }
PizzaD subAbCO {
PizzaD topAwCO { return new Olive(p.subAbC()); }
return new CrustO ; }
PizzaD subAbCO { }
return new CrustO; }
PizzaD remAO {
return new Sausage(p.remA()); }
PizzaD topAwCO {
return new Sausage(p.topAwC()); }
PizzaD subAbCO {
return new Sausage(p.subAbC()); }
MIat's New? 53
Copyrighted Material
46
Let's add more Pizzav foods. Good idea.
47
Here is one addition: Spinach. Yes, we must define three concrete methods
for each variant of Pizzav
class Spinach extends Pizzav {
Pizzav p;
Spinach(PizzaV -p) {
p -p; }
=
Pizzav remAO {
return new Spinach(p.remA()); }
Pizzav topAwCO {
return new Spinach(p.topAwC()); }
Pizzav subAbCO {
return new Spinach(p.subAbC()); }
}
8
4
po we need to change Pizzav, Crust, Cheese, No. When we add variants to the datatypes
Olive, Anchovy, or Sa u sage? we have defined, we don't need to change
what we have.
49
Isn't that neat? Yes, this is a really flexible way of defining
classes and methods. Unfortunately, the
more things we want to do with Pizzavs, the
more methods we must add.
50
True enough. And that means cluttered That would be great, because these
classes. Is there a better way to express all definitions are painful to the eye. But we
this? don't know of a better way to organize these
definitions yet.
And now you can replace anchovy with 52 We will stick with anchovies.
whatever pizza topping you want.
54 Chapter 3
Copyrighted Material
�.� .
� (()
Copyrighted Material
Wasn't this last collection overwhelming? It sure was. We defined seven classes and
each contained three method definitions.
Why does that become overwhelming? Because it becomes more and more difficult
to understand the rationale for each of the
methods in a variant and what the
relationship is between methods of the same
name in the different variants.
Correct. Let's do something about it. Take a 4 These methods look familiar. Have we seen
close look at this visitor class. them before?
class OnlyOnionsv {
boolean jorSkewerO {
return true; }
boolean jorOnion(ShishV s) {
return s.onlyOnionsO; }
boolean jorLamb(ShishV s) {
return false; }
boolean jorTomato(ShishV s) {
return false; }
}
Almost. Each of them corresponds to an That's right. The major difference is that
onlyOnions method in one of the va ria nt s of they are all in one class, a visitor, whose
Shishv. name is OnlyOnionsv.
Is only Onions different from OnlyOnionsV? The former is used to name methods, the
latter names a class.
Those methods that would have the same If we could do that, it would be much easier
name if we placed them into the variants of a to un derst and what action these methods
dat atype in one class. perform .
10
That's what we are about to do. We are It's about time.
going to separate the action from the
datatype.
11
What is t he difference between the method Everything following return is the same.
onlyOnions in the Onion variant and t he
method for Onion in t he visitor OnlyOnionsV?
12
Right. W hat is the difference? The difference is that onZyOnions in Onion is
followed by 0 and that forOnion in
OnlyOnionsv is followed by (ShishV s).
13
Yes, that is the difference. Are the other for Indeed, they are.
methods in OnlyOnionsv related to their
counterparts in the same way?
IS
The boring part tells us how to make all of True, we still don ' t know how to make
this work . Shishv and its variants work with this visitor
class, which contains all the action.
58 Chapter 4
Copyrighted Material
16
Now take a look at this. This is a strange set of definitions. All the
only Onions methods in the variants look
abstract {
class Shishv alike. Each of them uses an instance of
I OnlyOnionsv ooFn new OnlyOnionsV();
= I OnlyOnionsv, which is created in the
} matching name.
boolean oniyOnionsO {
return ooFn.forOnion(s}; }
}
boolean onlyOnionsO {
return ooFn.forLamb(s); }
}
boolean onlyOnionsO {
return ooFn.forTomato(s); }
}
1S
That's what "consumption" is all about. Nothing, because a skewer is the basis of a
And what does the forSkewer method in shish and therefore has no fields.
Skewer consume?
19
So what does the ( ShishV s) mean in the It is always the rest of the shish, below the
definition of forOnion? top layer, which is an onion. In other words,
it is everything but the onion.
20
Very good. The notation ( ShishV s) means That makes sense and explains
thatforOnion consumes a Shishv and that s.onlyOnionsO·
between { and }, s stands for that shish.
21
Explain s.onlyOnionsO. Here are our words:
"s is a Shishv, and therefore s.onlyOnionsO
determines whether what is below the
onion is also edible by an onion lover."
22
Explain ooFn.forOnion(s). You knew we wouldn't let you down:
"ooFn.forOnion says that we want to use
the method we just described. It consumes
a Shishv, and s is the Shishv that
represents what is below the onion."
2.
And how do we determine that value with We start w ith the only Onions method in
these new definitions? Onion, but it immediately uses the forOnion
method on the rest of the shish.
60 Chapter 4
Copyrighted Material
And what does the for Onion method do? 25 It checks whether the rest of this shish is
edible by onion lovers.
26
How does it do that? It uses the method only Onions on s.
27
Isn't that where we started from? Yes, we're going round and round.
28
Welcome to the carousel. Fortunately, the shish shrinks as it goes
around, and when we get to the skewer we
stop.
9
And then the ride is over and we know that 2 That's exactly it.
for this example the answer is true.
Do we need to remember that we are on a 3D No! Now that we understand how the
carousel? separation of data and action works, we only
need to look at the action p art to understand
how things work.
Is one example enough? 31 No, let's look at some of the other actions on
shishes and pizzas.
boolean onlyOnionsO {
return ooFn.forOnion(s}; }
� boolean is Vegetarian 0 {
eturn ivFn.forOnion(s); }
s _s; }
=
boolean onlyOnionsO {
return ooFn.forLamb(s); }
boolean is Vegetarian 0 {
return ivFn.forLamb(s}; }
}
boolean o nlyOnionsO {
return ooFn.forTomato ( s); }
boolean is VegetarianO {
return ivFn.forTomato ( s); }
}
62 Chapter 4
Copyrighted Material
3.
That's why we call this part boring. True, the re ' s very little to think about. It
could be done automatically.
How do we define the visitor? 35 Does that refer to the class that contains the
actions?
36
Yes, that one. Define the visitor. It is like OnlyOnionsv except for the method
forTomato.
class IsVegetarian v {
boolean forSkewerO {
return true; }
boolean forOnion(ShishV s) {
return s. is Vegeta rian (); }
boolean forLamb(ShishV s) {
return false; }
boolean forTomato(ShishV s) {
return s. is Vegetarian(); }
}
37
Are we moving fast? Yes, but there are only a few interesting
parts. The protocol is always the same and
boring; the visitor is always closely related to
what we saw in ch apter 2.
new Cheese(
new Crust())))) I class extends Pi z v { }
Crust z a
a shish kebab?
class Cheese extends P izzav {
Pizzav p;
Cheese(PizzaV -p) {
P -Pi }
=
40
So what do we do next? We can define the protocol for the methods
that belong to Pizzav and its extensions:
remA, topAwC, and subAbC.
64 Chapter 4
Copyrighted Material
Great! Here is the ab s ract
t portion of the 41 How innovative! The variants are totally
protocol. mindless, now.
-l
abstract class PizzaD { class Olive extends PizzaD {
RemAv remFn new R emAv O ;
= PizzaD p;
TopAwCv topFn new TopAwCvO;
= Olive(PizzaD -p) {
SubAbCv subFn = new SubAbCvO; p -p; }
=
43
Okay, here is R e m Av. By now, even this is routine.
••
The last one, SubAbCv, is a piece of cake. And we thought we were working with a
pizza pie.
class SubAbCv {
Pizzav forCrustO {
return new CrustOj }
Pizzav for Cheese(PizzaD p) {
return new Cheese(p.subAbC()); }
Pizzav forOlive(PizzaV p) {
return new Olive(p.subAbC())j }
Pizzav forAnchovy(PizzaD p) {
return new Cheese(p.subAbC()); }
PizzaD forSausage(PizzaV p) {
return new Sausage(p.subAbC())j }
}
66 Chapter 4
Copyrighted Material
�o
@�EvJJ@@{S� �Jf��
lP��@Yrr�Il� [) �@)@
"L-J
.' '7
7 .
� 7
-
--
�
Copyrighted Material
Have we seen this kind of definition before?l 1 What? More pizza!
r = _r; }
-( �--=----�
Top )-
}
Yes, still more pizza, but this one is different. Yes, it includes only one variant for adding
toppings to a pizza, and toppings are Objects.
What kind of toppings can we put on these Any kind, because Object is the class of all
kinds of pizza? objects. Here are some fish toppings.
Is it true that the value of Yes. The pizza that comes out is the same as
new Top(new Sa lmonO , the one that goes in, because there are no
new Top(new T u naO , anchovies on that pizza.
new BotO))
.remAO
is
new Top(new SalmonO,
new Top(new T unaO ,
new Bot()))?
7
Does remA belong to PieD? Yes, and it produces pizza pies.
Top
70 Chapter 5
Copyrighted Material
Great. Isn't that easy? 9 Easy and boring.
10
What part of this exercise differs from Determining how many fields a variant
datatype to datatype? contains. In our case, we had zero and two.
11
Anything else? No, from that we know that raFn.forBot is
followed by 0 and raFn.forTop by ( t , r) .
12
Why (t,r)? Because these are the fields of Top.
13
Let's define the visitor Re m A v. Here are some guesses.
15
And what does We could guess:
16
Not yet. It depends on what equals means. What?
IS
Yes! And what is the value of false,
new AnchovyO.equals(new Tuna())? because no anchovy is a tuna.
19
The class Object contains a method called If we know that equals's answer is always
equals. This method compares one Object to false, why bother to use it?
another, and it always returns false.l
21
For Fishv and its variants it works like this. Assuming that
72 Chapter 5
Copyrighted Material
22
Aren't they? Is every value constructed with Yes. Every such value is an Object, because
new an instance of Object? every class extends Object directly or
indirectly.
23
Ifclass A extends B, is every value created Yes, and of the class that B extends and so
by new A( ... ) an instance of class B? on.
25
Yet the value of Of course, because an anchovy is never a
26
Could we have written RemAv without using Absolutely, instanceof is enough.
equals?
class RemA v {
Piev JorBotO {
return new BotO; }
Piev JorTop(Object t,Piev r) {
if (t instanceof Anchovy)
return r.remA()j
else
return new Top(t,r.remAO); }
}
27
Easy, because we want to generalize RemA v We can do that, but when we use the
so that it works for any kind of fish topping. methods of the more general visitor, we need
to say which kind of fish we want to remove.
28
What are good names for the more general How about remFish and Rem Fish v?
methods and visitor?
30
Add the protocol for RemFishv. We designed The rest is routine.
the abstract portion.
Piev remFish(FishV f) {
RemFishV rfFn new RemFishvO;
=
return rfFn.forBot(f); }
abstract Piev remFish(FishV f);
Bot
Pie
Piev remFish(FishV f) {
return rfFn.forTop(t,r, f); }
Top
31
Where do (f) and (t,r,J) come from? The f stands for the Fish v we want to
remove in both cases. The t and the r are
the fields of Top; Bot doesn't have any.
3.
Let's define RemFishv and its two methods. Instead of comparing the top layer t of the
pizza to Anchovy, we now determine whether
it equals the Fishv f, which is the additional
value consumed by the method.
class RemFishv {
PieV forBot(FishV f) {
return new BotO; }
PieV jorTop(Object t,PieV r,FishV f) {
if (t. equals ( t))
return r.remFish(f);
else
return new Top(t,r.remFish(f)); }
}
33
If we add another kind of fish to our Nothing, we just have to remember to add
datatype, what would happen to the equals to the new variant.
definition of RemFishv?
74 Chapter 5
Copyrighted Material
34
Let's try it out with a short example: The object is a topping, so we use forTop
new Top(new AnchovyO, from Rem Fishv.
new Bot())
.remFish(new Anchovy()).
5
3
Yes. What values does forTop consume? It consumes three values: new AnchovyO,
which is t, the top-most layer of the pizza;
new BotO, which is r, the rest of the pizza;
and new AnchovyO, which is f, the Fishv to
be removed.
3
And now? 6 Now we need to determine the value of
if (f. equals(t))
return r.remFish(f);
else
return new Top(t,r.remFish(f));
where t, r, and f stand for the values just
mentioned.
37
So? Given what f and t stand for, f.equals(t) is
true. Hence, we must determine the value of
r. remFish(f) .
39
What does forBot in Rem Fish v produce? It produces new BotO, no matter what f is.
42
What does remlnt do? Itremoves Integers from pizza pies just as
44
Define the visitor RemlntV Wonderful! We do the interesting thing first.
This visitor is almost identical to Rem Fishv
We just need to change the type of what the
two methods consume.
class RemlntV {
Piev forBot(lnteger i) {
return new BotO; }
Piev JorTop(Object t,Piev r,lnteger i) {
--
if (i.equals(t))
return r.remlnt(i);
else
return new Top(t,r.remlnt(i)); }
}
Does it matter that this definit ion uses i and 45 No, i is just a better name than J, no other
not J? reason. As long as we do such substitutions
systematically, we are just fine.
46
Where is the protocol? It is so simple, let's save it for later.
76 Chapter 5
Copyrighted Material
Can we remove Integers from PieDs? 47 Yes.
48
Can we remove FishD from PieDs? Yes, and we use nearly identical definitions.
49
Let's combine the two definitions. If we use Object instead of the underlined
Integer above, everything works out.
50
Why? Because everything constructed w i th new is
an Object.
51
Just do it! It's done.
class Re m v {
PieD jorBot( Object 0 ) {
return new Bot(); }
PieD jorTop(Object t,PieD r,Object 0 ) {
--
if (o.equals(t))
return r.rem(o);
else
return new Top(i,r.rem(o)); }
}
53
You never know when it might be useful, Let's just consider Rem v
even if it does not contain any interesting
information.
54
Why not Rem Fishv and RemA v and They are unnecessary once we have Rem v
Remlntv?
Let's remove some things from pizza pies: 56 Works like a charm with the same result as
58
Next: No problem. This, too, removes 3 and leaves
new Top(new AnchovyO, the other layers alone:
new Top(new Integer(3), new Top(new AnchovyO,
new Top(new ZeroO, new Top(new ZeroO,
new Bot()))) new Bot())) .
. rem(new Integer(3)).
78 Chapter 5
Copyrighted Material
What is the value of 59 Oops. The answer is
new Top(new AnchovyO, new Top(new AnchovyO,
new Top ( new Integer(3) , new Top(new Integer(3),
new Top(new ZeroO, new Top(new ZeroO,
new 8ot()))) new 8ot()))) .
. rem(new Zero())?
60
What's wrong with that? We expected it to remove new ZeroO from
the pizza.
61
And why didn't it? Because equals for Numvs uses Object's
equals, which always produces false-as we
discussed above when we introduced equals.
62
Always? Unless we define it anew for those classes
whose instances we wish to compare.
63
Here is the version of Numv ( including Adding equals to Zero is easy. We use
OneMoreThan) with its own equals. Define instanceof to determine whether the
the new Zero variant. consumed value is a new ZeroO.
66
Correct. What do we know after if has We know that o's type is Object and that it
determined that is an instance of OneMoreThan.
(0 instanceof OneMoreThan)
is true?
67
Precisely. So what does ((OneMoreThan)o) It converts the type of 0 from Object to
do? OneMore Th a n .
6S
What is ((OneMoreThan) o ) s type?
' Its type is OneMoreThan, and now it makes
sense to write
((OneMoreThan) o).predecessor.
69
Are 0and ((OneMoreThan)o) The underlying object is the same. But no,
interchangeable? the two expressions are not interchangeable,
because the former's type is Object, whereas
the latter's is OneMoreThan.
Did you also notice the 71 How do the two uses of predecessor differ?
predecessor
. equals (
(( OneMoreTha n) 0). predecessor)
in equals for OneMoreThan?
72
The first one, predecessor, refers to the So the second one,
predecessor field of the instance of ((OneMoreThan) o).predecessor,
OneMoreThan on which we are using equals. refers to the predecessor field of the instance
And that field might not be a OneMoreThan. of OneMoreThan consumed by equals.
80 Chapter 5
Copyrighted Material
73
Yes. Are these two objects equal? If they are similar1 to the same int, they are
equal. But most of the time, they are not.
74
Time for lunch? That's just in time.
75
Did you have a good lunch break? Yes, thank you.
76
Now what is the value of Now we get
new Top(new AnchovyO, new Top(new AnchovyO,
new Top(new Integer(3), new Top(new Integer(3),
new Top(new Z eroO ,
new Bot() )) ,
80
What is the value of It is the same pizza pie with all the anchovies
new Top(new AnchovyO, replaced by salmon:
new Top(new TunaO, new Top(new SalmonO,
new Top(new AnchovyO, new Top(new TunaO,
new Bot()))) new Top(new SalmonO,
.substFish(new SalmonO, new Bot()))).
new Anchovy())?
81
What kind of values does substFish consume? It consumes two fish and works on PieDs.
83
What is the value of It is the same pizza pie with all 3s replaced
new Top(new Integer(3), by 5s:
new Top(new Integer(2), new Top(new Integer(5),
new Top(new Integer(3), new Top(new Integer(2),
new 80t()))) new Top(new Integer(5),
.substInt(new Integer(5), new 80t()))).
new Integer(3))?
8.
What kind of values does substInt consume? It consumes two Integers and works on PieDs.
85
And what does it produce? It always produces a PieD
86
We can define SubstFishv. To get from SubstFishv to SubstlntV, we just
need to substitute FishD by Integer
class SubstFishv { everywhere and 'Fish" by "Int" in the class
PieD JorBot (FishD n,FishD 0) { and method names.
return new 80tO; }
PieD JorTop(Object t, class SubstlntV {
PieD r, PieD JorBot(lnteger n,lnteger 0) {
FishD n, return new 80tO; }
FishD 0) { PieD JorTop(Object t,
if (0. equals (t)) PieD r,
return new Top(n,r.substFish(n,o)); Integer n,
else Integer 0) {
return new Top(t,r.substFish(n,o)); } if (o.equals(t))
} return new Top(n,r.substInt(n,o));
else
Define Substlntv. return new Top(t,r.substInt(n,o)); }
}
87
Did we forget the boring parts? Yes, because there is obviously a more
general version like Rem v
82 Chapter 5
Copyrighted Material
88
Yes, we call it Substv. D efine it. We substitute Object for Fishv and Integer.
class SubstV {
Piev JorBot(Object n,Object 0) {
return new BotOj }
Piev JorTop(Object t,
Piev r,
Object n,
Object 0) {
if (0. equals ( t))
return new Top(n,r.subst(n,o))j
else
return new Top(t,r.subst(n,o))j }
}
89
Now it is time to add the protocol for SubstV The abstract part is obvious.
to Piev Here are the variants.
abstract class Piev {
class Bot extends Piev { Rem v remFn new Rem v 0 j
=
r = _rj }
P i ev rem (Object 0) {
return remFn.forTop(t,r,o)j }
Piev subst(Object n,Object 0) {
return substFn.forTop(t,r, n, o)j }
}
90
So? That was some heavy lifting.
But, of course they are not. We just didn't Okay, here are the variants again.
want to spend much time on them. Let's
take a closer look at the last one we defined class Bot extends Piev {
in the previous chapter. Piev rem(Object 0) {
return remFn.forBot(0); }
abstract class Piev { Piev subst(Object n,Object 0) {
Rem v remFn new Rem v 0;
= return substFn.forBot(n,o); }
SubstV substFn new SubstVO;
=
}
abstract Piev rem(Object 0);
abstract Piev subst(Object n,Object 0);
class Top extends Piev {
}
Object t;
Piev r;
Top(Object _t,Piev _r) {
t = _t;
r _r; }
=
Piev rem(Object 0) {
return remFn.jorTop(t,r,o); }
Piev subst(Object n,Object 0) {
return substFn.jorTop(t,r,n, 0) ; }
}
What is the difference between rem and The first one consumes one Object, the
subst in Piev? second one consumes two.
What is the difference between rem and Simple: rem asks for the
forBot service from
subst in the Bot variant? remFn Object it
and hands over the
consumes; subst asks for the forBot service
from substFn and hands over the two Objects
it consumes.
What is the difference between rem and Simpler: rem asks for the forTop service
subst in the Top variant? from remFn and hands over the field values
and the Object it consumes; subst asks for
the for Top service from substFn and hands
over the field values and the two Objects it
consumes.
Boring Protocols 85
Copyrighted Material
And that is all there is to the methods in the 6 But remFn and substFn defined in the
variants of a protocol. datatype are still a bit mysterious.
Let's not cre ate remFn and substFn in the This looks like an obvious modification. The
datatype. new rem and subst now consume a remFn
and a substFn , respectively. C an they still
abstract class PieD { find forBot and forTop, their corresponding
abstract PieD rem(Rem v remFn, carousel partners?
Object 0);
abstract PieD sub s t ( Subst V substFn,
Object n,
Object 0);
}
Yes, it is a straightforward trade-off. Instead The definition of the datatype says that they
of adding a remFn field and a substFn field are a Rem v and a Substv , respectively. And
to the datatype, we now have rem or subst every Remv defines forBot and f o r Top , and
consume such values. W hat kind of values so does every Substv .
are consumed by rem and subst?
Here is how it changes Top. In the same manner . We just need to change
each concrete method's description of what it
class Top extends PieD { consumes. The rest remains the same.
Object t;
PieD r; class Bot extends PieD {
Top(Object _t,PieD _ r ) { PieD rem(Rem v remFn,
t = _ t; Object 0) {
r = _r; } return remFn.forBot( 0); }
PieD subst ( Subst V substFn,
PieD rem(Rem v remFn, Object n,
Object 0) { Object 0) {
return remFn . fo r Top( t , r , o ) ; } return substFn.forBot(n,o); }
PieD subst(Substv substFn, }
Object n,
Object 0) {
return substFn.forTop(t,r,n,o); }
}
How does it affect Bot?
86 Chapter 6
Copyrighted Material
0
1
That's right. Nothing else changes in the We still have some work to do.
variants. Instead of relying on fields of the
datatype, we use what is consumed.
11
Like what? Consuming an extr a value here also affects
how the met hods rem and subst are used.
12
Where are they used? In Remv and SubstV, the interesting parts ,
for example.
Yes. Here is Rem v . 13 That takes all the fun out of it.
1
What is this all about? 4 Yes, what about it. Copying is easy.
16
How did we get here? The p rot ocol is that rem in Bot and Top asks
for the JorBot and JorTop methods of
remFn, respectively.
Boring Protocols 87
Copyrighted Material
How does that happen? 17 It happens with
re�JCn·for13ot( ... )
and
re�JCn·forTop( ... ) ,
respectively.
18
Correct. And now for13ot and forTop can Oh, so inside the methods of Rem v, this
refer to the object re�JCn as this. stands for precisely that instance of Rem v
that allowed us to use those methods in the
first place. And that must mean that when
we use r.re�(this, ... ) in forTop, it tells re�
to use the same instance over again.
20
Why? Because this is a Rem v, and it is exactly
what we need to complete the job.
21
What is the value of We did the same example in the preceding
new Top ( new AnchovyO , chapter, and the result remains the same.
new Top ( new Integer(3),
new Top ( new Z e roO ,
new BotO)))
.re� ( new RemvO,
new Z e ro ()) ?
22
And how does the underlined part relate to It creates a Rem v object, which corresponds
what we did there? to the remFn in the old Piev
23
What is the value of We did the same example in the preceding
new Top ( new Integer(3), chapter, and the result remains the same.
new Top ( new Integer(2),
new Top ( new Integer(3),
new Bot())))
.subst ( new SubstVO,
new Integer(5),
new Integer(3))?
88 Chapter 6
Copyrighted Material
24
And how does the underlined part relate to It creates a SubstV obj ect, which corresponds
what we did there? to the remFn in the old PieD.
25
So what is the underlined part about? We changed the methods in PieD, which
means that we must also change how it is
used.
26
Ready for the next protocol? Let's grab a quick snack.
27
How about some ice cream? Cappuccino crunch sounds great. The more
coffee, the better.
28
Take a look at subst in Top and at forTop in Nothing really. They get handed back and
Substv. What happens to the values that forth, though forTop compares 0 to t.
they consume?
29
Is the handing back and forth necessary? We don't know any better way, yet.
30
1
Here is a way to define SubstV that avoids the Wow. This visitor has two fields.
handing back and forth of these extra values.
class Subst v {
Object n;
Object 0;
SubstV(Object _n,Object _0) {
n = _n;
0= _0; }
PieD forBotO {
return new BotO; }
PieD forTop(Object t,PieD r) {
if (0. equals ( t))
return new Top(n,r.subst(this));
else
return new Top(t,r.subst(this)); }
1 In functional programming, a visitor with fields is called a
} closure (or a higher-order function), which would be the
result of applying a curried version of 5ubst.
Boring Protocols 89
Copyrighted Material
31
How do we create a Substv? We use
new SubstV(new Integer(5),
new I nteger(3) ).
32
What does that do? It creates a Substv whose methods know how
to substitute new Integer(5) for all
occurrences of new Integer(3) in Piev
How do the methods know that without 33 The values have now become fieldsof the
consuming more values? SubstVobject to which the methods belong.
They no longer need to be consumed.
36
Does all that mean we have to change the Of course, because the methods subst in the
protocol, too? Bot and Top variants consume only one value
now.
90 Chapter 6
Copyrighted Material
37
That's right. Here are the datatype and its In the Top variant, we still need to hand over
Bot variant. Define the Top variant. both t and r.
r = -r'}
,
class Bot extends PieD {
PieD rem(RemV remFn) { PieD rem(Rem V remFn) {
return remFn-forBotO; } return remFn-forTop(t,r); }
PieD subst(Subst V substFn) { PieD subst(Substv substFn) {
return substFn-forBotO; } return substFn-forTop(t,r); }
}
}
Is there anything else missing? 38 We haven't defined Rem v for this new
protocoL But it is simple and hardly worth
our attention.
39
What is the difference between rem and Not much. The name of the respective values
subst in Bot? they consume and the corresponding types.
What is the difference between rem and 40 Not much. The name of the respective values
subst in Top? they consume and the corresponding types.
"
Can we eliminate the differences? It is easy to make them use the same names.
It doesn't matter whether rem is defined as
it is or as
PieD rem(RemV substFn ) {
return substFn-forTop(t,r); }.
42
True, because substFn is just a name for a Both Remv and SubstV are visitors that
value we don't know yet. But how can we contain the same method names and those
make the types the same? methods consume and produce the same
types of values. We can think of them as
extensions of a common abstract class.
Boring Protocols 91
Copyrighted Material
Yes! Do it! 43 Here it is.
Great job, except that we will use interface 44 Okay, that doesn't seem to be a great
for specifying visitors like these. difference. Can a class extend an interface
the way it extends an abstract class?
interface PieVisitorI {
PieD forBotO;
PieD forTop(Object t,PieD r);
}
46
Now that we have an interface that describes Yes, we can. Assuming we can use
the type of the values consumed by rem and interfaces like abstract classes, we can
subst, can we make their definitions even write
more similar? PieD rem ( PieV i sitorI pvFn) {
return pvFn.forTop(t,r); }
and
PieD subst(PieVisitorI pvFn) {
return pvFn.forTop( t,r); }
in Top.
Correct. What is the difference between rem 47 There isn't any. We can use the same name
and subst, now? for both, as long as we remember to use it
whenever we would have used rem or subst.
48
What is a good name for this method? The method accepts a visitor and asks for its
services, so we call it accept.
92 Chapter 6
Copyrighted Material
And what is a better name for pvFn? 49 Easy: ask, because we ask for services.
50
Now we can simplify the protocol. Here is Here we go.
the new Remv .
abstract class PieD {
class Rem v implements PieVisitorr { abstract PieD accept(PieVisitorI ask);
Object 0; }
RemV(Object 0) { _
0= _ 0; }
class Bot extends PieD {
public PieD forBotO { PieD accept(PieVisitorr ask) {
return new BotO; } return ask.forBotO; }
public PieD forTop(Object t,PieD r) { }
if ( 0. equals ( t))
return r.accept(this);
class Top extends PieD {
else
Object t;
return new Top (t,r.accept(this)); }
PieD r;
}
Top (Object _t,PieD _r) {
t t;
= _
Did you notice the two underlined 51 Yes, what about them?
occurrences of public?
It's a way to say that these are the methods 53 Looks weird, but let's move on.
that satisfy the obligations imposed by the
interface.
54
Correct. They are just icing. Okay, we still won't forget them.
Boring Protocols 93
Copyrighted Material
55
Now define the new Substv. Here it is.
57
Why is there is a line, not an arrow, from The SubstV visitor implements P ieVisito�,
SubstV to PieVisito�? it doesn't extend it. Arrows mean "extends,"
lines mean "implements."
And the dashed line? 58 It tells us the name of the method that
connects the datatype to the visitors.
94 Chapter 6
Copyrighted Material
What is the value of 59 Easy:
new Top (new AnchovyO, new Top(new Salmon(),
new Top(new TunaO, new Top( new Tuna O ,
new Top(new AnchovyO, new Top (new SalmonO,
new Top(new TunaO, new Top(new TunaO,
new Top(new AnchovyO, new Top(new AnchovyO,
new Bot()))))) new Bot()))))) .
. accept(new LtdSubstv (2,
new SalmonO,
new Anchovy()))?
60
Explain what LtdSubstv produces.1 The methods of LtdSubstV replace one fish on
a pie by another as many times as specified
1 A better name is LimitedSubstitutionV, and that is how by the first value consumed by LtdSubstV
we pronounce it.
61
Good. Define LtdSubstv That's easy. We have such a flexible protocol
that we only need to define the essence now.
Boring Protocols 95
Copyrighted Material
62
W hat is the value of Oops, there are too few anchovies on this
new Top(new AnchovyO, pizza pie:
new Top(new TunaO, new Top(new SalmonO,
new Top(new AnchovyO, new Top(new Tun a O ,
new Top(new TunaO, new Top(new SalmonO,
new Top(new AnchovyO, new Top(new Tu naO ,
64
Why doesn't c ever change? Because this, the LtdSubstV that performs
the substitutions, never changes.
65
Can we fix this? We can't change this, but we can replace
this with a new LtdSubstv that reflects the
change.
66
If c stands for the current count, how do we Simple, we use
create a LtdSubstV that shows that we have new LtdSubstV(c - 1,n,0)
just substituted one fish by another.
in place of this.
96 Chapter 6
Copyrighted Material
Define the new and improved version of 67 Voila.
LtdSubstv.
class LtdSubstV implements PieVisitorI {
int C;
Object n;
Object 0;
LtdSubst v (int _c,Object _n,Object _0) {
c _c;
=
n = _n;
o=-Dj }
else
return
new Top(t,
r.accept(
this)); }
}
68
How does They are two different LtdSubstVs. One
this replaces c occurrences of ° by n in a pizza
pie, and the other one replaces only c 1 of
differ from
-
them.
new LtdSubstV(c -l,n,o)?
How do you feel about protocols now? 69 They are exciting. Let's do more.
Boring Protocols 97
Copyrighted Material
aterial
Copyrighted M
Is 1 Yes.
new Flat(new AppleO,
new Flat(new PeachO,
new Bud()))
a flat TreeD?
2
Is Yes, it is also a flat TreeD.
new Flat(new Pea rO ,
new Bud())
a flat TreeD?
Is the difference between flat trees and split Unless there is anything else to TreeD, it's
trees obvious now? totally clear.
OhMy! 99
Copyrighted Material
Here are some fruits. It does not differ too much from what we
have seen before.
I abstract class FruitV {}
I abstract class Treev {}
class Peach extends FruitV {
public boolean equals(Object 0) { I class Bud extends Treev {}
return ( 0 instanceof Peach); }
}
class Flat extends Tr eev {
FruitV I;
class Apple extends FruitV { Treev t;
public boolean equals(Object 0 ) { Flat(FruitV _1,Treev -t) {
return ( 0 instanceof Apple); } I = -I;
t = -t·}
,
}
}
class Pear extends FruitV {
public boolean equals(Object 0 ) {
return ( 0 instanceof Pear); } class Split extends Treev {
} Treev I;
Treev 1';
Split(TreeV _1,Treev _1') {
class Lemon extends FruitV { I = _I;
public boolean equals(Object 0 ) { l' = _1'; }
return (0 instanceof Lemon); }
} }
Did you notice that we have redefined the That probably means that we will need to
method equals in the variants of Fruitv? compare fruits and other things.
Do Treev,s variants contain equals? No, which means we won't compare them,
but we could.
100 Chapter 7
Copyrighted Material
10
How does the datatype TreeD differ from all The name of the new datatype occurs twice
the other datatypes we have seen before? in its Split variant.
11
Let's add a visitor interface whose methods That just means extending what we have
produce booleans. with one method each.
_1'; }
=
12
Yes, it is. Hang in there, we need unusual Okay.
names for unusual interfaces. Here b reminds
us that the visitor's methods produce
booleans.
OhMy! 101
Copyrighted Material
13
How many methods does the definition of Three, because it works with Tree'Ds, and the
blsFlatV contain, assuming it implements datatype definition for Tree'Ds has three
bTreeVisito,.z? variants.
15
What visitor does blsFlatv remind us of? OnlyOnionsv
16
Here is a skeleton for blsFlatV That's easy now.
17
Define the blsSplitV visitor, whose methods Here is the easy part.
check whether a Tree'D is constructed with
Split and Bud only. class b lsSp li t V implements bTreeVisito,.z {
public
boolean jorBudO {
return true; }
public
boolean lorFlat(FruitD I,TreeD t) {
return false; }
public
boolean jorSplit(TreeD l,TreeD r) {
--- }
}
102 Chapter 7
Copyrighted Material
18
What is difficult about the last line? We need to check whether both 1 and rare
split trees.
19
Isn't that easy? Yes, we just use the methods of blsSplitV on I
and r.
21
If Yes, because if both are true, we have a split
l.accept(this) tree.
is true, do we need to know whether
r. accept(this)
is true?
22
If No, then the answer is false.
I. accept(this)
is false, do we need to know whether
r. accept (this)
is true?
2
Finish the definition of blsSplitV using 3 Now we can do it.
if ( . )
. .
26
Does this TreeD have any fruit? No.
class bHasFruitV
implements bTreeVisitorr {
public
boolean Jo1'BudO {
return false; }
public
boolean Jo1'Flat(FruitD J,TreeD t) {
return true; }
public
boolean Jo1'Split(TreeD I,TreeD 1') {
if! (l.accept(this))
return true;
else
return 1'.accept(this); }
}
return II r.accept(this).
104 Chapter 7
Copyrighted Material
28 3.
What is the height of
new Split(
new Split(
new BudO,
new Flat(new LemonO,
new BudO)),
new Flat(new FigO ,
new Split(
new BudO,
new B u d())))?
29
What is the height of 2.
new Split(
new BudO,
new Flat(new LemonO,
new Bud()))?
new BudO?
32
So what is the height of a TreeP? Just as in nature, the height of a tree is the
distance from the beginning to the highest
bud in the tree.
33
Do the methods of iHeightV work on a TreeP? Yes, and they produce an into
34
Is that what the i in front of Height is all It looks like i stands for int, doesn't it?
about?
36
Why is the height 4? Because the value of
new Split(
new BudO,
new Bud())
.accept(new iHeightV())
is 1; the value of
new Flat(new F i gO,
37
And how do we get from 3 to 4? We need to add one to the larger of the
numbers so that we don't forget that the
original Treev was constructed with Split and
those two Treevs.
u picks the larger of two numbers, x and y. 1 38 Oh, that's nice. What kind of methods does
iHeight v define?
1 When you enter this in a file, use
Math. max (x, y) .
Math is a class that contains max as a (static) method.
39
i He i ghtV , s
methods measure the heights of Now that's a problem.
the Treevs to which they correspond.
106 Chapter 7
Copyrighted Material
40
Why? We defined only interfaces that produce
booleans in this chapter.
'
4
So what? The methods of iHeightV produce ints, which
are not booleans.
Okay, so let's define a visitor interface that 42 It's almost the same as bTreeVisjto�
produces ints.
interface iTreeVisitorI {
int jorBudO;
int jorFlat(FruitD j, TreeD t);
int jorSplit(TreeD l,TreeD r);
}
Yes, and once we have that we can add 43 Does that mean we can have two methods
another accept method to TreeD with the same name in one class?l
We can have two methods with the same 44 bTreeVisitorI is indeed different from
name in the same class as long as the types iTreeVisitorI, so we can have two versions of
of the things they consume are distinct. accept in TreeD.
4
Add the new accept methods to TreeD's 5 It is easy.
variants. Start with the easy one.
class Bud extends TreeD {
boolean accept(bTreeVisitorI ask) {
return ask.forBudO; }
int accept(iTreeVisitorI ask) {
return ask.forBudO; }
}
47
Here is iHeightV That's easy now.
4S
What is the value of 1, of course.
new Split(
new BudO,
new Bud())
.accept(new iHeightV())?
108 Chapter 7
Copyrighted Material
50
What is the value of If the visitor tSubstV substitutes apples for
new Split( figs, here is what we get:
new Split( new Split(
new Flat(new Fig(), new Split(
new Bud()), new Flat(new Apple(),
new Flat(new Fig(), new Bud()),
new Bud())), new Flat(new Apple(),
new Flat(new Fig(), new Bud())),
new Flat(new Lemon(), new Flat(new Apple(),
new Flat(new Apple(), new Flat(new Lemon(),
new Bud())))) new Flat(new Apple(),
. accept ( new Bud())))).
new tSubstV (
new Apple(),
new Fig()))?
Correct. Define the tSubstV visitor. 51 It's like SubstFishV and SubstlntV from the
end of chapter 5, but we can't do it just yet.
52
What's the problem? Its methods produce TreeDs, neither ints nor
booleans, which means that we need to add
yet another interface.
interface tTreeVisitorI {
TreeD JorBudO;
TreeD JorFiat(FruitD J,TreeD t);
TreeD JorSpiit(TreeD i,TreeD r);
}
53
Good job. How about the datatype TreeD Easy. Here is the abstract one.
t = _tj }
110 Chapter 7
Copyrighted Material
55
Then define tSubst v That's easy, too. It has two fields, one for
the new FruitD and one for the old one, and
the rest is straightforward.
56
Here is a TreeD that has thr ee Figs: Even the visitors are no longer interesting.
new Split(
new Split( class iOccursv implements iTreeVisito,x {
new Flat(new FigO, FruitD a;
new Bud()), iOccursV(FruitD _a) {
new Flat(new FigO, a = _a; }
new Bud())),
new Flat(new FigO, public int jorBudO {
new Flat(new LemonO, return 0; }
new Flat(new AppleO, public int jorFlat(FruitD j,TreeD t) {
new Bud())))). if (t. equals(a ))
return t.accept(this) + 1;
Now define iOccursv, whose methods co unt else
how often some FruitD occurs in a tree. return t.accept(this); }
public int jorSplit(TreeD I,TreeD r ) {
return
l.accept(this) + r.accept(this); }
}
OhMy! 111
Copyrighted Material
57
Do you like your fruit with yogurt? We prefer coconut sorbet.
58
Is it disturbing that we have three nearly Copying definitions is always bad. If we
identical versions of accept in TreeDs and its make a mistake and copy a definition, we
variants? copy mistakes. If we modify one, it's likely
that we might forget to modify the other.
60
Remember Integer and Boolean? They make Yes, Boolean is the class that corresponds to
it possible. boolean, and Integer corresponds to into
61
Here is the interface for a protocol that Here they are.
produces Object in place of boolean, int,
and TreeD. class Flat extends TreeD {
FruitD f;
interface TreeVisito� { TreeD t;
Object forBudOj Flat(FruitD _f,TreeD _t) {
Object forFlat(FruitD f,TreeD t); f = -fj
Object forSplit(TreeD l,TreeD r); t _tj }
=
}
Object accept(TreeVisito� ask) {
Here is the datatype and the Bud variant. return ask.forFlat(f,t)j }
}
abstract class TreeD {
abstract
class Split extends TreeD {
Object accept(TreeVisito� ask);
TreeD l;
} TreeD r;
Split(TreeD _l,TreeD _r) {
class Bud extends TreeD { l = _lj
Object accept(TreeVisito� ask) { r _rj }
=
return ask.forBudOj }
} Object accept(TreeVisito� ask) {
return ask.forSplit(l, r)j }
Define the remaining variants of TreeD. }
112 Chapter 7
Copyrighted Material
62
Good. Now define IsFlatV, an Object That's no big deal.
producing version of blsFlatv.
class IsFlatV implements TreeVisitorr {
public Object forBudO {
return new Boolean(true); }
public Object forFlat(FruitV f ,Treev t) {
return t.accept(this); }
public Object forSplit(TreeV l,Treev r ) {
return new Boolean(false); }
}
63
And how about IsSplitv? Now that's different. Here we need a way to
determine the underlying boolean of the
Boolean that is produced by l.accept(this) in
the original definition.
64
Okay, here it is. Oh, because l.accept(this) produces an
Object, we must first convert 1 it to a Boolean.
class IsSplitV implements TreeVisitorr { Then we can determine the underlying
public Object forBudO { boolean with the boolean Value method. We
return new Boolean(true); } have seen this in chapter 5 when we
public Object forFlat(FruitV f,Treev t) { converted an Object to a OneMoreThan.
return new Boolean(false); }
public Object for Split(TreeV l,Treev r ) {
if (( (Boolean) (l. accept(this)))
. boolean Value())
return r. accept(this);
else 1 If Java had parametric polymorphism for methods, no
return new Boolean(false); } downward cast would be necessary for our visitors (Martin
Odersky and Philip Wadler, Pizza into Java: Translating
} Theory into Practice, Conference Record on Principles of
Programmmg Languages, 146-159. Paris, 1997).
65
Will the conversion always work? Yes, because the Object produced by
l. accept ( this) is always a Boolean.
OhMy! 113
Copyrighted Material
66
Did you think that was bad? Then study Oh my.,
this definition during your next break.
114 Chapter 7
Copyrighted Material
Copyrighted Material
What is the value of 1 12.
(7 + ((4- 3) x5))?
Where do the constructors come from? A datatype and its variants that represent
arithmetic expressions.
5
Did you like that? So far, so good.
6
What is the value of {7,5}.
({7,5} U (({4} \ {3}) n {5}))?
10
Where do the constructors come from? A datatype and its variants that represent
set expressions.
11
Do you still like it? Sure, why not.
12
Does the arithmetic expression look like the Yes, they look the same except for the
set expression? constants:
new Plus(
new Const(e),
new Prod(
new Diff(
new Const(e),
new Const(e)),
new Const(e))).
13
Let's say that an expression is either That's a tricky question.
a Plus(exprl,expr2),
a Diff(exprl, expr2)' interface Exp rVi si t o,x {
a Prod(exprl,expr2), or Object jorPlus(ExprD l,ExprD r);
a constant, Object jorDijJ(ExprD l,ExprD r);
Object jorProd(ExprD l,ExprD r);
where exprl and expr2 stand for arbitrary
Object jorConst(Object c);
expressions. What should be the visitor
interface?
}
118 Chapter 8
Copyrighted Material
14
Good answer. Here is the datatype now.
class Plus extends ExprD {
abstract class ExprD { ExprD l;
abstract ExprD r;
Object accept(ExprVisito.x ask); Plus(ExprD _1,ExprD _r ) {
} I _Ii
=
r =_r; }
Define the variants of the datatype and equip
them with an accept met hod that produces Object accept(ExprVisito.x ask) {
Objects. return ask.forPlus(I,r); }
}
r = -r', }
16
How do we add We have done this before. We use the
17
But what is the result of An int, what else?
new Integer(3).intValueO
+
new Integer(2).intValueO?
18
How do we turn that into an Integer? We use new Integer(... ) .
19
Okay, so here is a skeleton of IntEvalv That's an interesting skeleton. It contains
five different kinds of blanks and two of them
class IntEvalv implements ExprVisito,x { occur three times each. But we can see the
public Object forPlus(ExprV l,Exprv r) { bones only. Where is the beef?
return plus(l.accept(this),
r. accept (this)); }
public Object forDiff(ExprV l,Exprv r) {
return diff(l.accept(this),
r.accept(this)); }
public Object forProd(ExprV l,Exprv r ) {
return prod(l.accept(this),
r.accept(this)); }
public Object forConst(Object c) {
return c; }
Object plUS(_l l,-2 r ) {
return -3;}
Object diff(-l l'-2 r) {
return ---4; }
Object Prod(_l l,-2 r ) {
return -5;}
}
120 Chapter 8
Copyrighted Material
20
How does !oTPlus work? It consumes two ExprDs, determines their
respective values, and pl us es them.
21
How are the values represented? As Objects, because we are using our most
general kind of (and most recent) visitor.
22
So what kind of values must plus consume? Objects,
because that's what
l.accept(this)
and
T.accept(this)
produce.
25
Can we convert all Objects to Integers? No, but all Objects produced by IntEvalV are
made with new Integer(.. . ) so that this
,
26
Is that true? What is the value of Wow. At some level, this is nonsense.
new Plus(
new Const(n ew Empty()),
new Const(new Integer(5)))
.accept(new IntEvalV ()) ?
27
Correct, so sometimes the conversion may What should we do?
fail, because we use an instance of IntEvalV
on nonsensical arithmetic expressions.
29
If we want to add l and r, we write Now it's easy. Here we go.
new Integer(
((Integer)l).int Value 0 class IntEvalV implements ExprVisitorr {
+ public Object forPlus(ExprV l,Exprv r) {
((Integer)r). int Value 0 ). return plus(l. accept(this),
r.accept(this)); }
Complete the definition now. public Object forDiff(ExprV l,Exprv r) {
return diff (l. accept(this) "
r.accept(this)); }
public Object forProd(ExprV l,Exprv r) {
return prod(l.accept(this),
r. accept (this)) ; }
public Object forConst(Object c ) {
return c; }
Object plus(Object l,Object r) {
return
new Integer(
((Integer)l). intValue 0
+
(( Integer) r ) . intValue ()); }
Object diff(Object l,Object r ) {
return
new Integer(
((Integer)l).intValue 0
((lnteger)r).intValue()); }
Object prod(Object l,Object r ) {
return
new Integer(
((Integer)l).int Value 0
*
((lnteger)r).intValue()); }
}
122 Chapter 8
Copyrighted Material
That one was pretty easy, wasn't it? 30 Yes. Let's implement an ExprVisitorX for sets.
31
plusing,
•
What do we need to Implement one for sets? We certainly need methods for
d iff ing, and proding sets.
3.
Why is this so tricky? Constructors always construct, and add does
not always construct.
3'
Do we need to understand that? Not now, but feel free to absorb it when you
have the time.
boolean mem(lnteger n) {
if (i . equals( n ) )
return true;
else
return s.mem(n)j }
SetD plus(Set'D t) {
return s.plus(t.add(i)); }
SetD diff(SetD t) {
if (t.mem(i))
return s.diff(t)j
else
return s.diff( t}.add(i)j }
SetD prod(SetD t} {
if (t.mem(i))
return s.prod(t).add(i);
else
return s.prod(t}; }
}
124 Chapter 8
Copyrighted Material
Do we need to understand these definitions? 37 Not now, but feel free to think about them
when you have the time. We haven't even
used visitors to define operations for union,
set-difference, and intersection, but we trust
you can.
38
What do we have to change in IntEvalV to Not much, just plus, dijJ, and prod.
V
obtain Set E v al , an evaluator for set
expressions?
39
How should we do that? Oh, that's a piece of pie. We just copy the
definition of IntEvalV and replace its plus,
dijJ, and prod methods.
41
Why should we throw away more than half That's true. If we copied the definition and
of what we have? changed it, we would have identical copies of
JorPlus, JorDijJ, JorProd, and JorConst. We
should reuse this definition. 1
42
Yes, and we are about to show you better That part is easy:
ways. How do we have to change plus, dijJ, Object plus(Object I Obj ect r) {
,
return ((SetV)l).plus((SetD)r); }
Object diff(Object I,Object r) {
return ((SetV)I).diff{{SetV)r); }
Object prod(Object I,Object r) {
return ((SetV)l).prod{{SetV)r); }
}
44
Is it like equals? Yes, when we include equals in our class
definitions, we override the one in Object.
Here, we override the methods plus, diff, and
prod as we extend IntEvalV
46
How many methods from IntEvalv are not Four: forPlus, forDiff, forProd, and
overridden in SetEvalv? forConst.
47
Does SetEvalV implement ExprVisito,x? It doesn't say so.
48
Does SetEvalv extend IntEvalv? It says so.
49
Does IntEvalv implement ExprVisito,x? It says so.
50
Does SetEvalv implement ExprVisito,x? By implication.
126 Chapter 8
Copyrighted Material
51
That's correct. What is the value of Interesting question. How does this work
new Prod( now?
new Const(new EmptyO
.add(new Integer(7»),
new Const(new EmptyO
.add(new I ntege r(3» »
.accept(new SetEvaIV())?
52
What type of value is It is a Prod and therefore an E xprD
new Prod(
new Const(new Empty()
. add (new I ntege r ( 7 » ) ,
new Const(new Empty()
. add (new Int ege r (3 »» ) ?
3
5
And what does accept consume? An instance of SetEvalV, but its type is
ExprVisito�
5
Where is the definition of SetEvalV's method 5 It is in IntEvalV
forProd?
Suppose we had the values of 56 If their values were A and B, we would have
new Const(new EmptyO to determine the value of
. add(new I nteger(7») prod{A,B) .
. accept (this)
and
new Const(new EmptyO
.add(new Integer(3»)
. accept (this).
What would we have to evalu ate next?
So far, we have always used a method on a 58 That ' s true. What is the object with which
particular object. we u se prod(A,B)?
59
It is this object. Oh, does that mean we should evaluate
new SetEvaIVO.prod(A,B)?
61
Good. And now what? Now we still need to determine the values of
new Const(new EmptyO
.add(new Integer(7)))
. accept (this)
and
new Const(new Empty()
.add(new Integer(3)))
.accept(this).
62
The values are obviously It, too, is in IntEvalv.
new EmptyO
add (new Integer(7))
.
and
new EmptyO
.add(new Integer(3)).
Where is the definition of forConst that
determines these values?
63
Here is the next expression in our sequence: The object is an instance of SetEvalv, which
new SetEvalvO overrides the prod method in IntEvalv with
.prod(new EmptyO its own .
. add(new Integer(7)),
new EmptyO
.add(new Integer(3))).
Where does prod come from?
128 Chapter 8
Copyrighted Material
64
What next? Next we need to determine the value of
((SetV)(new EmptyO
. add(new Integer(7))))
. prod((SetV)new EmptyO
.add(new Integer(3))),
because it is
(( Se tV) ( I. accept( this)))
. prod ((S etV) r. accept ( this))
with l. accept(this) and r.accept(this)
replaced by their respective values.
65
Is Of course it is, but the type of l.accept(this),
new EmptyO.add(new Integer(7)) which is where it comes from, is Object.
an instance of Setv?
66
And how about It's the same.
new EmptyO.add(new Integer(3))?
7
And that is why the method must contain a 6 This example makes the need for conversions
conversion from Object to SetVs. obvious again.
68
Time for the last question. Where does this This one belongs to SetV or more precisely
prod come from now? its Empty and Add variants.
And what does prod do? 69 It determines the intersection of one SetV
with another S etV, but didn't we agree that
the previous question was the last question
on that topic?
70
We overrode that, too. Thanks, guys.
73
But j ust because something works, it doesn't Yes, let's do better. We have defined all
mean it's rational. these classes ourselves, so we are free to
rearrange them any way we want.
7'
What distinguishes IntEvalV from SetEvalV? The methods plus, diff, and prod.
7S
What are the pieces that they have in They share the methods forPlus, forDiff,
common? jorProd, and jorConst.
78
Good. Here is how we express that. Isn't this abstract class like Pointv?
130 Chapter 8
Copyrighted Material
77
Yes, we can think of it as a datatype for What do we do now?
EvarD visitors that collects all the common
elements as concrete methods. The pieces
that differ from one variant to another are
specified as abstract methods.
78
We define IntEvalV extending Eva I!> . Itis basically like the original but extends
Eval!>, not IntEvalv.
class IntEvalV extends Eval"V {
Object plus(Object I,Object r) { class SetEvalv extends Eval"V {
return Object plus(Object I,Object r) {
new Integer( return « SetD)l).plus«SetD)r); }
((Integer) l). intValue 0 Object diff(Object I,Object r) {
+ return «SetD)l).diff« SetD)r); }
«Integer)r). intValue()); } Object prod(Object l,Object r) {
Object diff(Object I,Object r ) { return « SetD)l).prod( SetD)r); }
return }
new Integer(
«Integer)l). intValue 0
«lnteger)r).intValue())j}
Object prod(Object I,Object r) {
return
new Integer(
((Integer) l). int Value 0
*
«lnteger)r). intValue()); }
}
Define SetEvalv.
79
Is it natural for two evaluators to be on the Much more so than one extending the other.
same footing?
80
Time for supper. If you are neither hungry nor tired, you may
continue.
82
What do the two visitors have in common? Many things: n, 0, and forBot.
83
Where do they differ? They differ in forTop , but LtdSubstV also has
an extra field.
84
And where do we put the pieces that two We put them into an abstract class.
classes have in common?
85
What else does the abstract class contain? It specifies the pieces that are different if
they are needed for all extensions.
132 Chapter 8
Copyrighted Material
86
Define the abstract class Subst!>, which It's not a big deal, except for the fields.
contains all the common pieces and specifies
what a concrete pie substituter must contain abstract class Subst!>
in addition. implements PieVisito,x {
Object n;
Object 0;
public Pie!> jorBot O {
return new Bot(); }
public
abstract Pie!> jorTop(Object t,Pie!> r);
}
87
We can define SubstV by extending Subst!> It also extends Substv.
if (o.equals{t))
return public Pie!> jorTop(O bj ect t,Pie!> r) {
new Top{n, r. accept{thi s)); if (c == 0)
else return new To p{t, r);
return else
new Top{t, r.accept(this)); } if (o.equals{t))
} return
new Top(n,
r.accept(
Define LtdSubstv. new LtdSubstV(c - 1,n,0)));
else
return
new To p{t , r. ac ce pt{this) ) ; }
}
88
Do the two remaining classes still have things No, but the constructors have some overlap.
in common? Shouldn't we lift the SubstV constructor into
Subst!>, because it holds the common
elements?
r.accept(
new LtdSubstV(c -l,n,o)));
else
return
new Top(t,r.accept(this)); }
}
134 Chapter 8
Copyrighted Material
91
That's neat. How about some art work? Is this called a pie chart?
accept
- PieVisitorT
93
Is it also possible to define LtdSubst v as an It may even be better. In some sense,
extension of Substv? LtdSubst v j ust adds a service to Subst v: It
counts as it substitutes.
94
IfLtdSubstv is defined as an extension of As we just said, c is an addition and forTop
SubstV, what has to be added and what has is different.
to be changed?
96
Let's draw a picture. Fine, and don't forget to use lines, rather
than arrows, for implements.
accept
- PieVisito..x
97
You deserve a super-deluxe pizza now. It's already on its way.
136 Chapter 8
Copyrighted Material
@o
ru� � ��@dl ��rr
Copyrighted Material
Remember Pointv? If not, here is the It has been a long time since we discussed
datatype with one additional method, minus. the datatype PointV and its variants, but
We will talk about minus when we need it, they are not that easy to forget.
but for now, just recall PointV, s variants.
class CartesianPt extends PointV {
abstract class PointV { CartesianPt (int _x,int _y) {
int x; super(_x,_y); }
int y;
PointV ( int _x,int _ y ) { int distance ToOO {
x -Xj = return l VX2 + y2J; }
Y = -Yj } }
V
boolean closerToO(Point p) {
return class ManhattanPt extends PointV {
distanceToO() :S p.distance ToOO; } ManhattanPt ( int _x,int _y) {
PointV minus ( PointV p) { super(_x,_y); }
return
new CartesianPt (x - p.x,y - p.y); } int distanceToOO {
abstract int distance ToOO; return x + y; }
} }
b.y _b.yj }
=
int distanceToOO {
return
super.distanceToOO + b.x b.yj }
+
}
and y.
5
ShadowedManhattanPt:
Is this a Yes.
new ShadowedManhattanPt(2,3,1,O)?
What is unusual about distance ToO? Unlike any other method we have seen
before, it contains the word super. So far,
we have only seen it used in constructors.
What does it mean?
Here, super. distance ToO refers to the Okay. That means we just add x and y when
method definition o f distance ToO that is we evaluate super.distanceToO().
relevant in the class that
ShadowedManhattanPt extends.
Correct. But what would we have done if Then we would refer to the definition in the
ManhattanPt had not defined distance ToO? class that ManhattanPt extends, right?
Yes, and so on. What is the value of It is 6, because 2 + 3 is 5, and then we have
new ShadowedManhattanPt(2,3,1,O) to add 1 and O.
distanceToO()?
.
140 Chapter 9
Copyrighted Material
10
Precisely. Now take a look at this extension Nothing. We just discussed this kind of
of Cartesian Pt. constructor for Shadowed Manhattan Pt.
class ShadowedCartesianPt
extends CartesianPt {
int .:l",;
int .:ly;
ShadowedCartesianPt(int -x,
int _y,
int �x,
int �y) {
super(-x,_y);
.:l", �"';=
.:ly �y; } =
int distanceToOO {
return
super.distanceToOO
+
L J.:l� + .:l�J; }
}
11
Is this a ShadowedCartesianPt: Yes.
new ShadowedCartesianPt(12,5,3,4)?
12
And what is the value of It is 18, because the distance of the
new ShadowedCartesianPt(12,5,3,4) Cartesian point (12,5) is 13, and then we add
distanceToOO?
. 5, because that is the value of
. 1.:l2 + .:l2y
V x
with .:lx replaced by 3 and .:ly replaced by 4.
13
W hat do we expect? 17, obviously.
new CartesianPt(15,9).
15
We need to add L\ to x and L\y to y when And indeed , the value of
x
we t hi nk of a ShadowedCartesianPt. new CartesianPt(15,9)
.distanceToOO
is 17.
16
Does this explain how distanceToO should Completely. It should make a new
measure the distance of a CartesianPt by adding the corresponding
ShadowedCartesianPt to the origin? fields and should then measure the distancf'
of that new point to the origin.
11
Revise the definit ion of ShadowedCartesianPt Okay.
accordingly.
class ShadowedCartesianPt
extends CartesianPt {
jnt L\x;
jnt L\y;
ShadowedCartesianPt(int _x,
jnt _y,
int _L\x,
int _L\y) {
super(_x,_y);
L\x = ..L\x;
L\y =
_L\y; }
int distanceToOO {
return
new CartesianPt(x + L\x,y + L\y)
.distanceToOO; }
}
18
Do we still need the new CartesianPt after No, once we have the distance, we have no
distanceToO has d et erm i ned the distance? need for this point. 1
142 Chapter 9
Copyrighted Material
Correct. W hat is the value of 19 true,
new CartesianPt(3,4) because the distance of t he CartesianPt to
.closerToO( the origin is 5, while that of the
new ShadowedCartesianPt(1,5,1,2))? ShadowedCartesianPt is 7.
20
How did we determine that value? That's obvious.
21
Is the rest of this chapter obvious, too? W hat ?
22
That was a hint that now is a good time to Oh. Well, that makes the hint obvious.
take a break.
2'
Are sandwiches square meals for you? They can be well-rounded.
25
Here are circles and squares. Then this must be the datatype that goes
with it.
class Circle extends ShapeD {
int r; abstract class ShapeD {
Circle(int _r) { abstract
r = _r; } boolean accept(ShapeVisito,x ask);
}
boolean accept(ShapeVisito,x ask) {
return ask.forCircle(r); }
}
s = _ s; }
boolean accept(ShapeVisitorI ask) {
return ask.forSquare(s); }
}
interface ShapeVisito,x {
boolean forCircle(int r);
boolean forSquare(int s);
boolean forTrans(PointV q,Shapev s);
}
27
Yes and we will need this third variant. Okay, now this looks pretty straightforward,
but what's the poi nt?
class Trans! extends Shapev {
PointV q;
ShapeV s;
Trans(PointV _q,ShapeV _s) {
q = -q;
s = _s; }
28
Let's create a circle. No problem:
new Circle(10).
How should we think about that circle? 29 We should think about it as a circle wi t h
radius 10.
Good. So how should we think about 30 Well, that's a square whose sides are 10 units
Where are our circle and square located? 31 What does that mean?
144 Chapter 9
Copyrighted Material
32
Suppose we wish to determine whether some In that case, we must think of the circle as
33
And how about the square? There are many ways to think about the
location of the square.
34
Pick one. Let's say the square's southwest corner sits
on the origin.
35 . .
That will do. Is the CartesianPt with x Yes, It IS. but barely.
coordinate 10 and y coordinate 10 inside the
square?
36
And how about the circle? Certainly not, because the circle's radius is
10, but the distance of the point to the origin
is 14.
Are all circles and squares located at the 37 We have no choice so far, because Circle and
origin? Square only contain one field each: the radius
and the length of a side, respectively.
38
This is where Trans comes in. What is Aha. With Trans we can place a circle of
newTrans( 10 at a point like
radius
new CartesianPt(5,6),
new CartesianPt(5,6).
new Circle(10))?
new Square(10)).
42
How do we determine whether some point is If the square is located at the origin, it is
inside a square? simple. We check whether the point's x
43
Is that all? No, we also need to do that for the y
coordinate.
44
Aren't we on a roll? We have only done the easy stuff so far. It is
not clear how to check these things when the
circle or the square are not located at the
origin.
45
Let's take a look at our circle around We can if we translate all other points by an
new CartesianPt(5,6) appropriate amount.
46
By how much? By 5 in the x direction and 6 in the y
direction, respectively.
47
How could we translate the points by an We could subtract the appropriate amount
appropriate amount? from each point.
48
Is there a method in PointP that Yes. Is that why we included minus in the
accomplishes that? new definition of PointP?
146 Chapter 9
Copyrighted Material
49
Indeed. And now we can define the visitor The three methods put into algebra what we
HasPtv, whose methods determine whether just discussed.
some Shapev has a PointV inside of it.
s)
50
What is the value of We said that this point wasn't inside of that
new Circle(lO) circle, so the answer is false.
. accept (
new HasPtV(new CartesianPt(lO,lO)))?
52
Let's consider something a bit more We already considered that one, too. The
interesting. What is the value of value is true, because the circle's orig i n is at
new Trans( new CartesianPt(5,6).
new CartesianPt(5,6),
new Circle(lO))
. accept (
new HasPtV(new CartesianPt(lO,lO)))?
54
But what is the value? First, we have to find out whether
new Trans(
new CartesianPt(5,6),
new Circle(lO))
. accept (
new HasPtV(new CartesianPt(5,6)))
is true or fa Ise.
56
Very good. Can we nest Trans three ti mes? Ten times, if we wish, because a Trans
contains a ShapeD, and that all ows us to nest
things as oft en as needed.
58
No. The exciting part is about to start. We are all eyes.
59
How can we proj ect a cube of cheese to a It becomes a square, obviously.
piece of paper?
60
And the orange on top? A circle, Transed appropriately.
148 Chapter 9
Copyrighted Material
61
Can we think of the two objects as one? We can, but we have no way of saying that a
circle and a square belong together.
62
Here is our way. That looks obvious after the fact. But why is
there a blank in accept?
class Union extends Shapev {
Shapev s;
Shapev t;
Union(ShapeV _s,Shapev _t) {
s _s; =
t _t; }
=
63
What do we know from Circle, Square, and We know that a ShapeVisito,x contains one
Trans about accept? method each for the Circle, Square, and Trans
variants. And each of these methods
consumes the fields of the respective kinds of
objects.
6.
So what should we do now? We need to change ShapeVisito,x so that it
specifies a method for the Union variant in
addition to the methods for the existing
variants.
65
Correct, except that we won't allow ourselves Why can't we change it?
to change ShapeVisito,x
66
Just to make the problem more interesting. In that case, we're stuck.
interface UnionVisito,x
extends ShapeV isito,x {
boolean forUnion(ShapeD s,ShapeD t);
}
68
1 Does that mean accept in Union should
Basically. This extension produces an
interface that contains all the obligations ,
receive a U n ionV i sito,x so that it can use the
(i. e., names of methods and what they for Union method?
consume and produce of ) ShapeV is ito,x and
the additional one named for Union.
69
Yes it should, but because UnionVisito,x We have been here before. Our accept
extends ShapeV is ito,x , it is also a method must consume a
ShapeVisito,x and
S hapeV isito,x . fortunately every UnionVisito,x implements a
S hapeV isito,x too. But if we know that
,
accept consumes a UnionV is ito ,x , we can
convert the ShapeVisito,x to a UnionVisito,x
and invoke the for Union method.
70
Perfect reasoning. Here is the completed And it makes complete sense.
definition of Union.
s = _ s;
t = _t; }
150 Chapter 9
Copyrighted Material
Let's create a Union shape. 71 That '8 trivial.
new Trans(
new CartesianPt(12,2),
new Union(
new Square(10),
new Trans(
new CartesianPt(4,4),
new Circle(5)))).
73
Could it be a UnionVisito�? No. It does not provide the method
forUnion.
74
Define UnionHasPtV, which extends HasPtV Here it is. Its method checks whether the
with an appropriate method forUnion. point is in one or the other part of a union.
The other methods come from HasPt v
75
Does UnionHasPtV contain for Union? Of course, we just put it in.
Correct, but unfortunately we have to add 77 The first two additional words have an
three more words to make this explicit. obvious meaning. They explicitly say that
this visitor provides the services of
class UnionHasPtV UnionVisitorr. And, as we have said before,
extends HasPtV the addition of public is necessary, because
implements UnionVisitorr { this visitor implements an interface.
UnionHasPtV(Point'D -p) {
super(_p); }
public
boolean forUnion(Shape'D s,Shape'D t) {
if (s.accept(this))
return true;
else
return t.accept(this); }
}
78
Good try. Let's see whether it works. What We know how for Trans works, so we're really
should be the value of asking whether
new Trans( new CartesianPt(lO,lO)
new CartesianPt(3,7),
is inside the Union shape.
new Union(
new Square(10),
new Circle(10)))
accept (
.
new UnionHasPtV(
new CartesianPt(13,17)))?
79
So? Which means that we're asking whether
new CartesianPt(lO,lO)
is inside of
new Square(10)
or inside of
new Circle{lO).
152 Chapter 9
Copyrighted Material
80
Okay. And what should be the answer? It should be true.
81
Let's see whether the value of Usually we start by determining what kind of
new Trans( object we are working with.
new CartesianPt(3,7),
new Union(
new Square(lO),
new Circle(lO)))
. accept(
new UnionHasPtV(
new CartesianPt(13,17)))
is true?
82
And? It's a ShapeD
8
How did we construct this shape? 3 With Trans.
84
Which method should we use on it? forTrans, of course.
8
5
Where is for Trans defined? It is defined in HasPtV
86
So what should we do now? We should determine the value of
new Union(
new Square(lO),
new Circle(lO))
. accept(
new HasPtV(
new CartesianPt(lO,lO))).
87
What type of object is It's a ShapeD.
new Union(
new Square(lO),
new Circle(lO))?
89
So which method should we use on it? jorUnion, of course.
90
How do we find the appropriate jorUnion In accept, which is defined in Union, we
method? confirm that
new HasPtV(
new Cartes i anPt ( lO, lO ) )
is a UnionVisitorr and then invoke its
jorUnion.
91
Is an instance of HasPtV a UnionVisitorr? No!
9.
What do we do next? Relax. Read a novel. Take a nap.
95
Which of those is best? You guessed it: whatever you did is best.
We should have prepared this extension in a 96 How could we have done that?
better way.
154 Chapter 9
Copyrighted Material
97
Here is the definition of HasPtV that we In two ways. First, it contai n s a new
should have provide d if we wanted to extend method: newHasPt. Second, it uses the new
it without making changes. method in place of new HasPtV in for7rans.
I I
ShapeVisito,x newHasPt(PointD p) {
return new HasPtV(p); }
98
Good. What does newHasPt produce? A new ShapeVisitorI, as its i nterface implies .
99
And how does it produce that? By constructing a new instance of HasPtv
100
Is newHasPt like a constructor? It is virtually i ndistinguishable from a
constructor, which is why it is above the line
that separates constructors from methods.
102
Very well. But how does that help us with That's not obvious.
our problem?
103
Can we override newHasPt when we extend Yes, we can override any method that we
HasPtv? wish to override.
104
Let's override newHasPi in UnionHasPtV When we override it, we need to make sure it
produces a ShapeVisito�
105
That's true. Should it produce a HasPtV or a The latter. Then for Trans in HasPtV keeps
UnionHasPtv? producing a UnionHasPtV, if we start with a
Uni onHas Ptv
106
we
'
156 Chapter 9
Copyrighted Material
And that's exactly what we need. Revise the 107 Here it is.
definition of UnionHasPtV.1 ,------,
class UnionHasPtV
extends HasPtv
implements UnionVisitorr {
UnionHasPtV(PointD -p) {
super(_p); }
ShapeVisitorr newHasPt(PointD p) {
return new UnionHasPtV(p); }
public
boolean jorUnion(ShapeD s,ShapeD t) {
if ( s. accept(this))
return true;
else
return t.accept(this); }
}
1 The is an instance of the factory method pattern [4].
108
If we assemble all this into one picture, what A drawing that helps our understanding of
do we get? the relationships among the classes and
interfaces.
accept
- ShapeVisitorr
accept
109
W hat does the box mean? Everything outside of the box is what we
designed originally and considered to be
unchangeable; everything inside is our
extension.
111
Is anything missing? Square, but that's okay.
112
Let's see whether this definition works. We remember that the shape was built with
What is the value of Trans.
new Trans(
new CartesianPt(3,7),
new Union(
new Square(lO),
new Circle(lO)))
. accept(
new UnionHasPtV(
new CartesianPt(13,17)))?
113
Which method should we use on it? for Trans , of course.
11.
Where is forTrans defined? It is defined in HasPt v
115
So what should we do now? We should determine the value of
new Union(
new Square(lO),
new Circle(lO))
. accept(
this. newHasPt(
new CartesianPt(lO,lO))).
116
What is this? The current visitor, of course.
117
And how does that work? We determine the value of
this. newHasPt(
new CartesianPt(lO,lO))
and then use accept for the rest.
158 Chapter 9
Copyrighted Material
118
And what do we create? The new UnionVisitorI:
new Un ionH asPt V (
new Ca rtes ianPt ( 10 , 10)).
119
What is the value of UnionHasPt v also satisfies the interface
new Union( ShapeVisitor1", so now we can invoke the
new S qu a re ( 10) , forUnion method.
new Circle(10))
. accept (
new Uni on H asPt V (
new CartesianPt(1O,10)))?
120
How do we do that? We first determine the value of
new Square(10)
. accept (
new UnionH asPt V (
new CartesianPt(10,10))).
If it is true, we're done.
121
Is it true? It is. So we're done and we got the value we
expected.
122
Are we happy now? Ecstatic.
123
Is it good to have extensible definitions? Yes. People should use extensible definitions
if they want their code to be used more than
once.
124
Very well. Does this mean we can put Yes, we can and should always do so.
together flexible and extensible definitions if
we use visitor protocols with these
constructor-like methods?
125
And why is that? Because no program is ever finished.
126
Are you hungry yet? Are our meals ever finished?
rfIl :·
111,'
11
d I
Copyrighted Material
Have you ever wondered where the pizza pies 1 You should have, because someone needs to
come from? make the pie.
2
Here is our pizza pieman. This is beyond anything we have seen before.
return occTop(t); }
public int remTop(Object t) {
P = (PieV)p.accept(new RemV(t»
return occTop(t); }
public int substTop(Object n,Object 0) {
p = (PieV)p.accept(new SubstV(n,o»
return occTop(n); }
public int occTop(Object 0) {
return
((Integer)p.accept(new Occursv (0»)
.intValueO; }
}
3
How so? Haven't we seen Piev, Top, and Bot We have seen t hem.
before?
And haven't we seen visitors like Rem v, Yes, yes. But what are the stand-alone
SubstV, and Occursv for various datatypes? semicolons about?
Let's not worry about them for a while. 5 Fine, but t hey are weird.
interface PiemanI {
int addTop(Object t)j
int remTop (Object t)j
int substTop(Object n,Object o)j
int occTop(Object o)j
}
162 Chapter 10
Copyrighted Material
Here is Occurs V. It counts how often some And this little visitor substitutes one good
topping occurs on a pie. topping for another.
n = n; _
10
Great! Now we have almost all the visitors We remember that one, too.
for our pieman. Define Rem v, which removes
a topping from a pie. class Rem v implements PieVisitorI {
Object 0;
Rem v (Object 0 ) {_
0 = _0; }
12
Which pie? The pie named p in the new PiemanM.
14
And what is the value of That's where those stand-alone semicolons
15
True. I f we wish t o determine the value of Yes, we must understand that. There is no
number x in the world for which
new PiemanM O.addTop(new Anchovy()),
x = x + 1,
we must understand what
p = new Top(new AnchovyO,p) so why should we expect there to be a Java p
such that
return occTop(new Anchovy()) p = new Top(new Anchovy(),p)?
means?
That's right. But that's what happens when 16 So what does it mean?
you have one too many double espressos.
17
Here it means that p changes and that future And the change is that p has a new topping,
references to p reflect the change. right?
IS
When does the future begin? Does it begin below the stand-alone
semicolon?
19
That's precisely what a stand-alone It produces the number of anchovies on p.
semicolon means. Now do we know what
164 Chapter 10
Copyrighted Material
20
And how many are there? We added one, so the value is 1.
21
And now what is the value of It's 2, isn't it?
22
No, it's not. Take a close look. We created a Oh, isn't there a way to place several
new pieman, and that pieman added only requests with the same pieman?
one anchovy to his p.
23
Yes, there is. Take a look at this: Okay, y stands for some pieman.
25
And now what is the value of Still 1. According to the rules of semicolon
and =, this replaces all anchovies on p with
y. substTop (new TunaO,new Anchovy ())?
tunas, changes p, and then counts how many
tunas are on p.
26
Correct. So what is the value of 0, because y ' s pie no longer contains any
anchovies.
y.occTop(new Anchovy ())?
27
Very good. And now take a look at this: What are the
29
And what is the value of It's 0, because remTop first r e moves all tunas
yy.remTop(new Tuna ( )) and then counts how many there are left.
Does that mean rem Top always produ ces O? 30 Yes, it alw ays does .
yy.occTop(new SalmonO)?
y.occTop(new Salmon ( )) ?
34
So is it the same on e? When we eat a pizza pie , we chang e , but we
are still the same .
36
Does that mean that anybody can w rite No, because y y ' s type is PiemanI, p isn't
yy.p = new Bot O available. Only addTop, remTop, substTop,
and oce Top are visible.
and thus change a pieman like yy?
166 Chapter 10
Copyrighted Material
37
Isn't it good that we didn't include p in Yes, with this trick we can prevent others
PiemanI? from changing p ( or parts of p) in strange
ways. Everything is clear now.
38
Just hke chIcken soup.
• •
Clear like soup?
40
And that's what we discuss next. Do you No, a cup of coffee will do.
need a break?
41
Compare this new PieVisitorI with the first It isn't all that different. A PieVisitorI must
one in this chapter. still provide two methods: for-Bot and
for-Top, except that the former now
interface PieVisito� { consumes a Bot and the latter a Top.
Object for-Bot(Bot that);
Object for-Top(Top that);
}
42
True. Here is the unchanged datatype. The definition is straightforward.
Is it? Why does it use this? 43 We only have one instance of Bot when we
use for-Bot, this, so for-Bot is clearly
namely
supposed to consume this.
r = _rj }
45
Modify this version of Occurs v so that it The forBot method basically stays the same,
implements the new PieVisito,x but forTop changes somewhat.
46
How does forBot change? It now consumes a Bo t , which is why we had
to add (Bot that) behind its name.
168 Chapter 10
Copyrighted Material
47
How does JorTop change? It no longer receives the field values of the
corresponding Top. Instead it consumes the
entire object, which makes the two fields
available as that. t and that. r.
48
And? With that, we can replace t he fields t and r
49
Isn't that easy? This modification of Occursv certainly is.
51
Do we need to do SubstV? Not really. It should be just like Remv.
52
And indeed, it is. Happy now? So far, so good. But what's the point of this
exercise?
53
Oh, Point'Ds? They will show up later. Seriously.
0= _0; }
return that; }
else {
that. r. accept ( this)
return that; }
}
}
55
news
• •
Don't they say "no is good news?" Does thIS saymg apply here, too?
56
Yes, because we want to define a version of That's a way of putting it.
SubstV that modifies toppings without
constructing a new pie.
57
What do the methods of Substv always They always return that, which is the object
return? that they consume.
58
So how do they substitute toppings? By changing the that before they return it.
Specifically, they change the t field of that to
n when it equals o.
170 Chapter 10
Copyrighted Material
59
What? The
that.t = n
does it.
60
Correct. And from here on, that. t holds the In the previous Su bstV, r.accept(this)
new topping. What is created a new pie from r with all toppings
that. r. accept(this) appropriately substituted. In our new
about?
version, that.r.accept(this) modifies the pie r
so that below the following semicolon it
contains the appropriate toppings.
61
Is there anything else to say about the new Not really. It does what it does, which is
Su bstv? what we wanted. 1
62
Do we have to change PiemanM? No, we didn't change what the visitors do,
we only changed how they do things.
63
Is it truly safe to modify the toppings of a Yes, because the PiemanM manages the
pie? toppings of p, and nobody else sees p.
64
Can we do LtdSu bstV now w ithout creating Now that's a piece of pie.
new instances of LtdSubstV or Top?
boolean closerToO(PointV p) {
return class ManhattanPt extends PointV {
distanceToOO :S p.distanceToOO; } ManhattanPt(int _x,int _y) {
PointV minus(PointV p) { super(_x,_y); }
return
new CartesianPt(x - p.x,y - p.y); } int distanceToOO {
abstract int distanceToOO; return x + y; }
} }
class ShadowedManhattanPt
extends ManhattanPt {
int .0.x;
int .0.y;
ShadowedManhattanPt(int -x,
int _y,
int _.0.x,
int _.0.y) {
super(_x,_y);
.0.x _.0.x;
=
.0.y _.0.y; }
=
int distanceToOO {
return
super.distanceToOO+.0.x + .0.y; }
}
66
Aren't we missing a variant? Yes, we are missing ShadowedCartesianPt.
172 Chapter 10
Copyrighted Material
67
Good enough. We won't need it. Here is one Shouldn't we add a method that changes all
point: the fields of the points?
new ManhattanPt(1,4).
If this point represents a child walking down
the streets of Manhattan, how do we
represent his movement?
68
Yes. Add to PointV the method moveBy, First we must know what the method is
which consumes two ints and changes the supposed to produce.
fields of a point appropriately.
69
The method should return the new distance Now we know how to do this.
to the origin.
abstract class PointV {
int xii
int Yi
PointV (int _x,int _y) {
x = -Xj
Y = -Yi }
boolean closerToO(PointV p) {
return
distanceToOO :S p.distanceToOOi }
PointV minus(PointV p) {
return
new CartesianPt(x - p.x,y - P.Y)i }
int moveBy(int �x,int �y) {
x = x + �x
,
return distanceToOO; }
abstract int distanceToOOj
}
70
LetptChild stand for 5.
new ManhattanPt(1,4).
What is the value of
72
Good. Now let's watch a child with a 7.
helium-filled balloon that casts a shadow.
LetptChildBalloon be
new ShadowedManhattanPt(I,4,I,I).
ptChildBalloon. distance To 0 O?
ptChildBalloon. moveBy(2,8)?
74
Did the balloon move, too? Yes, it just moved along as we moved the
point.
Isn't that powerful? 75 It sure is. We added one method, used it,
and everything moved.
76
The more things change, the cheaper our Yes, but to get to the dessert, we had to
desserts get. work quite hard.
77
Correct but now we are through and it is Don't forget to leave a tip.
time to go out and to celebrate with a grand
dinner.
174 Chapter 10
Copyrighted Material
Copyrighted Material
You have reached the end of your introduction to computation with classes, interfaces, and
objects. Are you now ready to tackle a major programming problem? Programming requires
two kinds of knowledge: understanding the nature of computation, and discovering the lexicon,
features, and idiosyncrasies of a particular programming language. The first of these is the more
difficult intellectual task. If you understand the material in this book, you have mastered that
challenge. Still, it would be well worth your time to develop a fuller understanding of all the
capabilities in Java-this requires getting access to a running Java system and mastering those
idiosyncrasies. If you want to understand Java and object-oriented systems in greater depth,
take a look at the following books:
References
1. Arnold and Gosling. The Java Programming Language . Addison-Wesley, Reading, Mas
sachusetts, 1996.
3. Firesmith and Eykholt . Dictionary of Object Technolog y . SIGS Books, Inc., New York,
New York, 1995 and Prentice Hall, Englewood Cliffs, New Jersey, 1995.
5. Gosling, Joy, and Steele. The Java Language Specification. Addison-Wesley, Reading,
Massachusetts, 1996.
ExprD, 119
Peach, 100
ExprVisito�, 118
Pear, 100
Copyrighted Material
Radish, 28, 36 subAbC, 52, 53
remA, 45, 49, 70 SubAbCv, 66
RemAv, 66,71,73 SubstV, 133, 134
RemFishv, 74 SubstFish v, 82
Remv, 77,87,93,163,169 SubstV, 83,87,89,94,132-136,163,170
RemlntV, 76 SubstlntV, 82
remTop, 161 substTop, 161
Rodv, 29 Sword, 29
Sabre, 29 Thyme, 5
Sage, 5 Tomato, 16, 27, 28, 36, 59, 62
Salmon, 69, 72 Top, 69, 78, 83, 85, 86, 91, 93, 162 168
Salt, 4 topAwC, 50, 53
Sausag e, 43, 53, 63-65 TopAwCv, 66
SeasoningV, 4 Trans, 144
SetV, 123 Treev. 100, 101, 107, 109, 112
SetEvalv, 126, 131 T reeVisito� . 112
ShadowedCartesianPt, 141, 142 tSubstV, 111
ShadowedManhattanPt, 139, 172 tTreeVisito�, 109
S hallot , 28, 36 Tuna, 69,72
Shapev, 143
ShapeVisito�. 144 Union, 149. 150
Shishv, 16,27,59,61 UnionHasPtv, 151, 152, 157
Shrimp, 28, 36 UnionVisito�, 150
Silver, 29
whatHolder, 33-36
Skewer, 16, 27, 59, 62
Wood, 29
Slice, 10
Spinach, 54
Zero, 7, 79
Split, 100, 101 , 108, 110 , 112
Zucchini, 37,
Square, 143
}
interface O--> oI {
Object apply(Object x); class Fact implements o-->oI {
} o-->oI fact;
Fact(o-->oI _fact) {
fact =-fact; }
interface oo-->ooI { public Object apply(Object i) {
o --> oI apply ( o-->oI x);
int inti =((lnteger)i).intValue();
} if (inti ==0)
return new Integer(1);
else
interface oo-->oo-->ooI {
I return
O-->O apply ( oo-->ooI x);
new Integer(
} inti
*
class H implements TI {
I
OO-+OO f;
H ( oo-+ooI -J) {
f -f; }
=
Copyrighted Material