A Little Java, A Few Patterns PDF
A Little Java, A Few Patterns PDF
A Little Java, A Few Patterns PDF
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 '''lith 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 ,vill teach
you a powerful new way of thinking that you should add to .your design toolbox.
Enjoy the pizzas!
Ralph E. Johnson
Champaign, Illinois
ix
Preface
An object-oriented programming language enables a programmer to construct reusable program
components. With such components, other programmers can quickly build large new programs
and program fragments. In the ideal case, the programmers do not modify any existing code
but simply glue together components and add a few new ones. This reusability of components,
however, does not come for free. It requires a well-designed object-oriented language and a strict
discipline of programming.
Java is a such a language, and this book introduces its object-oriented elements: (abstract)
classes, fields, methods, inheritance, and interfaces. This small core language has a simple
semantic model, which greatly helps programmers to express themselves. In addition, Java
implementations automatically manage the memory a program uses, which frees programmers
from thinking about machine details and encourages them to focus on design.
The book's second goal is to introduce the reader to design patterns, the key elements of a
programming discipline that enhances code reuse. Design patterns help programmers organize
their object-oriented components so that they properly implement the desired computational
process. l\lore importantly still, design patterns help communicate important properties about
a program component. If a component is an instance of an explicitly formulated pattern and
documented as such, other programmers can easily understand its structure and reuse it in their
own programs, even without access to the component's source.
The book is primarily intended for people-practicing programmers, instructors and students
alike-who wish to study the essential elements of object-oriented programming and the idea
of design patterns. Readers must have some basic programming experience. They will benefit
most from the book if they understand the principles of functional design, that is, the design
of program fragments based on their input-output behavior. An introductory computer science
course that uses Scheme (or ML) is the best way to get familiar with this style of design, but it
is not required.
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
distract 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 quickly. Thus, there is quite a bit more to patterns
than an introductory book could intelligibly cover. Yet, the simplicity of the patterns we use
and the power that they provide should encourage readers to study the additional references
about patterns mentioned at the end of the book.
ACKNOWLEDGMENTS
\\J'e are indebted to many people for their contributions and assistance throughout the devel-
opment of this book. Several extensive discussions with Shriram Krishnamurthi, Jon Rossie,
xi
and r..1itch 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, l\latthew
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 SIb-TEX.
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.
\Ve do not give any formal definitions in this book. V·le 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.
\Ve 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.) \Ve 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 \vill 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
EXPERIMENTING WITH JAVA
class Main {
public static void main(String args( ]) {
DataType_or_Interface y = new _____ •
System.out.println( ...... ); } }
With DataType_or _Interface y = new _____ , create the object y with which you wish to
experiment. Then 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)j
System.out.println( y.distanceToO() ); } }
lSee Arnold and Gosling (IJ 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.
xiii
If you wish to experiment with a sequence of expressions that modify y, as in chapter ]0, e.g.,
y.-
y.-
y.- -
replace . . . . . . with
For example, if you wish to experiment with the methods of PiemanM 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 Anchovy 0 ) + "\n" +
y. addTop(new Anchovy 0 ) + "\n" +
y.substTop(new Tuna() ,new Anchovy()) ); } }
xiv
flo
~(rv&C9IT~tll ~~
Is 5 an integer? 1 Yes, it is.
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.
12
\Vhat is a type? Sometimes we use it as if it were the
collection.
13
Can we make new types? \Ve don't know how yet.
Modern Toys 3
Draw the picture that characterizes the 14 Is this it?
essential relationships among the following
classes.
Yes. We say Seasoning D is a datatype, and 15 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 SeasoningD.
a Seasoning D?
17
And It's also a SeasoningD, because new Pepper()
new PepperO? creates an instance of Pepper, and every
instance of Pepper is also a SeasoningD .
Is there any other SeasoningD? 19 No, because only Salt and Pepper extend
Seasoning D. 1
4 Chapter 1
Correct, Salt and Pepper are the only 20 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.
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
coordinates of the points. But what is the
I abstract class Point V {} remaining thing above the bold bar?!
Modern Toys 5
The underlined occurrences of CartesianPt 26 How do we use these constructors'?
and ManhattanPt introduce the constructors
of the respective variants.
27
A constructor is llsed ,vith new to create Obvious!
ne\\" instances of a class.
\Vhell we create a CartesianPt like this: 2R So now \ve have created a CartesianPt whose
l' field is 2 and whose y field is 3. And
new CartesianPt(2,3),
because CartesianPt extends Point V , it is
\ve use the constructor in the definition of
also a Point v .
CartesianPt.
29
Correct. Is this a ManhattanPt: Yes, and its x field is 2 and its y field is 3.
new ManhattanPt(2,3)?
:lO
Isn't all this obvious'? 1'.10stly, but that means we have used
constructors before without defining them.
How does that work?
.11
\Vhen a class does not contain any fields, as And that's the constructor \\re used bdore,
in Salt and Pepper, a constructor is included right?
by default.
:12
~Y('s,
that's correct. Default constructors Good. But what is new PointVO?
!len'r consume values, and, when used with
new, alwa,vs create objects without fields.
:l3
An abstract class is by definition That makes sense. Let's move on.
incolllplete, so new cannot create an
instance from it.
6 Chapter 1
Do the following classes define another 34 Yes, they define a datatype and t",'o variants.
datatype with variants?
35
Is this a Num'D: Obviously, just like new SaitO is a
new ZeroO? Seasoning D .
37
How does OneMoreThan do that? We give it new ZeroO, which is a Num'D, and
it constructs a new Num D .
And what does it mean to construct this new 3B 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 Num D ,
instance of Zero? which, at the moment, may be either a Zero
or a OneMoreThan.
Modern Toys 7
40
\Vhat is A Num D , because OneMoreThan constructs
new OneMoreThan( an instance from a Num'D and we agreed
new OneMoreThan( that
new Zero()))? new OneMoreThan(
new Zero())
is a Num'D.
42
Is new Zero() the same 3..<; O? No. 0 is similar to. but not the same a.s.
new ZeroO.
And what is 44 4.
new OneMoreThan(
new OneMoreThan(
new OneMoreThan(
new OneMoreThan(
new Zero()))))
similar to?
46
Are there more Num'Ds than ints? No. 1
8 Chapter 1
\Vhat is the difference between new ZeroO 47 Easy: new ZeroO is an instance of Zero and,
and O? by implication, is a Num'D, whereas 0 is an
into This makes it difficult to compare them,
but we can compare them in our minds.
Correct. In general, if two things are 48 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.
Every class that does not explicitly extend 51 This must mean that everything is an Object.
another class implicitly extends the class
Object.
l'vlodern Toys 9
\Vhat do the following define? They define a new datatype and its two
variants. The first variant contains a field of
type Object.
.~()
They are, because everything created with Hence. we can use both
new is an Object. the cla:-;s of all objects. new Zero()
and
new SaitO
for the ('ons/ruction of a Base. which req1lires
an Object.
10 Chapter 1
57
Is anything else an Object? We said that only things created with new
are Objects. 1
58
Correct. Is this a Layer V : 5 is not created with new, so this must be
new Base( nonsense.
5)?
60
Correct again! How about this Layer v : This must mean that Integer creates an
new Base( object from an into
new Integer(5))?
Is it confusing that we need to connect int 62 Too much coffee does that.
with Integer and boolean with Boolean?
Modern Toys 11
~~
~~~~2J@ill~ \~
(~)~~:f
Remember points? Sure, we just talked about them. But what
are these labeled ovals about?
abstract class Point V {
-- ( Point )--
} ~--------~
- - ( CartesianPt ) --
}
- - ( ManhattanPt ) - -
}
We will find out soon. Did you notice the big 2 It must be for drawing the picture of the
white space on the right? classes.
\\'hat do the methods produce? ints, \vhich represent the distances to the
origin.
pOintJ
--- .--- .. -,-.------~-.-~~--~--~-
CartesianPt '
--- --------,--,------,---~
How ]WlllY tillH'S have we defined the method Three times. but the first one differs from the
rilstmu'(' ToO? other two. It is labeled abstract. wbile the
others are not preceded by a special word.
Chapter 2
Do abstract methods belong to the 10 Yes, they always do.
abstract class?
What does l JXJ compute? 15 The largest int that does not exceed the
square root of x.
Time for a short break? 16 An apple a day keeps the dentist away. A
cup of coffee does not.
-- ( Shish )--
} ~.----~
I - ( Skewer )-
, }
L._ ... _~. __.. _.~ ~~~._._. ____ ~~___.___________.__~
- - (~_ _
O_n_i(_)l_l_ _) - -
}
-- ( Lamb )--
} ----------~
-- ( Tomato ) --
} ~----~
Did you notice the big space on the right? III Yes, isn't it for drawing the picture of the
classes?
16 Chapter 2
19
Construct a Shish'D. How about
new SkewerO?
21
And a third? Here's one more:
new Onion(
new Lamb(
new Onion(
new SkewerO))).
22
Are there only Onions on this Shish'D: true, because there is neither Lamb nor
new SkewerO? Tomato on new Skewer O.
23
Are there only Onions on this Shish'D: true.
new Onion(
new SkewerO)?
24
And how about: false.
new Lamb(
new SkewerO)?
25
Is it true that true.
new Onion(
new Onion(
new Onion(
new SkewerO)))
contains only Onions?
Here are the lIlethods. Yes. \Ve said above that the labeled ovals in
the center of the blank lines in the above
I abstract boolean onlyOniml.s(); class definitions tell llS where to put the
boxes \\lith the cOlT('sponciing labels.
Shish
I boolean onlyOnioTl.s()
return true: }
Skewer
boolean onlyOnioTlsO {
I return s.onlyOnious(); }
Onion
r
boolean oTl.lyOnioJls()
I return false: }
Lamb
boolean onlyOnioll:; ()
I return false: }
Tumato
Guod. Huw lllany methods have we defined? Five. but the first one is abstract: the
others are concretc.
18 Chapter 2
31
Do abstract methods belong to the Yes, we said so.
abstract class?
Does each variant of Shish D contain a 32 Yes, because Shish D contains an abstract
method called onlyOnions? method called only Onions that obligates
each variant to define a matching, concrete
method.
What do these concrete methods consume? 34 Nothing, just as the abstract method says.
35
\\That 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))
.onlyOnionsO?
Which definition of only Onions must we use 38 This object is an instance of Onion, so we
to determine the value of need to use the definition of onlyOni.ons that
new Onion( belongs to the Onion variant.
new Onion(
new SkewerO))
.onlyOnions()?
Dot's s always stand for all Onion? No, it has type ShishD, and it can stand for
any variant of Shish D : Skewer, Onion, Lamb.
or Tomato.
20 Chapter 2
Which definition of only Onions must we use 45 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)
.onlyOnions()?
46
What follows the word return in the s.onlyOnionsO·
only Onions method in Onion?
47
What is the field s of the object new SkewerO.
new Onion(
new SkewerO)?
How do we determine the answer for 50 We need to determine one more time which
new SkewerO version of only Onions we must use .
. onlyOnionsO?
52
Then \vhat is the answer'? true.
5:1
\Vhy? Because true is what the onlyOniorls method
in Skewer always returns.
54
Are we done? Yes! The answer for
new Onion(
new Onion(
new Skewer()))
· only Onions ()
is the same as the answer for
new Onion(
new Skewer())
· onlyOnions (),
which is the same a..'i the answer for
new Skewer()
· only Onions (),
which is
true.
56
\Vhich definition of onlyOnions must we use This object is an instance of Onion, so \\'C
to determine the value of need to use the definition of onlyOnions that
new Onion( belongs to the Onion variant.
new Lamb(
new Skewer()))
. unlyOnions()?
22 Chapter 2
57
What follows the word return in the s.onlyOnionsO·
onlyOnions method in Onion?
And now what is the answer? 63 false, because false follows the word return
in the corresponding method definition in
Lamb.
65
Descri be the methods (i. e., the function) Here are our words:
on/yOnions in your own words. "The methods determine for a Shish D
"vhether its contents are edible b~' all onion
lover."
66
Describe how the methods (i.e., the Here are our words again:
fUllctioIl) unlyOniuus accomplish this. "For each layer of the ShishD, except for
Onion, the corresponding method knows
whether it is good or bad. The method for
Onion needs to determine whether the
remaining layers are onl,\' Onions sitting Oil
a Skewer."
Is Yes.
new Tomato(
new Skewer())
a Shish D ?
Is The object
new Onion( new Tomato(
new Tomato( new Skewer())
new SkewerO)) is an instance of Shish D. so we can also wrap
a Shish D ? an Onion around it.
24 Chapter 2
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 SkewerO)))?
72
Define the methods (i. e.) the function) That's no big deal now.
is Vegetarian,
abstract boolean is Vegetarian();
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 is Vegetarian 0 {
return true; }
Skewer
boolean is Vegetarian 0 {
return s.isVegetarianO; }
Onion
boolean is Vegetarian 0 {
return false; }
Lamb
boolean is Vegetarian 0 {
return s. is Vegetarian 0; }
Tomato
7;)
Do('s each varia1lt of Shish D ('olltain a Yes, because Shish D contains all abstract
llldhud called isl'cgdarian'? method called is \'cgetarian.
\Yhat do tlH'st' COllnctt' lllethods cOllsume? Nothing, just a.s the abstract llH'thod says.
\Yhat d() these ('(Jnnde llldhods produce'? 7,; booleans, just as the abstract method says.
2G Chapter 2
79
Collect all the pieces of Shish'D. Here is the There are two methods per variant.
datatype.
class Skewer extends Shish'D {
abstract class Shish'D { boolean onlyOnions () {
abstract boolean onlyOnionsO; return true; }
abstract boolean is VegetarianO; boolean is l"egetarian () {
} return true; }
boolean onlyOnion.'iO {
return s.onlyOnionsO; }
boolean is Vegetarian () {
return .'i. is lTegetm'ian(); }
boolean onlyOnions () {
return false: }
boolean is Vegdarian () {
return false; }
-- ( Holder )--
} ~--------~
-- ( Shallot ) --
} ~--------~
-- ( Shrimp ) --
} ~---'---~
-- ( Radish )--
} ~---~
28 Chapter 2
\Vhat is different about them? 81 They are placed onto different Holders.
Here are some holders. 82 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 Rod D {} 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.
84
\Vhat is It's a Kebab D.
new Shallot(
new Radish(
new Holder(
new DaggerO)))?
86
Is Sure, becaul:ie Gold is a PlateD. PlateD is used
new Shallot( as a Holder, and radishes and shallots can be
new Radish( put on any Holder.
new Holder(
new Gold())))
a Kebab D ,?
87
Is Sure it is. It is basically like
new Shallot( new Shallot(
new Radish( new Radish(
new Holder( new Holder(
new GoldO))) new Dagger()))),
a vegetarian kebab? except that we have moved all the food from
a Dagger to a Gold plate.
Let's define the methods (i.e., the function) 88 If you can, you may rest now.
isl'eggie,
which check whether a kebab contains only
vegetarian foods, regardless of what Holder it
is on.
H9
\Vrite the abstract method isl'eggie. That's possible nm\!.
~----~----------------~·-l
bstract boolean is Veggie () .
[ Kebab j
30 Chapter 2
The concrete methods are similar to those 90 Except for the names of the methods and
called is Vegetarian. Here are two more; fields, the definitions are the same as they
define the remaining two. were for Shish v .
Holder Shrimp
Shallot Radish
92
What is It is a Kebab v , but we also know that it is an
new Shallot( instance of the Shallot variant.
new Radish(
new Holder(
new DaggerO)))7
94
And what is It is also a Kebab v , because any kind of
new Shallot( Holder will do.
new Radish(
new Holder(
new GoldO)))7
98
Does that mean is \'eggie works for all five Yes, and all other kinds of Objects that we
kinds of Holders'! eould possibly think of.
\Vhat is the holder of 100 All the food is now on a Gold plate.
new Shallot(
new Radish(
new Holder(
new Gold())))?
32 Chapter 2
What is the holder of 101 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?
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 Kebab D , then we
must add a method definition to each of the
abstract Object whatHolderO four variants.
Kebab
IIH)
\Vhat is the value of new Sword ().
new Holder(
new Sword ())
.whatHoldcr()?
III
Ddill(, tht' COllnett' lllethod that goes into \Vith these kinds of hints. it's easy.
the spac{' labeled Holder.
Object 10hatHolder() {
return 0: }
Holder
112
\Vhat is tht' value of new Integer(52).
new Radish(
new Shallot(
new Shrimp(
new Holder(
new Integer(52)))))
.ll'hatHolder()?
II:!
\Vhat is the value of new Integer(52).
new Shallot(
new Shrimp(
new Holder(
new Integer(52))))
. whatHoldcr()?
Chapter 2
\\That is the value of 114 new Integer(52).
new Shrimp(
new Holder(
new Integer(52)))
· whatHolder ()?
Does that mean that the value of 115 Yes. all four have the same answer:
new Radish( new Integer(52).
new Shallot(
new Shrimp(
new Holder(
new Integer(52)))))
· whatHolder ()
is the same as
new Shallot(
new Shrimp(
new Holder(
new Integer(52))))
· whatHolder 0,
which is the same as
new Shrimp(
new Holder(
new Integer(52)))
· whatHolderO,
which is the same as
new Holder(
new Integer(52))
· whatHolder ()?
Here is the method for Shallot. 116 They are all the same.
Shallot Shrimp
"'Trite the methods of whatHolder for Shrimp
and Radish. Object whatHolderO {
return k. whatHolder(); }
Radish
boolean is Veggie() { I
class Holder extends Kebab v { return k.isVeggieO: }
i
~
o = _0; }
:
I
boolean is Veggie () {
return true; }
Object whatHolderO {
class Shrimp extends Kebab v {
Kebab v k;
--l
I} return 0; } Shrimp(Kebab V _k) {
k = _k; }
I
-~
boolean is Veggie () { I
return k.is VeggieO; }
Object whatHolderO {
ret urn k. whatHolder (); } I
---'
118
Are there any other Kebab v foods besides No, these are the only kinds of foods on a
Shallot, Shrimp, and Radish? Kebab v .
36 Chapter 2
Can we add more foods? 119 Sure. \Ve did something like that ,vhen we
added Thyme and Sage to Seasoningv.
Let's define another Kebab v . 120 A concrete class that extends Kebab v must
define these two methods. That's \vhat the
class Pepper extends Kebab v { abstract specifications say. \Ve can define as
Kebab v k; many Kebabvs as we wish.
Pepper(Kebab V _k) {
k = _k; } class Zucchini extends Kebab v {
Kebab v k;
boolean is Veggie() { Zucchini(Kebab V _k) {
return k.isVeggieO; } k = _k; }
Object whatHolderO {
return k. whatHolderO; } boolean is Veggie () {
ret urn k. is Veggie (); }
Object whatHolderO {
Why does it include is Veggie and whatHolder return k.whatHolder(): }
methods?
Is it obvious how the new methods work? 121 Totally. In both cases is Veggie just checks
the rest of the Kebab v , because green
peppers and zucchini are vegetables.
Similarlv. whatHolder returns whatever
holder belongs to the rest of the Kebab v .
122
And then there were six. Yes, now Kebab v has six variants.
\Vhich of these points is closer to the origin: 123 The second one,
new ManhattanPt(3,4) because its distance to the origin is 6 while
and the first point's distance is 7.
new ManhattanPt(1,5)?
Good. Which of the following points is closer 124 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)?
y = -y: } y = -y: }
1:l<J
So finally, what is the value of . That's nonsense.
new CartesianPt(3A)
. do.'wrToO(new ManhattanPt( 1,5))':
Chapter 2
Why? 130 The method closerToO can only consume
CartesianPts, not ManhattanPts.
132
If we do that, can we still determine the Yes, because the definition of Point D
value of obligates every variant to provide a method
p.distanceToOO? named distance ToO.
133
Why does it help to replace (CartesianPt p) Every CartesianPt is a Point D , and every
by (Point D p)? ManhattanPt is a Point D . too. .
135
Is the definition of closerToO in CartesianPt Yes, they are identical.
the same as the one in ManhattanPt?
\Vhat else do tht' two point variants have in J:l7 Their fields. Shouldn't \ve lift them. too'?
common?
13K
\'ps. It's tricky, but here is a start. This not only lifts x and .II, it also introduces
a new constructor.
abstract class Point V
jnt :r:
int .II:
Pointv(int j,int _.II) {
l' = _.r;
y = _.1/: }
boolean closerToO(Point V p) {
return
distanceToOO :S p.distanceToO():
abstract jnt distance ToO():
}
L
130
Absolutely. And we need to change how a l\Iimicking this change is easy. But what
CartesianPt is built. Define ManhattanPt. does super(_x,_y) mean?
40 Chapter 2
140
The expressions super(_x,_y) in the That's simple.
constructors CartesianPt and ManhattanPt
create a Point D 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 PointDs in the datatype? two variants from each other reside in the
corresponding variants.
Is this a long chapter? 142 Yes, let's have a short snack break.
-- ( Pizza ) -- Pizza D p;
} '-----~ Sausage(Pizza D -p) {
p = -p: }
-- ( Cheese ) --
} '-----~
-- ( Olive ) --
} '--------'
-- ( Anchovy ) --
} ~"------=--~
What's New? 43
1I('J'(' is ullr favorit (' pizza: This looks too salty.
new Anchovy(
new Olive(
new Anchovy(
new Anchovy(
new Cheese(
new Crust()))))),
L<,t's n'lllU\'(' thelll. \\'hat is t hc valuc of It should be a dw('s(' alld ulive pizza, like
new Anchovy( this:
new Olive( new Olive(
new Anchovy( new Cheese(
new Anchovy( new Crust())),
new Cheese(
new Crust())))))
,nlllA I (r!
Dot'S !'ImA helong to the datat,Vpc Pizzaf' Yes, awl it produCt'S thclll. too.
aud its \'ariallts'!
---------------------------------.---------~
Chapter .3
Define the methods that belong to the five 7 We didn1t expect you to know this one.
variants. Here is a start.
Pizza
Pizza v remAO {
return new CrustO; }
Crust
Define the two methods that belong to Olive The Olive and Sausage methods are similar
and Sausage. We've eaten the cheese already. to the Cheese method.
Cheese Olive
Pizza v 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 pizza that p.remAO produces.
new Sausage ...
when we define these methods.
10
The methods remA must produce a Pizza v , Yes, and now the methods of remA produce
so let's use new CrustO, the simplest Pizza v , pizzas without any anchovies on them.
for the method in Anchovy.
Pizza v remAO {
return new CrustO; }
Anchovy
What's New? 45
Let'~ tr,v it out on a small pizza: That's easy. The object is an Anchovy. So
new Anchovy( the answer is new CrustO.
new Crust())
.I'IITIA().
12
b Absolutely, but what if we had mol'{'
Hew Crust() allchovies?
likt'
new Anchovy(
new Crust())
wit bOllt anchuvy?
~() problelll. Here is an example: That's easy again. As before, the object is an
Hew Anchovy( Anchovy so that tilt' answer lllust still be
Hew Anchovy( new Crust().
new CrustO))
.f'l'mA.().
() kay, so w hat if we had an uli ve alld cheese \Vell, this object is an Olive and its p stan(is
Oil top: for
new Olive( new Cheese(
new Cheese( new Anchovy(
Hew Anchovy( new Anchovy(
new Anchovy( new Crust()))).
new Crust()))))
.f"(lnA()?
Chapter 3
vVhat is the value of 16 It is
new Cheese( new Cheese(p. remA 0),
new Anchovy(
where p stands for
new Anchovy(
new CrustO))) new Anchovy(
.remAO? new Anchovy(
new CrustO)).
17
And what is the value of It is the pizza that
new Cheese( new Anchovy(
new Anchovy( new Anchovy(
new Anchovy( new CrustO))
new CrustO)) .remA()
.remAO)? 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?
Does it matter in which order we add those 20 Yes, we must first add cheese, producing
two toppings? new Cheese(
new CrustO)
and then we add the olive.
21
So what is the final answer? It is
new Olive(
new Cheese(
new CrustO)).
Doesn't that Illean that the result is )'es, it puts t he cheese on 'Nhatever we gd
new Cheese( for
new Anchovy( new Anchovy(
new Cheese( new Cheese(
new Crust())) new Crust()))
. rem.A())? .remA() .
And t he answer is 21i IreS, but we need to add chees(' OIl top.
new Crust()?
27
Does that mean the final ammrer is Yes, though it's not the answer we want.
new Cheese(
new Crust())?
48 Chapter 3
What do we want? 28 A double-cheese pizza like
new Cheese(
new Cheese(
new CrustO)),
because that's what it means to remove
anchovies and nothing else.
Anchovy
Does this remA still belong to Pizza v ? 30 Yes, and it still produces them.
We could add cheese on top of the anchovies. 31 Yes, that would hide their taste, too.
Did you notice the underlines? 33 Yes, they show where we added cheese.
What's New? 49
And what i:-; Here \vt' dOlt"t add an~' cheese. hecau:-;e the
new Olive( pizza does not contain any anchovies:
new Cheese( new Olive(
new Sausage( new Cheese(
new Crust()))) new Sausage(
.topAlvC()? new Crust()))).
DefiIle the remaiIling methods. \Ve expect you to know some of the answers.
Ip~z~a-;-~()~."~Ii~'() {
I return new Sausage(p.topAwC() I: }
I
Sausage
l __ _
Take a look at this method. \Vith that definition. topAwC would give the
same results <l."> 7'(;TTlA. The Illcthod topA1DC
Pizzafl topA10CO { ill Anchovy Illllst put the anchovy back 011
return p.topAwC(): the pizza and top it with cheese.
Anch()v~r
Pizzafl topAwC() {
I return
I new Cheese(
I new Anchovy(p. topAwC())):
I Anchovy
50 Chapter 3
How many layers of cheese are in the result 37 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 CrustO))))
.remAO)
.topAwCO?
How many occurrences of cheese are in the 38 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 CrustO)))) new Cheese(
.topAwCO) new Crust())))) .
. remAO?
Perhaps we should replace every anchovy 39 We just did something like that.
with cheese.
40
Is it true that for each anchovy in x Yes, and it does more. Once all the cheese is
x.topAwCO·remAO added, the anchovies are removed.
adds some cheese?
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
Here is a differcnt description:
"The methods look at each topping of a
pizza alld replace each anchovy with
dH'{'se.·'
Ddiw' til{' lllet hods that match this
desniptiull. Call them subAbC. 1 Here is the
abstract method.
Pizza D 8ubAbCO {
abstract Pizza D 8ubAbCO; return new Cheese(p.8ubAbCO); }
I
Pizza
Cheese
I
- -----.-------------.---~----
I
Pizza D 8ubAbCO {
return new Olive(p.subAbCO):
Olive
Anchovy
Pizza D 81lbAbCO {
return new Sausage(p.subAbC()): }
Dot's this skdeton look familiar'? Yes, this skeleton looks just like those of
topAwC and T'ernA.
,/4
Ddiw' t IH' llldhod that belongs in Anchovy. Here it is.
Pizza D 8ubAbCO {
return new Cheese(p.811bAbCO): }
Anchovy
Chapter 3
Collection time. l 45 The classes are getting larger.
Pizza v remAO {
return new Sausage(p.remAO): }
Pizza v topAwCO {
return new Sausage(p.topAwCO): }
Pizza v subAbCO {
return new Sausage(p.subAbCO)i }
1 This is similar to the interpreter and composite
patterns [4].
What's New? 53
Let's add more Pizza v foods. 46 Good idea.
47
Hen' is OIle addition: Spinach. Yes, \ve must define three concrete methods
for each variant of Pizza v.
class Spinach extends Pizza v
Pizza v p;
Spinach( Pizza v _p) {
p = -p; }
Pizza v remAO {
return new Spinach(p.remA()); }
Pizza v topAwCO {
return new Spinach(p.topAwCO); }
Pizza v subAbCO {
return new Spinach(p.subAbCO); }
48
Do we need to change Pizza v, Crust, Cheese, No. \Vhen we add variants to the datatypcs
Olive, Anchovy, or Sausage? we have defined, we don't need to change
what we have.
50
True enuugh. 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 tlH'se
definitions yet.
And now yuu can replace anchovy with \\re will stick with anchovies.
\vhatever pizza topping you want.
54 Chapter :3
~-~ -
~o
@@)EJ~ '(fJ@ @.)~~:ff
(£'~~l1J~~JU
\Vasn't this last collection overwhelming? It sure was. \Ve defined seven classes and
each contained three method definitions.
\Vhy 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 OnlyOnions v {
boolean jorSkewerO {
return true; }
boolean jorOnion(Shish V s) {
return s.onlyOnionsO; }
boolean jorLamb(Shish V s) {
return false; }
boolean jorTomato(Shish v s) {
return false; }
Almost. Each of them corresponds to an That's right. The major difference is that
only Onions method in one of the variants of they are all in one class, a visitor, whose
Shish v . name is OnlyOnions v .
Is only Onions different from OnlyOnions v? The former is used to name methods, the
latter names a class.
Those llldho<is that would have the same If \ve could do that, it would be much ('a;:;ier
WutH' if we placed t hem into t he variants of a to understand what action these methods
datatnH' in one class. perform.
That's what we are about to do. \Ve are It's about time.
guing to separate the action from the
datat~·I)('·
\Vhat is till' ditft'l'cnce between the method 11 Everything following return is tll(' same.
onl,l/Onion.'i ill the Onion variant and the
lIH'thud fOl'Onioli ill the visitor OnlyOnions v ,?
Y('s, that is the diffcH'Ilce. Are the other for Indeed, the.y are.
llH'thuds ill OnlyOnions v related to their
cUllllterparts ill the samc way?
The hmillg part tells llS hO\v tu make all of True. we still dOll't know how to make
this work. Shish D and its variants work with this visitor
cla.o..;s, which contains all the action.
Chapter 4
Now take a look at this. 16 This is a strange set of definitions. All the
only Onions methods in the variants look
abstract class Shish v { alike. Each of them uses an instance of
I OnlyOnions v ooFn = new OnlyOnionsvO; I OnlyOnions v , which is created in the
abstract boolean onlyOnionsO; datatype, to invoke a for method with a
} matching name.
boolean onlyOnionsO {
return ooFn.forLamb(s); }
boolean onlyOnionsO {
return ooFn.forTomato( s); }
}
III
That's what "consumption" is all about. Nothing, because a skewer is the basis of a
And what clops the jorSkeuwr method in shish and therefore ha.."> no fields.
Skewer cOllsume'?
So what does the (Shish D s) mean in the It is always the rest of the shish, below the
definition of jorOnion? top layer, which is an onion. In other words.
it is everything but the onion.
20
\rery good. The notation (Shish D s) means That makes sense and explains
that jorOnion consumes a Shish D and that 8. onlyOnions ().
:n
So what is the value of It is still true.
new Onion(
new Onion(
new Skewer()))
. onlyOnioTls()?
And how do we determilH:' that value with 24 \Ve start with the onlyOnions method in
these Ilew definitions? Onion, but it immediately uses the jOTOnion
method on the rest of the shish.
flO Chapter 4
And what does the forOnion method do? 25 It checks whether the rest of this shish is
edible by onion lovers.
Isn't that where we started from? 21 Yes, we're going round and round.
And then the ride is over and we know that 29 That's exactly it.
for this example the answer is true.
Do we need to remember that we are on a 30 No! Now that we understand how the
carousel? separation of data and action works, we only
need to look at the action part 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 olllyOnions () {
return ooFn.forOnion(8); }
boolean is \;'egetarian () {
return ivFn.forOnion(s); }
boolean onlyOnions () {
return ooFn.forLamb(8): }
boolean is ~'egetarian() {
return ivFn.forLamb(8): }
1::--_._---------------_._---
• class Tomato extends Shish v {
. Shish V 8;
Tomato(Shish V _8) {
8 = _8: }
62 Chapter 4
That's why we call this part boring. 34 True, there'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 OnlyOnions v except for the method
forTomato.
class IsVegetarian v {
boolean forSkewerO {
return true; }
boolean forOnion(Shish D s) {
return s.is VegetarianO; }
boolean forLamb(ShishD s) {
return false; }
boolean forTomato(Shish D s) {
return s.is VegetarianO; }
Are we moving fast? 37 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 chapter 2.
a shish kehah?
class Cheese extends Pizza D {
Pizza D p;
Cheese(Pizza D -p) {
p = -p; }
~
-~-
lL.
So what do we do llext'! \Ve can define the protocol for the Illethods
that belong to Pizza D and its extensions:
n:mA, topAwC, and 8ubAbC.
Chapter 4
Great! Here is the abstract portion of the 41 How innovative! The variants are totally
protocol. mindless, nm\!.
Pizza'D remAO {
return remFn.forCheese(p); }
class Sausage extends Pizza D {
Pizza'D topAwCO {
Pizza'D p;
return topFn.forCheese(p); }
Sausage(Pizza'D _p) {
Pizza'D subAbCO {
p = -Pi }
return subFn.forCheese(p); }
}
Pizza'D remAO {
return remFn.forSausage(p); }
Define the rest. Pizza'D topAwCO {
return topFn.forSausage(p); }
Pizza'D subAbCO {
return subFn.forSausage(p); }
I Pizza D jorSa.'usage(Pizza D p) {
I return new Sausage(p.topAwC()):
}
Ddi!l(, TopAwC v .
TI\(, la.'it Ollt'. SubAbCv ~ is a. piece of cake . Alld we tholli!;ht. W(' wer€' workin~ v... il.h a
pizza pie.
,------ '- "- -"-"--"- - --- "l
class SubAbC v {
I Pizza D jorCru8t() {
return new Crust(); }
Pizza D jorCh cesc( Pizza D p) {
I return new Cheese(p.8IlbAbC O):
. Pizza D jorOlive (Pizza D p) {
I return new Olive(p .subAbCO): }
Pizza D jorAnchovy(Pizza D p) {
I return new Cheese(p.subAbCO ): }
Pizza D jorSalL8ugc(Pizza D p) {
I} return new Sausage(p.8ubAbC(I); }
66 Chapter -4
~o
@!~JJ@@iS~ !~~
l~{@r~l1~~ "i?~@
~
.'?
7.:-
~ 7
-- - -
-.,--
Have we seen this kind of definition before?1 1 What? J\!ore pizza!
-( Top )-
} ---------"
Yes, still more pizza, but this one is different. 2 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.
b it t nil' that t he value of '{(,So The pizza that (,OIlH'S out is the saIll(' <i.'i
new Top(llew Salmon(). the Olll' that gO('S ill. because t })(,I'e HI(' 110
Hew Top(new Tuna(). atl('hovil'S 011 that pizza.
new Bot()))
.r"CInA()
IS
new Top(new SalmonO.
new Top(new Tuna().
new Bot()))?
Ddilll' Ill(' protocol for RemAV. \Vp provi<ic This is ('as~r h~r llOW.
i PieTl,,('rnAO {
I return mF71.jol' To l'(t.r):
Top
70 Clwpter .)
Great. Isn't that easy? 9 Easy and boring.
What part of this exercise differs from 10 Determining how many fields a variant
datatype to datatype? contains. In our case, we had zero and two.
13
Let's define the visitor RemA v. Here are some guesses.
15
And what does We could guess:
new Anchovy().equals(t) "This expression determines whether t is
equal to new AnchovyO."
mean?
The class Object contains a method called If we know that equ.aL'is answer is always
equals. This method compares one Object to false. why bother to use it?
HnothtT, and it ahvays returns false. 1
\Ve lllust define it anew l for all classes whose Okay. How'?
instancps we wish to compare.
For Fish 11 and its variants it works like this. Assuming that
(0 instanceof Tuna)
abstract class Fish D {}
is true when 0 is an instanc(' uf Tuna. t h('s('
method definitions are obvious.
i class Anchovy extends Fish v {
I public 1 boolean equals (Object 0) {
ret urn (0 instanceof Anchovy): }
}
72 Chapter .')
Aren't they? Is every value constructed with 22 Yes. Every such value is an Object, because
new an instance of Object? every class extends Object directly or
indirectly.
If class A extends B, is every value created 23 Yes. and of the class that B extends and so
by new A( . .. ) an instance of class B? on.
Easy, because we want to generalize RemA v 27 \Ve can do that, but vlhen 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.
\Vhat are good names for the more general 28 How about rernPish and Rem Fish v,?
methods and visitor?
:J()
Add the protocol for RemFish v. \Ve designed The rest is routine.
the abstract portion.
r'ernFish(Fish D J) {
r
--~-·--~-··--~-----~----------I
---~------------~---~----~~~
PieD n;mFish(Fish D J) {
return rjFn.for·Top( t,T, f):
To~
\Vhert' do (f) and (t),. J) come from? The f stands for the Fish D we want to
remove ill both ca::-;es. The t and t 11(' rare
the fields of Top; Bot doesll't haw any.
:12
Let's define Rem Fish v and its two methods. Instead of comparing the top layer I of the
pizza to Anchovy, we now ddermill(' whpt her
it equals the Fish D f, which is tll(' additional
value consumed by the md hod.
class RemFish V {
PieD forBot(Fish D J) {
return new Bot(); }
PieD forTop(Object i,Pie D r.FishD f) {
if (J. equ.als ( t ) )
return r. remFish(f);
else
return new Top(t,r.n ·rnFish(f)):
If we add another kind of fish to our Nothing, we just have to n'IlH'llliwJ' to add
datat,ype, what \\!ould happen to the equals to the lIe\V variant.
definition of Rem Fish v?
74 Chapter 5
Let's try it out with a short example: 34 The object is a topping, so we use fo1'Top
new Top( new AnchovyO, from RemFish v.
new Bot())
.1'emFish(new AnchovyO).
Yes. What values does fo1'Top consume? 35 It consumes three values: new AnchovyO,
which is t, the top-most layer of the pizza;
new BotO, which is 1', the rest of the pizza;
and new AnchovyO, which is f, the Fish'D to
be removed.
37
So? Given what f and t stand for, f. equals ( t) is
true. Hence, we must determine the value of
1'. 1'emFish (f).
39
\Vhat does fo1'Bot in Rem Fish v produce? It produces new BotO, no matter what f is.
\\'hat dol'S nrllint do'? It l'ClllOV(,S Integers from pizza pies jllSt as
{'elf/Fish n'llloves fish frol\l pizza pies.
Ddill(' t he visitor Remlnt v. II \Vonderful! \Vc du the illten'st ing t liillg first.
This visitor is allllost identical to Rem Fish v.
\Ve just llced to change tll(' type of what the
two lllethods ('onsllllH'.
class Remlnt v {
I
PieD JmBof(lnteger i) {
. return new Bot(): }
PieD JorTop(Object (,PieD r,lnteger i) {
if (i.eqlloI8(f)) ---
return r.nmlnf(i):
else
return new Top(t,r.l'frn/nt(l)
Docs it IIlatt(>r that this defillitioll uses i awl No, i is just a hdt<>r ltalll<' thall J. 11<) other
not r: IT(l.SOll. As long as we do such substitutions
s,yst(,Illatically. \ve are just fille.
7(; C'lwpteJ" tj
47
Can we remove Integers from PieDs? Yes.
Can we remove Fish D from PieDs? 48 Yes, and we use nearly identical definitions.
Let's combine the two definitions. 49 If we use Object instead of the underlined
Integer above, everything works out.
class Rem v {
PieD JorBot(Object 0) {
return new Bot(); }
PieD JorTop(Object t,Pie D r,Object 0) {
if (o.equals(t)) --
return r.rem{o);
else
return new Top{t,r.rem{o)); }
52
Should we do the protocol for all these Now?
visitors?
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
\Vhy not Rem Fish v and RemAv and They are unnecessary once we have Rem v .
Remlnt v ?
PieD rem(Object 0) {
I returnnrnPn.forTop(t,r,o): }
l}
Ld's rt'llIU\'(' SUlllP thillgs frolll pizza pies: \Vorks like a charlll with the same result as
Hew Top(new Integer(2). before.
Hew Top(new Integer(3),
Hew Top(new Integer(2),
Hew Bot())))
.I'tln(new Integer(3)).
---------------------------------------------------------------------------
Chapter 5
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 BotO))) new BotO))) .
. rem(new Zero())?
60
\iVhat's wrong with that? \iVe expected it to remove new Zero() 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 ,ve introduced equals.
63
Here is the version of Num v (including Adding equals to Zero is easy. Vle use
OneMoreThan) with its own equals. Define instanceof to determine whether the
the new Zero variant. consumed value is a new ZeroO.
fj~)
CorH'ct. \Vhat do we know after if hllii \Ve knO\v that 0 's type is Object and that it
deterlllill('d that is an instance of OneMoreThan.
(0 instanceof OneMoreThan)
is true?
\Vhat is {(OneMoreThan) oj's type? Its type is OneMoreThan, and now it makes
sense to write
((OneMoreThan) o).predN'eSSOf'.
Are () and (( OneMore Than) 0) The underlying object is the sallle. But no.
illterchangea\)le? the t\\'O expressions are not interchangeable,
because the former's type is Object. wherea,...,
the latter's is OneMoreThan.
71
Did you also notice the How do the two uses of predC('fS80r differ?
predc('t'ssor
. ('qua[s(
(( OneMoreThan) 0) .predecessor)
in equals for OneMoreThan?
The first one, predecessor, refers to the 7'2 So the second one,
predecessor field of the instance of (( OneMore Than) 0). predecessor.
OneMoreThan 011 which \ve are using equaLs. refers to the predecessor field of the instance
And that field might not be a OneMoreThan. of OneMore Than consuIllcd hy e(jlw.is.
80 Chapter .5
73
Yes. Are these two objects equal? If they are similar 1 to the same int, they are
equal. But most of the time, they are not.
What is the value of 80 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 Sa Imon () , new BotO))).
new AnchovyO)?
81
What kind of values does substFish consume? It consumes two fish and works on Pievs.
\Vhat. is the value of It i::; the ::;aIIlt' pizza pie wit It all :~::; H'plact'd
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 BotO))) new Top(new Integer(5),
.suhstInt(new Integer(5), new Bot()))).
new Integer(3))?
,;cI D
\Vhat killd of values does 8ub"tInt consullle'! It COllsumes two Integers and works OIl Pie s.
H!:')
And what does it produce? It always product's a PieD.
\Ve call defille SubstFish v. To get from SubstFish v to Substlnt v, \ve just
1 need to substitute FishD by Integer
, class SubstFish v { , everywhere and 'Fish" by "Int" in the cla;.;s
I PieD jorBot(FishDn,Fish D 0) { , and method llames.
, return new Bot(): } I
PieD jorTop(Object t, V
'1 .class Substlnt . {
I PieD r, . PieD jorBot(lntegern,lnteger 0)
FishD n, . I return new Bot(): }
I Fish D 0) { I '. PieD jorTop(Object t,
, if (o.cljual8(t)) . 1 PieD T',
~~:::~ ;~;
return new Top(n.r.8ubsfFish(n,o)):
else I 'I :
Did we furget the borillg parts? ):'es, because there is obviuuslv a more
general versioll like Rem v. .
82 Chapter .5
88
Yes, we call it Subst v. Define it. We substitute Object for Fish D and Integer.
class Subst v {
PieD JorBot(Object 71,Object 0) {
return new BotO; }
PieD JorTop(Object t,
PieD r,
Object 71,
Object 0) {
if (0. equals( t))
return new Top(71,r.subst(n,o));
else
return new Top(t,r.subst(n,o)); }
}
89
Now it is time to add the protocol for Subst v The abstract part is obvious.
to PieD. Here are the variants.
abstract class PieD {
class Bot extends PieD { Rem V remF71 = new RemV();
PieD rem(Object 0) { Subst V substFn = new Subst V ():
return remF71.JorBot( 0); }
PieD subst(Object 71,Object 0) {
return substFn.JorBot(71,o); }
abstract PieD rem(Object 0);
} abstract PieD subst(Object n,Object 0 l;J
class Top extends PieD {
Object t;
PieD r;
Top(Object _t,Pie D _r) {
t= _t;
r = J;}
PieD rem(Object 0) {
ret urn remF71.JorTop (t, r, 0); }
PieD subst(Object 71,Object 0) {
return substF71.jorTop(t,r,71,o); }
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 PieD {
in the previous chapter. PieD rem(Object 0) {
return remFn.jorBot( 0); }
abstract class PieD { PieD subst(Object n,Object 0) {
Rem v remFn = new Rem v 0; return substFn.jorBot( n,o); }
Subst V substFn = new SubstVO;
abstract PieD rem(Object 0);
abstract PieD subst(Object n,Object 0);
} class Top extends PieD {
Object t;
PieD r;
Top(Object _t,Pie D _r) {
t = _t;
r = _r; }
PieD rem(Object 0) {
return remFn.jorTop(t,r,o); }
PieD subst(Object n,Object 0) {
return substFn.forTop(t,r,n,o); }
}
What is the difference between rem and The first one consumes one Object, the
subst in PieD? 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 and hands over the Object it
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 for Top service
subst in the Top variant? from remFn and hands over the field values
and the Object it consumes; subst asks for
the forTop service from substFn and hands
over the field values and the two Objects it
consumes.
Boring Protocols 85
Awl that is all there is to the methods in the () ButremFn and substFn defined in the
variants of a protocol. datatype are still a bit lll}'sterious.
Ll't's !lut ('!'cate remPn and sllbstFn in the This looks like an obvious modificatioll. Thp
datatype. Hew rem and 8ubst now consume a remFn
and a substFn, respcctively. Can the,v still
: abstract class PieD { find jorBot and jor Top , their corresponding
I abstract PieD rem (Rem v rernFn, carousel partners?
• Object 0):
abstract PieD .'illbst(Subst V sllbstFn,
Object 71,
I } Object oj;
'Yes, it is a straightforward trade-off. Instead The definition of the datatype says that they
of adding a I'tcmFn field and a substFn field are a Rem v and a Subst v, respectively. And
to the datat:vpe. we now have rem or subst every Rem v defines j07Bot and JOT'Tojl, and
COnSllllH.' such values. \Vhat kind of values so does every Subst v.
are consuuwd b:v rem and sllbst'?
Here is how it changes Top. In the same manner. \Ve just need to change
each concrete method's description of what it
class Top extends PieD conSUlnes. The rest remains the same.
Object t:
PieD 1'; class Bot extends PieD {
Top(Object _t,Pie D -r) { PieD rem(Rem V remFn,
t = _t: Object 0) {
r = -r: } return remFn.jorBot( 0): }
PieD subst(Subst v substFn,
PieD n'm(Rem V remFn, Object n,
Object 0) { Object 0) {
return remFn.joTTop(t,T',o): return sl1bstFn.forBot( n,o):
PieD 8ubst (Subst v 8ubstFn,
Object n,
Object 0) {
I} return sobstFll.jorTop( t,T', n, 0);
_ _ _ _ _ _.. _ • _ _~_._..O
Chapter 6
That's right. Nothing else changes in the 10 We still have some work to do.
variants. Instead of relying on fields of the
datatype, we use what is consumed.
12
Where are they used? In Rem v and Subst v, the interesting parts,
for example.
Yes. Here is Rem v. 13 That takes all the fun out of it.
What is this all about? 14 Yes, what about it. Copying is easy.
How did we get here? 16 The protocol is that rem in Bot and Top asks
for the JorBot and JorTop methods of
remFn, respectively.
Boring Protocols 87
How does that happen? It happens v,'ith
remFn.forBot( . .. )
and
remFn.forTop( ... ),
respectively.
Corrpct. And now forBot and forTop can 18 Oh. so inside the methods of Rem v. this
rder to the object remFn as this. stands for precisely that instance of Rem v
that allowed us to use those methods ill the
first place. And that must mean that whcn
\ve use r.rem(this, ... ) in for'Top, it tells rem
to use the same instance over again.
\Vhat is the value of \Ve did the same example in the preceding
new Top(new Anchovy(), chapter, and the result remains the same.
new Top(new Integer(3),
new Top(new Zero(),
new BotO)))
.rcm(new RemvO,
new ZeroO)?
22
And how does the underlined part relate to It creates a Rem v object, which correspouds
what we did there'? to the remFn in the old PieD.
\Vhat is the value of \Ve did the same example in the preceding
new Top(new Integer(3), chapter, and the result remains the .'ialIlP.
new Top(new Integer(2),
new Top(new Integer(3),
new BotO)))
.subst(new Subst v 0,
new Integer(5),
new Integer(3))'1
88 Chapter (j
24
And how does the underlined part relate to It creates a Subst V object, 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.
How about some ice cream? 21 Cappuccino crunch sounds great. The more
coffee, the better.
Take a look at subst in Top and at for Top in 28 Nothing really. They get handed back and
Subst v. \\That happens to the values that forth, though forTop compares 0 to t.
they consume?
Is the handing back and forth necessary? 29 We don't know any better way, yet.
Here is a way to define Subst V that avoids the 30 Wow. This visitor has two fields. 1
handing back and forth of these extra values.
class Subst v {
Object n;
Object 0;
Subst v (Object _n,Object _0) {
n = _n;
o = _0; }
PieD forBot() {
return new Bot(); }
PieD forTop(Object t,Pie D r) {
if (o.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 subst.
Boring Protocols 89
How do we create a Subst v? \Ve use
new SubstV(new Integer(5),
new Integer(3)).
\Vhat does that do'? It creates a Subst v whose methods know hmv
to substitute new Integer(5) for all
occurrences of new Integer(3) in Pie l ).
:1:1
How do the methods know that without The values have now become fields of the
consuming more values"? Subst v object to which the methods belong.
They no longer need to be consumed.
:14
Okay. so hmv would \ve substitute all \Ve write
new Integer(3) \vith new Integer(5) in new Top(new Integer(3),
new Top(new Integer(3), new Top(new Integer(2),
new Top(new Integer(2), new Top(new Integer(3).
new Top(new Integer(3). new Bot())))
new Bot())))? .subst(new Subst v (
new Integer(5),
new Integer(3))).
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
That's right. Here are the datatype and its 37 In the Top variant, we still need to hand over
Bot variant. Define the Top variant. both t and r.
Is there anything else missing? 38 \\Ie haven't defined Rem v for this new
protocol. But it is simple and hardly worth
our attention.
vVhat is the difference betvv'een rem and 39 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 \vhether rem is defined a...,
it is or as
PieD reTn(Rem V substFn) {
ret urn substFn.forTop ( t, r): }.
42
True, because substFn is just a name for a Both Rem v and Subst v 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 sam('
types of values. ,\re can think of them as
extensions of a comlllon abstract class.
Boring Protocols 91
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 PieVisito,x {
PieD JorBotO;
PieD JorTop(Object t,Pie D r);
Now that we have an interface that describes 46 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(PieVisitorI pvFn) {
return pvFn.forTop(t,r); }
and
PieD subst(PieVisitorI pvFn) {
return pvFn.JorTop(t,r); }
in Top.
Correct. \Vhat is the difference between rem 47 There isn't any. We can use the sanIE' name
and subst, now'? for both, as long as we remember to use it
whenever we would have used rem or subst.
\Vhat is a good name for this method'? 48 The method accepts a visitor and asks for its
services, so we call it accept.
92 Chapter 6
And what is a better name for pvFn? 49 Easy: ask, because we ask for services.
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.
Correct. They are just icing. 54 Okay, we still won't forget them.
Boring Protocols 93
Now define the new Subst v . 55 Here it is.
Object 0; i
SubstV(Object _n,Object _0) {
' - - - - - - - - - -
57
\Vhy is there is a line, not an arrow, from The Subst v visitor implements PieVisitorI ,
Sub~t v to PieVisitorX? 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
What is the value of 59 Easy:
new Top(new AnchovyO, new Top(new Salmon(),
new Top(new TunaO, new Top(new TunaO,
new Top(new AnchovyO, new Top(new SalmonO,
new Top(new TunaO, new Top(new TunaO,
new Top(new AnchovyO, new Top(new Anchovy(),
new BotO))))) new Bot()))))) .
. accept(new LtdSubst V (2,
new SalmonO,
new AnchovyO))?
60
Explain what LtdSubst V produces. 1 The methods of LtdSubst v 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 LtdSubst v.
we pronounce it.
Good. Define LtdSubst v. 61 That's easy. \Ve have such a flexible protocol
that we only need to define the essence now.
Boring Protocols 95
\Vhat is the value of 62 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 Anchovy(), new Top(new TunaO,
new Top(new TunaO, new Top(new Salmon(),
new Top( new AnchovyO, new Top(new Tuna().
new BotO))))) new Top(new SalmonO,
.accept(new LtdSubst V(2, new Bot () ) ) ) ) ).
new SalmonO,
new AnchovyO))?
64
\Vhy doesn't c ever change? Because this, the LtdSubst V that performs
the substitutions, never changes.
Can we fix this? 65 We can't change this, but \ve carl replace
this with a new LtdSubst v that reflects the
change.
96 Chapter 6
67
Define the new and improved version of Voila.
LtdSubst v.
class LtdSubst v implements PieVisitorI {
int c;
Object n;
Object 0:
LtdSubstv(int _c,Object _n,Object _0) {
c = _c:
n = _n:
o = _0: }
68
How does They are two different LtdSubst v s. One
this replaces c occurrences of 0 by n in a pizza
pie, and the other one replaces only c - 1 of
differ from
them.
new LtdSubstV(c - 1,n,0)?
How do you feel about protocols now? 69 They arc exciting. Let's do more.
Boring Protocols 97
Is Yes.
new Flat(new AppleO,
new Flat(new PeachO,
new BudO))
a flat Tree v ?
2
Is Yes, it is also a flat Tree v .
new Flat( new PearO,
new BudO)
a flat Tree v ?
Is the difference between flat trees and split Unless there is anything else to TreeD, it's
trees obvious now? totally clear.
Oh My! 99
Here are some fruits. It does not differ too much from what we
have seen before.
abstract class Fruit D {}
~
1
a_b_s_t_r_a_c_t_c_Ias
,-I __s_T_re_e_D_{}_ _ _ _ _
class Peach extends Fruit D {
public boolean equals(Object 0) {
return (0 instanceof Peach); }
D
c_I_a_s_s_B_u_d_e_x_t__e_n_d_s_T_re_e__{_}_ _ _
,-I ~
class Flat extends TreeD {
Fruit D I;
class Apple extends Fruit D { TreeD t;
public boolean equals (Object 0) { Flat(Fruit D _1,Tree D _t) {
return (0 instanceof Apple); } I = -I;
t = _t; }
}
class Pear extends Fruit D {
public boolean equals(Object 0) {
return (0 instanceof Pear); } class Split extends TreeD {
TreeD l;
TreeD r;
Split(TreeD _l,Tree D _r) {
class Lemon extends Fruit D { 1 = _l;
public boolean equals(Object 0) { r =];}
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 Fruit D? compare fruits and other things.
Do TreeD's variants contain equals? No, which means we won't compare them,
but we could.
100 Chapter 7
How does the datatype TreeD differ from all 10 The name of the new datatype occurs twice
the other datatypes we have seen before? in its Split variant.
Let's add a visitor interface whose methods 11 That just means extending what we have
produce booleans. with one method each.
Oh My! 101
13
How many methods does the definition of Three, because it works with Treevs, and the
blsFlat v c'ontain, assuming it implements datatype definition for Treevs has three
bTreeVisitorI ? variants.
14
\Vhat type of values do the methods of booleans.
blsFlat v produce?
16
Here is a skeleton for blsFlat v . That's easy now.
17
Define the blsSplit v visitor, whose methods Here is the easy part.
check whether a Tree v is constructed with
Split and Bud only. blsSplit V implements bTreeVisitorI {
public
boolean JOTBud() {
return true; }
public
boolean JOTFlat(Fruit V J,Tree v t) {
return false; }
public
boolean JOTSplit(Tree v I,Tree v 1') {
---}
102 Chapter 7
What is difficult about the last line? 18 We need to check whether both I and r art'
split trees.
20
And then? Then we need to know that both are true.
23
Finish the definition of blsSplit V using Now we can do it.
if ( ... )
class blsSplit v implements bTreeVisitorI {
return ...
public
else
return ... boolean jorBudO {
return true: }
public
boolean jorFlat(Fruit V j,Tree D t) {
return false; }
public
boolean jorSplit(Tree D l,Tree D r) {
if! (l.accept(this))
return r.accept(this);
else
return false: }
Dh lvIy! 10~3
Give an example of a TreeD for which the There is a trivial one:
methods of blsSplit v respond with true. new BudO.
25
Hmv about OIle \'lith five uses of Split? Here is one:
new Split(
new Split(
new BudO ,
new Split(
new BudO ,
new BudO)L
new Split(
new Bud(L
new Split(
new BudO ,
new BudO))).
I class bHasFruit v
, implements bTreeVisito~ {
public
boolean jorBud() {
I
return false; }
public
boolean jorFlat(Fruit D j,Tree D t) {
return true; }
public
boolean jorSplit(Tree D l,Tree D r) {
if! (l.accept(this))
return true;
b_ else
return r. accep! (this); }
104 Chapter 7
What is the height of 28 3.
new Split(
new Split(
new BudO,
new Flat(new LemonO,
new BudO)),
new Flat(new FigO,
new Split(
new BudO,
new BudO)))?
30 1.
\\That is the height of
new Flat(new LemonO,
new BudO)?
So what is the height of a TreeD? 32 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 iHeight v work on a TreeD? Yes, and they produce an into
Is that what the i in front of Height is all 34 It looks like i stands for int, doesn't it?
about?
Ob A1y! 105
35
What is the value of 4.
new Split(
new Split(
new BudO,
new BudO),
new Flat(new FigO,
new Flat(new LemonO,
new Flat(new AppleO,
new Bud()))))
.accept(new iHeightVO)?
:17
And how do we get from 3 to 4? \Ve need to add one to the larger of the
numbers so that we don't forget that the
original TreeD wa..'> constructed with Split and
those two TreeDs.
1 38
u picks the larger of two numbers, x and y. Oh, that's nice. vVhat kind of methods does
iHeight v define?
1 \Vhen you enter this in a file, use
Hath.~ax(x.y).
Hath is a class that contains max as a (static) method.
39
iHeightV,s methods measure the heights of Now that's a problem.
the TreeDs to which they correspond.
106 Chapter 7
Why? 40 We defined only interfaces that produce
booleans in this chapter.
Okay, so let's define a visitor interface that 42 It's almost the same as bTreeVisitorX.
produces ints.
interface iTreeVisitorX {
int jorBudO;
int jorFlat(Fruit D j ,TreeD t);
int jorSplit(Tree D l,Tree D 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?!
We can have two methods with the same 44 bTreeVisitorI is indeed different from
name in the same class as long as the types iTreeVisitorL , so we can have two versions of
of the things they consume are distinct. accept in TreeD.
45
Add the new accept methods to TreeD,s It is easy.
variants. Start with the easy one.
class Bud extends TreeD {
boolean accept ( bTree VisitorX ask) {
return ask.jorBud(); }
int accept(iTreeVisitorX ask) {
return ask.forBud(); }
OhMy! 107
The others are easy, too. We duplicate 46 We must also change the type of what the
accept. new accept method consumes and produces.
boolean accept (b Tree VisitorI" ask) { boolean accept (b Tree VisitorI ask) {
return ask.forFlat(f,t); } ret urn ask.forSplit (l, r); }
int accept(iTreeVisitorI" ask) { int accept(iTreeVisitorI ask) {
return ask.forFlat(f ,t); } return ask.forSplit(l,r); }
'----__________J
47
Here is iHeight v. That's easy now.
108 Chapter 7
vVhat is the value of 50 If the visitor tSubst v substitutes apples for
new Split( figs, here is what we get:
new Split( new Split(
new Flat(new FigO, new Split(
new BudO), new Flat(new Apple(),
new Flat(new FigO, new BudO),
new BudO)), new Flat( new Apple(),
new Flat(new FigO, new BudO)),
new Flat(new LemonO, new Flat(new AppleO,
new Flat( new AppleO, new Flat(new LemonO,
new BudO)))) new Flat(new AppleO,
. accept ( new Bud())))).
new tSubst v (
new AppleO,
new FigO))?
Correct. Define the tSubst v visitor. 51 It's like SubstFish v and Substlnt v 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 \\'e need to add
yet another interface.
interface tTreeVisitorI {
TreeD jorBudO;
TreeD jorFlat(Fruit D j,Tree D t);
TreeD jorSplit(Tree D [,TreeD r);
}
Good job. How about the datatype TreeD. 53 Easy. Here is the abstract one.
OhMy! 109
54
Define the variants of TreeD. No problem.
I
' class Bud extends TreeD {
boolean accept ( b Treev, isitorI ask) {
I return ask.forBudO: }
'I' int accept(iTreeVisitorI ask) {
return ask.forBudO; }
TreeD accept (t TreeVisitorI ask) {
I} return ask.J01Bud(); }
I----------------------------~
class Split extends TreeD { I
[
, TreeD l;
TreeD r' I
Split(Tree D _l,Tree D _r) {
I
I ~ :: ~;; } I
LI
j
110 Chapter 7
55
Then define tSubst v. That's easy, too. It has two fields, one for
the new Fruit D and one for the old one. and
the rest is straightforward.
56
Here is a TreeD that has three Figs: Even the visitors are no longer interesting.
new Split(
new Split( class iOccurs v implements iTreeVisito~ {
new Flat(new FigO, Fruit D a;
new BudO), iOccursV(Fruit D _a) {
new Flat(new FigO, a = _a: }
new BudO)),
new Flat(new Fig(), public int JorBudO {
new Flat(new Lemon(), return 0; }
new Flat(new AppleO, public int JorFlat(Fruit D J,Tree D t) {
new Bud())))). if (f. equals ( a) )
return t.accept(this) + 1;
Now define iOccursv , whose methods count else
how often some Fruit D occurs in a tree. return t.accept(this); }
public int JorSplit(Tree D I,Tree D r) {
return
l.accept(this) + r.accept(this); }
Dh Aly! 111
57
Do you like your fruit with yogurt? \Ve prefer coconut sorbet.
Is it disturbing that we have three nearly 5" 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 ot her.
6U
Remember Integer and Boolean? They make Yes, Boolean is the class that corresponds to
it possible. boolean, and Integer corresponds to into
-l
61
Here is the interface for a protocol that Here they are.
produces Object in place of boolean, int,
and TreeD.
interface TreeVisito~ {
[ class Flat extends TreeD {
FruitD f;
TreeD t;
--l
Object forBudO; Flat(Fruit D _f,Tree D -t) {
Object forFlat(Fruit D f,Tree D t); f = -1; :
I } Object !orSplit(TreeD I,TreeD r);
_ _ _J _t_=
__-t_:_}_____________
Object acccpt(TreeVisitor I ask) {
!
Here is the datatype and the Bud variant. return ask.forFlat(J,t); }
112 Chapter 7
Good. Now define IsFlat v, an Object 62 That's no big deal.
producing version of blsFlat v.
class IsFlat v implements TreeVisitorI {
public Object forBudO {
return new Boolean(true); }
public Object forFlat(Fruit V f,Tree v t) {
return t.accept(this); }
public Object forSplit(Tree V l,Tree v r) {
return new Boolean(false); }
}
And how about IsSplit v? 63 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 IsSplit v implements TreeVisitorI { 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(Fruit V f,Tree v t) { converted an Object to a OneMoreThan.
return new Boolean(false); }
public Object forSplit(Tree V l,Tree v r) {
if (( (Boolean) (l. accept (this)))
. boolean Value 0)
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
Progmmming Languages, 146-159. Paris, 1997).
Will the conversion always work? 65 Yes, because the Object produced by
l.accept(this) is always a Boolean.
Oh My! 113
Did you think that was bad? Then study 66 Oh my!
this definition during your next break .
114 Chapter 7
""Vhat is the value of 1 12.
(7+((4-3)x5))?
\\,'here do the constructors come from? A datatype and its variants that represent
arithmetic expressions.
6
\Vhat is the value of {7,5}.
({7,5} U (({4} \ {3}) n {5}))?
\\There do the constructors come from? A datatype and its variants that represent
set expressions.
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))).
118 Chapter 8
14
Good answer. Here is the datatype now.
class Plus extends Expr'D {
abstract class Expr'D { Expr'D l;
abstract Expr'D r;
Object accept(ExprVisitorI ask); Plus(Expr'D _1,Expr'D _r) {
1= _l:
r = _r; }
Define the variants of the datatype and equip
them with an accept method that produces Object accept(ExprVisitorI ask) {
Objects. return ask.forPlu.s(l,r); }
How do we add \Ve have done this before. \Ve use tile
new Integer(3) method int l"altw to determille the ints that
and correspond to the Integers. and then add
new Integer(2)? them together.
How do we turn that into an Integer? \Ve use new Integer( ... ).
120 Chapter 8
20
How does forPlus work? It consumes two Exprvs, determines their
respective values, and pluses them.
How are the values represented? 21 As Objects, because ,ve are using our most
general kind of (and most recent) visitor.
24
Can we add Objects? No~ we must convert them to Integers first
and extract their underlying ints.
2."
Can we convert all Objects to Integers? No, but all Objects produced by IntEval V are
made with new Integer( ... ). so that this
conversion always succeeds.
Is that true? \Vhat is the value of 26 Wow. At some leveL this is nonsense.
new Plus(
new Const(new Empty()),
new Const(new Integer(5)))
.accept(new IntEvaIVO)?
r
((Integer)i) .int Value() ,~~~s IntEva~ implements DE. xprVisitor
D
I
{
122 Chapter 8
30
That one was pretty easy, wasn't it? Yes. Let's implement an ExprVisitorL for sets.
\\That do we need to implement one for sets? 31 We certainly need methods for plusing,
diJJing, and pmding sets.
Do we need to understand that? 35 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); }
Set V plus(SetV t) {
return s.plus( t.add( i)); }
Set V diff(Set V t) {
if (t.mem(i))
return s.diff(t);
else
return s.diff(t).add(i); }
Set V prod(SetV t) {
if (t.mem(i))
return s.pTod(t).add(i);
else
return s.prod(t); }
124 Chapter 8
Do we need to understand these definitions? 37 Not now, but feel free to think about them
when you have the time. \\Fe haven't even
used visitors to define operations for union,
set-difference, and intersection, but we trust
you can.
What do we have to change in IntEval V to 38 Not much, just plus, diff, and prod.
obtain SetEval V , an evaluator for set
expressions?
How should we do that? 39 Oh, that's a piece of pie. We just copy the
definition of IntEval V and replace its plus,
diff, and prod methods.
Why should we throwaway more than half 41 That's true. If we copied the definition and
of what we have? changed it, we would have identical copies of
JorPlus, JorDifJ, JorProd, and JorConst. \Ve
should reuse this definition.1
1 Sometimes we do not have license to see the definitions, so
copying might not even be an option. .
Yes, and we are about to show you better 42 That part is easy:
ways. How do we have to change plus, diff, Object plus(Object l,Object r) {
and prod? return ((SetV)l).plus((SetV)r); }
and
Object difJ(Object l,Object r) {
return ((SetV)l).diff((SetV)r); }
and
Object prod(Object l,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 IntEval v .
How many methods from IntEval v are not Four: forPlw" forDiff, forProd, and
overridde~ in SetEval v ? forConst.
126 Chapter 8
That's correct. What is the value of 51 Interesting question. How does this work
new Prod( now?
new Const(new EmptyO
. add (new Integer(7))),
new Const(new EmptyO
.add(new Integer(3))))
. accept (new SetEval V O)?
53
And what does accept consume? An instance of SetEval V , but its type is
ExprVisito,x .
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 Integer(7))) prod(A,B) .
. accept (this)
and
new Const(new EmptyO
.add(new Integer(3)))
. accept (this).
What would we have to evaluate next?
So far, we have always used a method on a 58 That's true. What is the object with which
particular object. we use prod(A,B)?
59
It is this object. Oh, does that mean we should evaluate
new SetEvaIVO.prod(A,B)?
Good. And now what? 61 Now we still need to determine the values of
new Const(new EmptyO
.add(new Integer(7)))
. accept( this)
and
new Const(new EmptyO
.add(new Integer(3)))
. accept (this).
62
The values are obviously It, too, is in IntEval v .
new EmptyO
.add(new Integer(7))
and
new EmptyO
.add(new Integer(3)).
\Vhere is the definition of forConst that
determines these values?
63
Here is the next expression in our sequence: The object is an instance of SetEval v ) which
new SetEval v 0 overrides the prod method in IntEval v with
. prod(new EmptyO its own .
.add(new Integer(7)),
new EmptyO
.add(new Integer(3))).
\Vhere does prod come from?
128 Chapter 8
What next? 64 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
((Set V )( l.accept(this)))
. prod ( (Set V) 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 Set v ?
And that is why the method must contain a 67 This example makes the need for conversions
conversion from Object to SetVs. obvious again.
Time for the last question. Where does this 68 This one belongs to Set V or more precisely
prod come from now? its Empty and Add variants.
And what does prod do? 69 It determines the intersection of one Set V
with another Set V , but didn't we agree that
the previous question ,vas the last question
on that topic?
73
But just because something works, it doesn't Yes, let's do better. \Ve have defined all
mean it's rational. these classes ourselves, so we are free to
rearrange them any way we want.
\Vhat distinguishes IntEval V from SetEval v ,? 74 The methods plus, diff, and prod.
75
\Vhat are the pieces that they have in They share the methods forPlus, forDiff,
common'? forProd, and forConst.
Good. Here is how \ve express that. 76 Isn't this abstract class like Point D ?
130 Chapter 8
Yes, we can think of it as a datatype for 77 What do we do now?
EvalD visitors that collects all the common
elements as concrete methods. The pieces
that differ from one variant to another are
specified as abstract methods.
We define IntEval V extending EvalD. 78 It is basically like the original but extends
EvalD, not IntEval v .
class IntEval V extends EvalD {
Object plus(Object l,Object r) { class SetEval v extends EvalD {
return Object plus(Object l,Object r) {
new Integer( return ((Set D)l).plus((Set D )7'); }
((Integer) l). int Value() Object dijJ(Object l,Object r) {
+ return ((SetD)l).dijJ((SetD)r); }
((lnteger)r).intValue()); } Object prod(Object l,Object r) {
Object diff(Object l,Object r) { return ((SetD)l).prod( (SetD)r); }
return
new Integer(
((Integer)l). int Value()
((lnteger)r).intValue()); }
Object prod(Object l,Object r) {
return
new Integer(
((Integer)l) .int Value 0
*
((lnteger)r).intValue()); }
Define SetEval v .
Is it natural for two evaluators to be on the 79 :Much more so than one extending the other.
same footing?
Time for supper. 80 If you are neither hungry nor tired, you may
continue.
Object 0; Object n:
Subst v (Object _n,Object _0) { Object 0;
n = _n; ltdSubst v (int _c,Object _n.Object _0) {
o = _0; } c = _c; I
n = _n; I
public PieD forBotO { o = _0; }
return new Bot(); }
public PieD forTop(Object t,Pie D r) { public PieD forBotO {
if (o.equals(t)) return new Bot(); }
return public PieD JorTop(Object t,Pie D r) {
new Top(n,r.accept(this)); if (c == 0)
else return new Top(t,r);
return else
new Top( t,r. accept(this)); } if (o.equals(t))
return
new Top (n,
r.accept(
new LtdSubstv(c - 1,n,o))):
else JI
~ r::~nTOP~'TaccePt(thiS)); 1__
\Vhat do the two visitors have in common? 82 :t\lany things: n, 0, and forBot.
83
\\There do they differ? They differ in forTop, but LtdSubst v also has
an extra field.
And where do we put the pieces that two 84 \Ve put them into an abstract class.
classes have in common?
\\rhat else does the abstract class contain 7 85 It specifies the pieces that are different if
they are needed for all extensions.
132 Chapter 8
Define the abstract class Subst D , which 86 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 D
in addition. implements PieVisitorI {
Object n;
Object 0;
public PieD jorBotO {
return new Bot(); }
public
abstract PieD jorTop(Object t,Pie D r);
Do the two remaining classes still have things 88 No, but the constructors have some overlap.
in common? Shouldn't we lift the Subst V constructor into
Subst D , because it holds the common
elements?
134 Chapter 8
That's neat. How about some art work? 91 Is this called a pie chart?
accept
- - - - - - - - PieVisito,x
93
Is it also possible to define LtdSubst v as an It may even be better. In some sense,
extension of Subst v ? LtdSubst v just adds a service to Subst v: It
counts as it substitutes.
lJ
return
new Top(t,r.accept(this)); } new LtdSubstV(c - 1,n,0))):
96
Let's draw a picture. Fine, and don't forget to use lines, rather
than arrows, for implements.
accept
- - - - - - - - PieVisitor2"
PieD
136 Chapter 8
@o
~~ @ ~@)~)ru ~~ff
Remember Point v ? If not, here is the It has been a long time since we discussed
datatype with one additional method, minus. the datatype Point V 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 Point V {
abstract class Point V { CartesianPt(int _x ,int _y) {
int x; super(_x,_y); }
int y;
PointV(int _x,int _y) { int distanceToOO {
x = _x; return l Jx
2
+ y2J; }
y = -y; }
boolean closerToO(Point V p) {
return class ManhattanPt extends Point V {
distanceToOO :s; p.distanceToOO; } ManhattanPt(int _x,int _y) {
Point V minus(Point V p) { super(_x,_y); }
return
new CartesianPt(x - p.x,y - p.y); } int distanceToO() {
abstract int distance ToO(); return x + y; }
}
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.distanceToO refers to the Okay. That means we just add x and y when
method definition of distanceToO that is we evaluate super.distanceToOO.
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 0.
. distance ToOO?
140 Chapter 9
Precisely. Now take a look at this extension 10 Nothing. \Ve just discussed this kind of
of CartesianPt. constructor for ShadowedManhattanPt.
class ShadowedCartesianPt
extends CartesianPt {
int ~x;
int ~y;
ShadowedCartesianPt(int _x,
int _y,
int -~x,
int _~y) {
super(_x,_y);
Llx = _Llx;
~y = -~y; }
int distanceToOO {
return
super. distance ToOO
+
lV~; + ~~J; }
Does this explain how distanceToO should 16 Completely. It should make a new
mea"ure the distance of a CartesianPt by adding the corresponding
ShadowedCartesianPt to the origin? fields and should then measure the distance
of that new point to the origin.
17
Revise the definition of ShadowedCartesianPt Okay.
accordingly. r--
class ShadowedCartesianPt
extends CartesianPt {
int ~x;
int ~y;
ShadowedCartesianPt(int _x,
int _y,
int -~'r,
int _~y)
super(_x,_y);
~x = -~;r;
~y = -~y; }
int distanceToOO {
return
new CartesianPt(x + ~x.y + ~y)
.d'istanceToO(); }
III
Do \ve still need the new CartesianPt after No, once we have the distance, \ve have no
distance ToO has determined the distance'? need for this point. 1
142 Chapter 9
19
Correct. \Vhat is the value of true,
new CartesianPt(3,4) because the distance of the CartesianPt to
.closerToO( the origin is 5, while that of the
new ShadowedCartesianPt(1,5,1,2))? ShadowedCartesianPt is 7.
21
Is the rest of this chapter obvious, too? What?
That was a hint that now is a good time to 22 Oh. \Vell, that makes the hint obvious.
take a break.
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(ShapeVisitorI ask);
I interface ShapeVisito,x {
: boolean forCircle(int r);
boolean forSquare(int s);
boolean forTrans(Point V q,Shape v s);
}
27
Yes and we will need this third variant. Okay, now this looks pretty straightforward,
I: class but what's the point?
Trans! extends Shapev {
Point V q;
ShapeV s;
Trans(Point V _q,Shape V _s) {
q = -q:
s = _s; }
28
Let's create a circle. No problem:
new Circle(10).
How should we think about that circle? 29 \Ve should think about it as a circle ,\lith
radius 10.
Good. So how should we think about 30 \Vell, that's a square whose sides are 10 units
new Square(10)? long.
\Vhere are our circle and square located? 31 \Vhat does that mean?
144 Chapter 9
Suppose we wish to determine whether some 32 In that case, we must think of the circle as
CartesianPt is inside of the circle? being drawn around the origin.
And how about the square? 33 There are many ways to think about the
location of the square.
That will do. Is the CartesianPt with x 35 Yes, it is. but barely.
coordinate 10 and y coordinate 10 inside the
square?
And how about the circle? 36 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.
This is where Trans comes in. What is 38 Aha. With Trans we can place a circle of
new Trans( radius 10 at a point like
new CartesianPt(5,6), new CartesianPt(5,6).
new Circle(lO))?
How do we determine whether some point is 42 If the square is located at the origin, it is
inside a square? simple. We check whether the point's x
coordinate is between 0 and s, the length of
the side of the square.
45
Let's take a look at our circle around \Ve can if we translate all other points by an
new CartesianPt(5,6) appropriate amount.
again. Can we think of this point as the
origin?
46
By how much? By 5 in the x direction and 6 in the y
direction, respectively.
How could we translate the points by an 47 \Ve could subtract the appropriate amount
appropriate amount? from each point.
48
Is there a method in Point V that Yes. Is that why we included minus in the
accomplishes that? new definition of Point v ?
146 Chapter 9
Indeed. And now we can define the visitor 49 The three methods put into algebra what we
HasPt v, whose methods determine whether just discussed.
some ShapeD has a Point D inside of it.
\\That is the value of 50 We said that this point wasn't inside of that
new Circle(lO) circle, so the answer is false .
. accept (
new HasPt V(new CartesianPt(lO,lO)))?
51
Good. And what is the value of true.
new Square(lO)
. accept (
new HasPt V(new CartesianPt(lO,lO)))7
52
Let's consider something a bit more We already considered that one, too. The
interesting. \\That is the value of value is true, because the circle's origin is at
new Trans( new CartesianPt(5,6).
new CartesianPt(5,6),
new Circle(lO))
. accept (
new HasPt v (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 false.
55
And then? Second, we need to look at
new Circle(lO)
.accept(
new HasPtV(new CartesianPt(O,O))),
but the value of this is obviously true.
Very good. Can we nest Trans three times? 56 Ten times, if we wish, because a Trans
contains a ShapeD, and that allows us to nest
things as often &'3 needed.
57
Ready to begin? \Vhat? \Vasn't that it?
No. The exciting part is about to start. 58 \Ve are all eyes.
59
How can we project a cube of cheese to a It becomes a square, obviously.
piece of paper?
148 Chapter 9
Can we think of the two objects as one? 61 We can, but we have no way of saying that a
circle and a square belong together.
Here is our way. 62 That looks obvious after the fact. But why is
there a blank in accept?
class Union extends Shape v {
Shape v s;
Shape v t;
Union(Shape V _s,Shape v _t) {
63
\Vhat do we know from Circle, Square, and We know that a ShapeVisito~ 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.
64
So what should we do now? We need to change ShapeVisitorI so that it
specifies a method for the Union variant in
addition to the methods for the existing
variants.
Correct, except that we won't allow ourselves 65 Why can't we change it?
to change ShapeVisitorI .
Just to make the problem more interesting. 66 In that case, we're stuck.
G8
Basically.l This extension produces an Does that mean accept in Union should
interface that contains all the obligations receive a UnionVisitorI, so that it can use the
(i.e., Hames of methods and \vhat they for'Union method?
consume and produce) of ShapeVisitorL" and
the additional one Ilamed forUnion.
69
Yes it should, but because UnionVisitorI \Ve have been here before. Our accept
extends ShapeVisitorI, it is also a method must consume a ShapeVisitorT and
ShapeVisitorL". fortunately every UnionVisitorL" implements a
ShapeVisitorI , too. But if we know that
accept consumes a UnionVisitorI , we can
convert the ShapeVisitorI to a UnionVisitorI
and invoke the forUnion method.
150 Chapter 9
Let's create a Union shape. 71 That's 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 UnionVisitorI ? No. It does not provide the method
fo,Union.
74
Define UnionHasPt V, which extends HasPt V Here it is. Its method checks whether the
with an appropriate method fo,Union. point is in one or the other part of a union.
The other methods come from HasPt v.
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
I class UnionHasPt V UnionVisitorI. And, as we have said before,
extends HasPt v the addition of public is necessary, because
implements UnionVisitorI { 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 forTrans 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(lO),
new Circle(lO)))
. accept(
new UnionHasPt V(
new CartesianPt(13,17)))?
152 Chapter 9
Okay. And what should be the answer? 80 It should be true.
Let's see whether the value of 81 Usually we start by determining what kind of
new Trans( object we are working with.
new CartesianPt(3,7),
new Union(
new Square(10),
new Circle(10)))
. accept (
new UnionHasPt v (
new CartesianPt( 13, 17)))
is true?
82
And? It's a ShapeD.
How do \ve find the appropriate for'Union In accept) which is defined in Union, we
method? confirm that
new HasPt V (
new CartesianPt(lO,lO))
is a UnionVisitorI and then invoke its
forUnion.
91
Is an instance of HasPt V a UnionVisitorI ? No!
~)3
Then what is the value of It doesn't have a value. \Ve are stuck. 1
new Union(
new Square(lO), 1 A Java program raises a RuntimeException, indicating
new Circle(lO)) that the attempt to confirm the UnionVisitorIlItSS of the
object failed. t-.lore specifically, we would see the following
. accept ( when running the program:
new HasPt V ( java . lang . ClassCastException: UnionHasPtV
at Union.accept( ... java: ... )
new CartesianPt(lO,lO)))'1 at UnionHasPtV.forTransC. .. java: ... )
at Trans.accept( ... java: ... ).
94
\Vhat do we do next'? Relax. Read a novel. Take a nap.
95
vVhich of those is best? You guessed it: whatever you did is best.
\Ve should have prepared this extension in a ~J6 How could we have done that?
better way.
154 Chapter 9
97
Here is the definition of HasPt v that we In two ways. First, it contains a new
should have provided if we wanted to extend . method: newHasPt. Second, it uses the new
it without making changes. method in place of new HasPt v in for Trans .
98
Good. What does newHasPt produce? A new ShapeVisito~, as its interface implies.
99
And how does it produce that? By constructing a new instance of HasPt v.
Very well. But how does that help us \vith That's not obvious.
our problem"?
Ill:!
Can we override newHasPt when we extend Yes, we can override any method that we
HasPt v ? wish to override.
Let's override newHasPt in UnionHasPt v. \Vhen we override it, we need to make sure it
produces a ShapeVisitorX.
That's true. Should it produce a HasPt v or a The latter. Then forTrans in HasPt v keeps
UnionHasPt v? producing a UnionHasPt V , if we start with a
UnionHasPt v.
Good answer. Should we repeat it? 106 Let's just reread it.
156 Chapter 9
And that's exactly what we need. Revise the 107 Here it is.
definition of UnionHasPt V. 1 ,------------------------,
class UnionHasPt v
extends HasPt v
implements UnionVisito,x {
UnionHasPt V(Point V _p) {
super( _p ); }
ShapeVisito,x newHasPt(Point V p) {
return new UnionHasPtV(p); }
public
boolean !orUnion(Shape V s,Shape v t) {
if (s.accept(this))
return true;
else
return t.accept(this); }
If we assemble all this into one picture, what 108 A drawing that helps our understanding of
do we get? the relationships among the classes and
interfaces.
accept
------- - - - - - - - ShapeVisitorI
What does the box mean? 109 Everything outside of the box is what we
designed originally and considered to be
unchangeable; everything inside is our
extension.
Let's see whether this definition works. 112 We remember that the shape was built with
\Vhat is the value of Trans.
new Trans(
new CartesianPt(3,7),
new Union(
new Square(lO),
new Circle(lO)))
. accept (
new UnionHasPt V (
new CartesianPt(13,17)))?
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
And what do we create? 118 The new UnionVisitorI:
new UnionHasPt V (
new CartesianPt(lO,lO)).
Is it good to have extensible definitions? 123 Yes. People should use extensible definitions
if they want their code to be used more than
once.
Very well. Does this mean we can put 124 Yes, we can and should always do so.
together flexible and extensible definitions if
we use visitor protocols with these
constructor-like methods?
Are you hungry yet? 126 Are our meals ever finished?
I
Have you ever wondered where the pizza pies You should have. because someone needs to
come from? make the pie.
Here is our pizza pieman. 2 This is beyond anything we have seen before.
return occTop(t); }
public int rem Top (Object t) {
p = (PieD)p.accept(new RemV(t))
return occTop(t); }
public int substTop(Object n,Object 0) {
p = (PieD )p. accept( new Subst v (n, 0))
return occTop(n); }
public int occTop(Object 0) {
return
((Integer)p. accept (new Occurs v (0)))
.int Value (); }
3
How so? Haven't we seen PieD, Top, and Bot We have seen them.
before?
And haven't we seen visitors like Rem v, Yes, yes. But what are the stand-alone
Subst V , and Occurs v for various datatypes? semicolons about?
Let's not worry about them for a while. 5 Fine, but they are weird.
interface Pieman I {
int addTop(Object t);
int remTop(Object t);
int HubstTop(Object n,Object 0);
int occTop(Object 0);
!l
i1-~·~--_ - - - .- - .- - - - - - - - :
Here are PieVisitorI and PieD. They are very familiar.
l
interface PieVisitorI { :lass Bot extends PieD {
Object jorBot(); Object accept(PieVisitorI ask) {
I Object jOT'Top(Object LPieDr); , return ask.forBot(); }
}
}__ __ ____________ _ ______J
abstract class PieD { class Top extends PieD {
abstract Object t;
Object accept(PieVisitor I ask); PieD r;
Top(Object _LPie D _T) {
t = _t;
Define Bot and Top. r = _r; }
162 Chapter 10
Here is Occu rs v. It counts how often some And this little visitor substitutes one good
topping occurs on a pie. topping for another.
10
Great! Now we have almost all the visitors \\;'e remember that one. too.
for our pieman. Define Rem v, which removes
a topping from a pie. class Rem v implements PieVisitorT {
Object 0;
RemV(Object _0) {
o = _0: }
12
\Vhich pie? The pie named p in the new Pieman· VI .
14
And what is the value of That's where those stand-alone semicolons
new Pieman,VlO.addTop(new AnchovyO)? come in again. They were never explained.
15
True. If we wish to determine the value of Yes, we must understand that. There is no
new Pieman-VlO.addTop(new AnchovyO), number x in the world for which
x = x + 1,
we must understand what
p = new Top(new Anchovy(),p) so why should we expect there to be a Java p
such that
return occTop(new AnchovyO) 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.
Here it means that p changes and that future 17 And the change is that p has a new topping,
references to p reflect the change. right?
18
\Vhen does the future begin? Does it begin below the stand-alone
semicolon?
164 Chapter 10
And how many are there? 20 We added one, so the value is 1.
No. it's not. Take a close look. We created a 22 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.
Yes, there is. Take a look at this: 23 Okay, y stands for some pieman.
Pieman I y = new PiemanMO.
And now what is the value of 25 Still 1. According to the rules of semicolon
y.substTop(new TunaO,new AnchovyO)? and =, this replaces all anchovies on p with
tunas, changes p, and then counts how many
tunas are on p.
Correct. So what is the value of 26 0, because y's pie no longer contains any
y.occTop(new AnchovyO)? anchovies.
Very good. And now take a look at this: 27 What are the
I
Pieman yy = new PiemanMO.
\Vhat is the value of doing at the end?
yy.addTop(new AnchovyO)
yy.addTop(new AnchovyO)
yy.addTop(new SalmonO)
And what is the value of 29 It's 0, because rem Top first removes all tunas
yy.remTop(new Tuna()) and then counts how many there are left.
after we are through with all that?
Does that mean rem Top always produces O? 30 Yes, it always does.
yy.occTop(new SalmonO)?
36
Does that mean that anybody can write No) because yy's type is Pieman I , p isn't
yy.p = new BotO available. Only add Top , rem Top , substTop,
and thus change a pieman like yy? and occTop are visible.
166 Chapter 10
Isn't it good that we didn't include p in 37 Yes, with this trick we can prevent others
Pieman I ? from changing p (or parts of p) in strange
ways. Everything is clear now.
And that's what we discuss next. Do you 40 No, a cup of coffee will do.
need a break?
Compare this new PieVisito,x with the first 41 It isn't all that different. A PieVisito,x must
one in this chapter. still provide two methods: forBot and
for Top , except that the former now
interface PieVisito,x { consumes a Bot and the latter a Top.
Object forBot(Bot that);
Object forTop(Top that);
}
Is it? Why does it use this? 43 We only have one instance of Bot when we
use forBot, namely this, so forBot is clearly
supposed to consume this.
~Iodify this version of Occurs v so that it The forBot method basically stays the same,
implements the new PieVisitorI . but forTop changes somewhat.
How does forBot change? 46 It now consumes a Bot, which is why we had
to add (Bot that) behind its name.
168 Chapter 10
How does JorTop change? 47 It no longer receives the field values of the
corresponding Top. Instead it consumes the
entire object, which makes the two fields
available a,." that. t and that. r.
49
Isn't that easy? This modification of Occurs v certainly is.
51
Do we need to do Subst v? Not really. It should be just like Rem v.
And indeed, it is. Happy now? 52 So far, so good. But what's the point of this
exercise?
return that: }
else {
that. r. accept (this)
return that; }
}
}
Don't they say "no news is good news?" 55 Does this saying apply here, too?
\iVhat do the methods of Subst v always 57 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
What? 59 The
that.t = n
does it.
60
Correct. And from here on, that.t holds the In the previous Su bst v, 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.
Is there anything else to say about the new 61 Not really. It does what it does, which is
Subst v ? what we wanted. 1
1 This is a true instance of the visitor pattern [4J. \Vhat we
previously called "visitor" pattern instances, were simple
variations on the theme.
Do we have to change Pieman M ? 62 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 Pieman M manages the
pie? toppings of p, and nobody else sees p.
boolean closerToO(Point'D p) {
return class ManhattanPt extends Point D {
distanceToOO :::; p.distanceToOO; } ManhattanPt(int _x ,int _y) {
Point D minus(Point D 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 Ax;
int Ay;
ShadowedManhattanPt(int _x,
int _y,
int _Ax,
int _Ay) {
super(_x,_y);
Ax = _Ax;
Ay = _Ay; }
int distanceToOO {
return
super.distanceToOO+~x + ~y; }
}
172 Chapter 10
Good enough. We won't need it. Here is one 67 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?
Yes. Add to Point'D the method moveBy, 68 First we must know what the method is
which consumes two ints and changes the supposed to produce.
fields of a point appropriately.
The method should return the new distance 69 Now we know how to do this.
to the origin.
abstract class Poi nt'D {
int X;l
int y;
Point'D(int _x,int _y) {
x = _x;
y = -y; }
boolean closerToO(Point'D p) {
return
distanceToOO ~ p.distanceToOO; }
Point'D minus(Point'D p) {
return
new CartesianPt(x - p.x,y - p.y); }
int moveBy(int ~x,int ~y) {
x = x + ~x
y = y + ~y
return distanceToOO; }
abstract int distance ToOO;
}
74
Did the balloon move, too? Yes, it just moved along as we moved the
point.
Isn't that powerful? 75 It sure is. \Ve added one method, used it,
and everything moved.
The more things change, the cheaper our 76 Yes, but to get to the dessert, we had to
desserts get. work quite hard.
Correct but nmv we are through and it is 77 Don't forget to leave a tip.
time to go out and to celebrate with a grand
dinner.
174 Chapter 10
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-\\Tesley, Reading, Mas-
sachusetts, 1996.
3. Firesmith and Eykholt. Dictionary of Object Technology. 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-\\Tesley, Reading,
Massachusetts, 1996.
Commencement 177
This is for the loyal Schemers and 11Lers. No, we wouldn't forget factorial.
interface TI {
o-+OI apply(TI x);
class MkFact implements oo--->ooI
public o--->oI apply (O--->oI fact) { :
II
} return new Fact(fact); } ~
interface o-+OI {
Object apply(Object x); class Fact implements o--->oI {
} o-+oI fact;
Fact( O--->oI -fact) {
fact = -fact; }
I 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(l);
interface oo-+oo-+ooI { else
o-+oI apply (oo-+oOI x); return
new Integer(
}
inti
*( (Integer)
class Y implements oo-+oo-+ooI {
public o-+oI apply (oo-+ooI I) { fact. apply (new Integer( inti -- 1)))
return new H(f).apply(new H(f)); } . int Value 0 ); }
class H implements TI {
OO-+OOI f;
H(OO-+OOI -I) {
f = -f; }
public o-+OI apply(T I x) {
return J.apply(new G(x)); }