Anda di halaman 1dari 185

A Little J ava, A Few Patterns

Copyrighted Material
A Little Java, A Few Patterns

Matthias Felleisen
Rice University
Houston, Texas

Daniel P. Friedman
Indiana University
Bloomington, Indiana

Drawings by Duane Bibby

Foreword by Ralph E. Johnson

The MIT Press


Cambridge, Massachusetts
London, England

Copyrighted Material
Second printing. 2000

© 1998 Massachusetts Institute of Technology


All rights reserved. No part of this book may be reproduced in any form by any
electronic or mechanical means (including photocopying. recording, or information
storage and retrieval) without permission in writing from the publisher.
T his book was set by the authors and was printed and bound in the United States
of America.
Library of Congress Cataloging-in-Publication Data

Felleisen, Matthias
A little Java, a few patterns. / Matthias Felleisen and Daniel P. Friedman;
drawings by Duane Bibby; foreword by Ralph E. Johnson
p. cm.
Includes index and bibliographical references.
ISBN 0-262-56115-8 (pbk : alk. paper)
1. Java (Computer program language) I. Friedman, Daniel P. II. Title.
QA 76. 73.J38F45 1998
005.13'3-dc21 97-40548
CIP

Copyrighted Material
To Helga, Christopher, and Sebastian.
To Mary, Rob, Rachel, Sara,
and to the memory of Brian.

Copyrighted Material
Contents
Foreword ix
Preface xi
Experimenting with Java xiii
1. Modern Toys 3
2. Methods to Our Madness 13
3. What's New? 43
4. Come to Our Carousel 57
5. Objects Are People, Too 69
6. Boring Protocols 85
7. Oh My! 99
8. Like Father, Like Son 117
9. Be a Good Visitor 139
10. The State of Things to Come 161
Commencement 177
Index 178

Copyrighted Material
FOREWORD

Learning to program is more than learning the syntactic and semantic rules of a programming
language. It also requires learning how to design programs. Any good book on programming
must therefore teach program design.

Like any other form of design, program design has competing schools. These schools are
often associated with a particular set of languages. Since Java is an object-oriented programming
language, people teaching Java should emphasize object-oriented design.

Felleisen and Friedman show that the functional ( input-output driven ) method of program
design naturally leads to the use of well-known object-oriented design patterns. In fact, they
integrate the two styles seamlessly and show how well they work together. Their book proves that
the functional design method does not clash with, but supports object-oriented programming.

Their success doesn't surprise me, because I've seen it in Smalltalk for many years, though
unfortunately, it seems to have remained one of the secrets of object-oriented design. I am
happy to see that Felleisen and Friedman have finally exposed it. This book will be especially
useful if you are a C++ programmer learning Java, since you probably haven't seen functional
program design before. If you know functional design, the book will gently introduce you to
pattern-based programming in Java. If you don't know it, Felleisen and Friedman will teach
you a powerful new way of thinking that you should add to your design toolbox.

Enjoy the pizzas!

Ralph E. Johnson
Champaign, Illinois

Copyrighted Material
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 mod ify any existing code
but simply glue together components and add a few new ones. This reusability of comp onents ,
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 ( abstract )
introduces its object-oriented elements:
classes, fields, methods, inheritance, and interfaces. language has a simple
This small core
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 patterns, the key elements of a
second goal is to introduce the reader to design
programming discipline that enhances code reuse. Design patterns help programmers organize
their object- oriented components so t hat they properly implement the desired computatio nal
process. More importantly s till , des ign patterns help communicate important properties about
a program component. If a component is an instance of an explicitly formulated pattern and
documented as such, ot her programmers can easily understand its structure and reuse it in their
own programs, even without access to the component's source.

THE INTENDED AUDIENCE


The book is primarily intended for people--practicing programmers, instructors and students
alike--w ho 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 boolfi f 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 .

WHAT THIS BOOK IS NOT ABOUT

Java provides many useful features and libraries beyond its object-oriented core. While these
additional Java elements are important for professional programming, their coverage would
dist ract from the book's important goals: object-oriented programming and the use of design
patterns. For that reason, this book is not a complete introduction to Java. Still, readers
who master its contents can quickly become skilled Java programmers with the supplementary
sources listed in the Commencement.
The literature on design patterns evolves quickl y. Thus, there is quite a bit more to patterns
than an introductory book could intelligibly cover. Yet, the sim plicity of the patterns we use
and the power that they provide should encourage readers to study the additional references
about patterns mentioned at t he end of the book.

ACKNOWLEDGMENTS

We are indebted to many people for their contribu tions and assistance throughout the devel­
opment of this book. Several extensive discussions with Shriram Krishnamurthi, Jon R ossie ,

Copyrighted Material
xi
and Mitch Wand kept us on track; their detailed comments deeply influenced our thinking at
critical junctures. Michael Ashley, Sundar Balasubramaniam, Cynthia Brown, Peter Drake,
Bob Filman, Robby Findler, Steve Ganz, Paul Graunke, John Greiner, Erik Hilsdale, Matthew
Kudzin, Julia Lawall, Shinn-Der Lee, Michael Levin, Gary McGraw, Benjamin Pierce, Amr
Sabry, Jonathan Sobel, and George Springer read the book at various stages of development and
their comments helped produce the final result. We also wish to thank Robert Prior at MIT
Press who loyally supported us for many years and fostered the idea of a "Little Java." The book
greatly benefited from Dorai Sitaram's incredibly clever Scheme typesetting program SJb.1EX.
Finally, we would like to thank the National Science Foundation for its continued support
and especially for the Educational Innovation Grant that provided us with the opportunity to
collaborate for the past year.

READING GUIDELINES

Do not rush through this book. Allow seven sittings, at least. Read carefully. Mark up the book
or take notes; valuable hints are scattered throughout the text. Work through the examples,
don't scan them. Keep in mind the motto "Think first, experiment later."

The book is a dialogue about interesting Java programs. After you have understood the
examples, experiment with them, that is, modify the programs and examples and see how they
behave. Since most Java implementations are unfortunately batch interpreters or compilers, this
requires work of a repetitive nature on your side. Some hints on how to experiment with Java
are provided on the following pages.

We do not give any formal definitions in this book. We believe that you can form your own
definitions and thus remember and understand them better than if we had written them out for
you. But be sure you know and understand the bits of advice that appear in most chapters.

We use a few notational conventions throughout the text to help you understand the
programs on several levels. The primary conventions concern typeface for different kinds of
words. Field and method names are in italic. Basic data, including numbers, booleans, and
constructors introduced via datatypes are set in sans serif. Keywords, e.g., class, abstract,
return and interface are in boldface. When you experiment, you may ignore the typefaces
but not the related framenotes. To highlight this role of typefaces, the programs in framenotes
are set in a typewriter face.

Food appears in many of our examples for two reasons. First, food is easier to visualize
than abstract ideas. ( This is not a good book to read while dieting. ) We hope the choice of
food will help you understand the examples and concepts we use. Second, we want to provide
you with a little distraction. We know how frustrating the subject matter can be, and a little
distraction will help you keep your sanity.

You are now ready to start. Good luck! We hope you will enjoy the experiences waiting for
you on the following pages.

Bon appetit!

Matthias Felleisen
Daniel P. Friedman

xii
Copyrighted Material
EXPERIMENTING WITH JAVA

Here are some hints on how to experiment with Java:1


1. Create a file that contains a complete hierarchy of classes.

2.To each class whose name does not end with a superscript V, V, I, or M, add a toString
method according to these rules:
a) if the class does not contain any fields, use

public String toString() {


return "new " + getClass () . getName () + " () ; } "

b) if the class has one field, say x, use

public String toString() {


return "new " + getClass() . getName 0 + " ( " + x + " ) " ; }

c) if the class has two fields, say x and y, use

public String t oString ( ) {


return "new "
+ getClass 0 getName 0 . + " ( " + x +
" " + y +
If) If; }

3. Add the following class at the bottom of the file:

class Main {
public static void main(String args[ ]) {
DataType_or_Interface y new =

System.out.println( . . . . . . ); } }

With DataType_oLlnterface y = new create the object y with wh i ch you wish to _,

experiment. T hen replace with the example expression that you would like to experiment
. . . . . .

with. For example, if you wish to experiment with the distanceToO method of ManhattanPt
as defined in chapter 2, add the following definition to the end of your file:

class Main {
public static void main(String args[ ]) {
PointD y = new ManhattanPt(2,8);
System.out.println( y.distanceToO() ); } }

lSee Arnold and Gosling [lJ for details on how they work. These hints make little sense out of context, so for
now, just follow them as you read this book.

Copyrighted Material X111


If you wish to experiment with a sequence of expressions that modify y, as in chapter 10, e.g.,

y.­
y.­
y.- -

replace . . . . . . with

y.­ + "\n " +

y.- - + "\n " +

y.-

For example, if you wish to experiment with the methods of Pi emanM as defined in chapter 10,
add the following definition to the end of your file:

class Main {
public static void main(String args[ ]) {
PiemanI y = new PiemanM();
System.out.println (
y.addTop (new AnchovyO) + "\n" +

y. addTop (new Anchovy 0) + "\n" +

y.substTop (new Tuna () ,new Anchovy (» ); } }

4. Finally, compile the file and interpret the class Main.

XIV
Copyrighted Material
A Little Java, A Few Patterns

Copyrighted Material
flo
�@�l� �

Copyrighted Material
Is 5 an integer? 1 Yes, it is.

2
Is this a number: -23? Yes, but we don't use negative integers.

Is this an integer: 5.32? 3 No, and we don't use this type of number.

What type of number is 5? int.1

1 In Java, int stands for "integer."

Quick, think of another integer! 5 How about 19?

What type of value is true? 6 boolean.

What type of value is false? 7 boolean.

Can you think of another boolean? 8 No, that's all there is to boolean.

9
What is int? A type.

10
What is boolean? Another type.

11
What is a type? A type is a name for a collection of values.

What is a type? 12 Sometimes we use it as if it were the


collection.

13
Can we make new types? We don't know how yet.

Modern Toys 3
Copyrighted Material
Draw the picture that characterizes the 14 Is this it?
essential relationships among the following
classes.

I abstract class SeasoningV {}

I class Salt extends SeasoningV {}

I class Pepper extends SeasoningV {}

'D This superscript is a reminder that the class is a


datatype. Lower superscripts when you enter this kind of
definition in a file: SeasoningD.

15
Yes. We say SeasoningV is a datatype, and Okay. But aren't all three classes introducing
Salt and Pepper are its variants. new types?

16
Yes, in a way. Now, is Yes, it is, because new SaitO creates an
new SaitO instance of Salt, and every instance of Salt is
also a SeasoningV
a Seasoningv?

17
And It's also a SeasoningV, because new PepperO
new PepperO? creates an instance of Pepper, and every
instance of Pepper is also a Seasoningv.

,.
What are abstract, class, and extends? Easy:
abstract class introduces a datatype,
class introduces a variant, and
extends connects a variant to a datatype.

19
Is there any other Seasoningv? No, because only Salt and Pepper extend
1
SeasoningV

1 Evaluating new Salt () twice does not produce the same


value, but we ignore the distinction for now.

4 Chapter 1
Copyrighted Material
20
Correct, Salt and Pepper are the only No, but boolean is a type that also has just
variants of the datatype Seasoningv. Have two values.
we seen a datatype like SeasoningV before?

21
Let's define more SeasoningVs. We can have lots of SeasoningVs.

I class Thyme extends SeasoningV { } I class Sage extends SeasoningV {}

22
And then there were four. Yes.

What is a Cartesian point? 23 It is basically a pair of numbers.

What is a point in Manhattan? 24 An intersection where two city streets meet.

25
How do CartesianPt and ManhattanPt differ Each of them contains three things between
from Salt and Pepper? { and }. The X and the yare obviously the

I
coordinates of the points. But what is the
abstract class PointV {} remaining thing above the bold bar?1

class CartesianPt extends PointV {


int Xi
int Yi
CartesianPt(int _x,int _ y) {
X = _Xi
Y = -Yi }

class ManhattanPt extends PointV {


int Xi
int Yi
ManhattanPt(int -x,int _y) {
X = _Xi
Y = -Yi } 1 This bar indicates the end of the constructor definition. It
is used as an eye-catching separator. We recommend that
you use
/I
} ------------------------------

when you enter it in a file.

Modern Toys 5
Copyrighted Material
6
The underlined occurrences of Cartesian Pt 2 How do we use these constructors?
and ManhattanPt introduce the constructors
of the respective variants.

A constructor is used with new to create 27 Obvious!


new instances of a class.

28
When we create a CartesianPt like this: So now we have created a Cartesian Pt whose
x 2 and whose y field is 3. And
new CartesianPt(2,3), field is
because CartesianPt extends PointV, it is
we use the constructor in the definition of
also a Pointv.
Cartesian Pt.

Correct. Is this a Manhattan Pt: 29 Yes, and its x field is 2 and its y field is 3.
new Manhattan Pt(2,3)?

Isn't all this obvious? 30 Mostly, but that means we have used
constructors before without defining them.
How does that work?

31
When a class does not contain any fields, as And that's the constructor we used before,
in Salt and Pepper, a constructor is included right?
by default.

3
2
Yes, that's correct. Default constructors Good. But what is new PointVO?
never consume values, and, when used with
new, always create objects without fields.

An abstract class is by definition 33 That makes sense. Let's move on.


incomplete, so new cannot create an
instance from it.

6 Chapter 1
Copyrighted Material
34
Do the following classes define another Yes, they define a datatype and two variants.
datatype with variants?

I abstract class Numv {}

I class Zero extends Num v {}

OneMoreThan
class O n e M oreThan extends Numv {
Numv predecessor;
OneMoreTh an ( Num V _p ) {
predecessor = _Pi }

Draw the picture, too .

5
3
Is this a Numv: Obviously, just like new SaitO is a
Seasoningv.
new Zero()?

Is this a Numv: 36 Yes, because OneMoreThan constructs a

new OneMoreThan( Numv from a Numv, and every instance of


new Zero())? One MoreThan is also a Numv.

37
How does OneMoreThan do that? We give it new ZeroO, which is a Numv, and
it constructs a new Numv.

3
And what does it mean to construct this new 8 This new instance of OneMoreThan is a value
instance? with a single field, which is called
predecessor. In our example, the field is
new ZeroO.

39
Does predecessor always stand for an No, its type says that it stands for a N umv ,
instance of Zero? which , at the moment, may be either a Zero
or a OneMoreThan.

Modern Toys 7
Copyrighted Material
40
What is A Numv, because OneMoreThan constructs
new OneMoreThan( an instance from a Numv and we agreed
new OneMoreThan( that
new Zero()))? new OneMoreThan(
new ZeroO)
is a Numv.

41
What is That is nonsense,l because 0 is not a N u mv.
new OneMoreThan(
O)? 1 We use the word "nonsense" to refer to expressions for
which Java cannot determine a type.

Is new ZeroO the same as O? 42 No, 0 is similar to, but not the same as,

new ZeroO.

Is 43 1 is similar to, but not the same as,

new OneMoreThan( new OneMoreThan(


new ZeroO) new Zero()).

like
1?

And what is 44 4.
new OneMoreThan(
new OneMoreThan(
new OneMoreThan(
new OneMoreThan(
new Zero()))))

similar to?

45
Are there more N u mvs than booleans? Lots.

Are there more Numvs than ints? 46 No.1

1 This answer is only conceptually correct. Java limits the


number of ints to approximately 232.

8 Chapter 1
Copyrighted Material
47
What is the difference between new Zero() Easy: new Zero() is an instance of Zero and,
and O? by implication, is a NumD, whereas 0 is an
into This makes it difficult to compare them,
but we can compare them in our minds.

48
Correct. In general, if two things are So are types just names for different
instances of two different basic types, they collections with no common instances?
cannot be the same.

The primitive types (int and boolean) are 49 What are non-basic types?
distinct; others may overlap.

0
Class definitions do not introduce primitive 5 And what is that?
types. For example, a value like new Zero()
is not only an instance of Zero, but is also a
NumD, which is extended by Zero. Indeed, it
is of any type that NumD extends, too.

Every class that does not explicitly extend 51 This must mean that everything is an Object.
another class implicitly extends the class
Object.

Almost. We will soon see what that means. 52 Okay.

The First Bit of Advice

When specifying a collection of data,


use abstract classes for datatypes and
extended classes for variants.

Modern Toys Copyrighted Material 9


53
What do the following define? They define a new datatype and its two

I abstract class LayerD {}


variants. The first variant contains a field of
type Object.

class Base extends LayerD {


Object OJ
Base(Object _0) {
0= _OJ}

class Slice extends LayerD {


LayerD I;
Slice(LayerD -l) {
I= _lj }

54
What is It looks like an instance of Base, which
newBase( means it is also a LayerD and an Object.
new Zero())?

And what is 55 It also looks like an instance of Base. But


newBase( how come both
new Salt())? new Base(
new Zero())
and
new Base(
new SaitO)
are instances of the same variant?

56
They are, because everything created with Hence, we can use both
new is an Object, the class of all objects. new ZeroO
and
new SaitO
for the construction of a Base, which requires
an Object.

10 Chapter 1
Copyrighted Material
57
Is anythin g else an Object? We said that only things created with new
are O bjects . 1

1 Arrays and strings are objects, too. We don't discuss


them.

58
Correct. Is this a LayerV ; 5 is not created with new, so this must be
new Base( nonsense.
5)?

59
;
V false is not created with new, so this must be
Is this a L ayer
new Base( nonsense, too.
false)?

60
Correct again! How about this Layerv: This must mean that Integer creates an
new Base( object from an into
new I nteger( 5))?

61
Guess how we create a LayerV from false? Easy now:
new Base(
new Boolean(false)).

62
Is it confusing that we need to connect int Too much coffee does that.
with Integer and boolean with Boolean?

63
Ready for more? Can't wait.

Modern Toys 11
Copyrighted Material
��{Q
��{!;�.2J�g \�
(�)l!1�:f�

Copyrighted Material
Remember points? Sure, we just talked about them. But what
are these labeled ovals about?
abstract class Point'D {
-- (� Point
----') --
}
---

class CartesianPt extends Point'D {


i nt x;
int y;
CartesianPt(int _x , int _y) {
x _x;
=

y = -y; }

-- ( CartesianPt ) -­

class ManhattanPt extends Point'D {


i nt x;
int y;
ManhattanPt(int _x,int _y) {
x = _x;
y = -y; }

-- ( ManhattanPt )-­
}

2
We will find out soon. Did you notice the big It must be for drawing the picture of the
white space on the right? classes.

How far is If the Empire State Building is the origin, we


have to walk seven blocks: 3 over, 4 up.
new ManhattanPt(3,4)
from the Empire State Building?

And how far is 5, which is )32 + 42.


new CartesianPt(3,4)
from the origin?

Methods to OUf Madness Copyrighted Material 13


5
Write the methods distanceToO usi ng {, }, ( , Of course, you can't write these methods ,
), ;, return, int, +, LV:J, and 2 which . , yet. Okay, you deserve something sweet for
determine how far a point is from the origin. enduring this last question .

What do the methods produce? i nts , which represent the distances to the
origin.

Here they are. They correspond to the unexplained labels in


the definition of the dat atyp e and its
abstract int distance ToOO; variants.

Point

int distanceToOO {
return L y'x2 + y2J \ }

C artesi anPt

int distanceToOO {
return x + Yi }

M a nhat tanPt

To what do Point, CartesianPt , and


ManhattanPt in the boxes refer?

1 When you enter this in a file, use


(int)Math. sqrt (x.x+y.y).
Math is a class that contains sqrt as a (static) method. Later
we will see what (int) means.

8
The labels remind us that we need to insert That's simple enough.
these methods into Point"D, CartesianPt, and
ManhattanPt.

9
defi ned Three times, but the first one differs from the

How many tImes have we the method
distance ToO? other two. It is labeled abstract, while the
others are not preceded by a special word.

14 Chapter 2
Copyrighted Material
10
Do abstract metho d s belong to th e Yes, they always do.
abstract class?

II
An abstract method in an abstract class Okay.
,
int rod uces an o bli gat ion which says that all
con crete classes that extend this abstract
class! mus t contain a matching method
definition.

1 Directly or indirectly. That is, the concrete class may


extend an abstract class that extends the abstract class with
the obligation and so on.

12 7.
What is the value of
new ManhattanPt(3,4)
. distance ToOO?

How do we arrive at that value? 13 We determine the value of


x+y,

w it h x re pla ced by 3 an d y replaced by 4.

14
What is the value of 5, because that is the value of
new CartesianPt(3,4) ";x2 + y2
. distance ToOO?
with x re pl aced by 3 and y replaced by 4.

15
What does l JXJ compute? The largest int that does not excee d the
square root of x.

16
Time for a short break? An apple a day keeps the dentist away. A
cup of coffee does not.

Methods to Our Madness 15


Copyrighted Material
17
Here is another datatype with its variants. It is like Numv but has more variants.
What is different about them?

abstract class Shishv {


-- ( Shish ) --
}

class Skewer extends Shishv {


-- C,-_S;.k_
.. we _e_r _�)--
}

class Onion extends Shishv {


Shishv s;
Onion(ShishV _s) {
s = s; }
_

-- C,-_O_n_i_on_�)-­
}

class lamb extends Shishv {


Shishv s;
lamb(ShishV _s) {
s = _s; }

} -- C,-_L::.,::a=.m:..:.
b _ ) --
. _

class Tomato extends Shishv {


Shishv s;
Tomato(ShishV _s) {
s = s; }
_

}
-- ( Tomato )-

18
Did you notice the big space on the right? Yes, isn't it for drawing the picture of the
classes?

16 Chapter 2
Copyrighted Material
19
Construct a Shishv. How about

new SkewerO?

20
Yes, every Skewer is also a Shishv. How Here's one:
about another one? new Onion(
new Skewer() ) .

21
And a third? Here's one more:
new Onion(
new Lamb(
new Onion(
new Skewer()) ) ) .

22
Onions
Are there only on this Shishv : true, because there is neither Lamb nor

new SkewerO? Tomato on new Skewer O.

Are there only Onions on this Shishv : 23 true.


new Onion(
new Skewer() ) ?

And how about: 24 false.


new Lamb(
new Skewer()) ?

25
Is it true that true.
new Onion(
new Onion(
new Onion(
new Skewer())) )
contains only Onions?

26
And finally: false.
new Onion(
new Lamb(
new Onion(
new Skewer() ) ) ) ?

Methods to Our Madness 17


Copyrighted Material
27
Write the methods onlyOnions1 using {, }, ( , Of course, you can't write these methods,

), ,;, true, false, return, and boolean. yet. Okay, you deserve a lollipop for
enduring this kind of question again.
1 A better name for these methods would be
nothingButOn1ona.

And what do they produce? 28 booleans.

Here are the methods. 29 Yes. We said above that the labeled ovals in
the center of the blank lines in the above
abstract boolean onlyOnionsO; class definitions tell us where to put the
boxes with the corresponding labels.
Shish

: boolean onlyOnionsO {
return true; }

Skewer

boolean onlyOnionsO {
return s.onlyOnionsO; }

Onion

boolean onlyOnionsO {
return false; }

Lamb

boolean onlyOnionsO {
return false; }

Tomato

Did you notice the labels in the boxes?

30
Good. How many methods have we defined? Five, but the first one is abstract; the
others are concrete.

18 Chapter 2
Copyrighted Material
31
Do abstract methods belong to the Yes, we said so.
abstract class?

32
Does each variant of Shishv contain a Yes, because Shishv contains an abstract
method called onlyOnions? method called onlyOnions that obligates
each variant to define a matching, concrete
method.

33
Is this always the case? Always.

34 • •
What do these concrete methods consume? Nothing, Just as the abstract method says.

35
What do these concrete methods produce? booleans, just as the abstract method says.

What is the value of 36 true.


new Onion(
new Onion(
new Skewer(»)
.onlyOnionsO?

37
And how do we determine the value of We will need to pay attention to the method
new Onion( definitions.
new Onion(
new SkewerO»
.onlyOnions()?

38
Which definition of onlyOnions must we use This object is an instance of Onion, so we
to determine the value of need to use the definition of onlyOnions that
new Onion( belongs to the Onion variant.
new Onion(
new Skewer(»)
. onlyOnions ()?

Methods to Our Madness


Copyrighted Material 19
What follows the word return in the 39 s.onlyOnions{).
onlyOnions method in Onion?

What is the field s of the object 40 It is


new Onion( new Onion(
new Onion( new Skewer()) ,
new Skewer() ))? isn't it?

41
Does s always stand for an Onion? No, it has type Shishv, and it can stand for
any variant of Shishv : Skewer, Onion, Lamb,
or Tomato.

Then what is s.onlyOnions()? 42 It should be


new Onion(
new Skewer() )
.onlyOnionsO,
right?

Why do we need to know the meaning of 43 Because the answer for


new Onion( new Onion (
new SkewerO) new Skewer() )
. onlyOnions O? . onlyOnions 0
is also the answer for
new Onion(
new Onion(
new SkewerO) )
. onlyOnionsO·

..

How do we determine the answer for 44 Let's see.


new Onion(
new Skewer())
.onlyOnions()?

20 Chapter 2
Copyrighted Material
45
Which definition of onlyOnions must we use This object is an instance of Onion, so we
to determine the value of need to use the definition of onlyOnions that
new Onion( belongs to the Onion variant.
new SkewerO)
.onlyOnionsO?

46
What follows the word return in the s. onlyOnions O.
onlyOnions method in Onion?

47
W hat is the field s of the object new SkewerO.
new Onion(
new Skewer())?

Then what is s.onlyOnionsO? 48 It is


new SkewerO
.onlyOnionsO,
just as we would have expected.

Why do we need to know the meaning of 49 Because the answer for


new SkewerO new SkewerO
. onlyOnions O? . onlyOnions 0
is also the answer for
new Onion(
new SkewerO)
. onlyOnions 0 ,
which in turn is the answer for
new Onion(
new Onion(
new Skewer()))
.onlyOnionsO·

How do we determine the answer for 50 We need to determine one more time which
new Skewer O version of onlyOnions we must use.
.onlyOnionsO?

Methods to Our Madness 21


Copyrighted Material
Is 51 Obviously.
new SkewerO
a

Skewer?

2
Then what is the answer? 5 true.

53
Why? Because true is what the onlyOnions method
in Skewer always returns.

Are we done? 54 Yes ! The answer for


new Onion(
new On i o n (
new Skewer()))
. only Onions 0
is the same as the answer for
new Onion(
SkewerO)
new
.onlyOnionsO,
which is the same as the answer for
new SkewerO
.onlyOnionsO,
which is

true.

What is the value of 55 false, isn't it?


new Onion(
new Lamb(
new Skewer()))
. onlyOnionsO?

56
Which definition of only Onions must we use This object is an instanc e of Onion, so we
to determine the value of need to use the de fin iti o n
of onlyOnions that
new Onion( belongs to the Onion variant.
new Lamb(
new Skewer()))
.onlyOnionsO?

22 Chapter 2
Copyrighted Material
57
What follows the word return in the s. onlyOnions() .
onlyOnions method in Onion?

58
What is the field s of the object It is the object built from
new Onion( new Lamb(
new Lamb( new Skewer()).
new Skewer()))?

Then what is s.onlyOnions()? 59 I t is


new Lamb(
new Skewer())
·onlyOnions() ,
of course.

Why do we need to know the meaning of 60 Because the answer for


new Lamb( new Lamb(
new Skewer()) new Skewer())
. onlyOnions () ? ·onlyOnions()
is also the answer for
new Onion(
new Lamb(
new Skewer()))
·onlyOnions o.

61
How do we determine the answer for We determine which version of onlyOnions
new Lamb( to use.
new Skewer())
.onlyOnions()?

62
And? We use the one that belongs to Lamb.

63
And now what is the answer? false, because false follows the word return
in the corresponding method definition in
Lamb.

Methods to OUf Madness 23


Copyrighted Material
Are we done? 64 Yes! The answer for
new Onion(
new Lamb(
new Skewer()))
. onlyOnions0
is the same as the answer for
new Lamb(
new Skewer())
. onlyOnionsO,
which is
false.

65
Describe the methods (i. e., the function ) Here are our words:
only Onions in your own words. "The methods determine for a S hish v
whether its contents are edible by an onion
lover."

66
Describe how the methods (i.e., the Here are our words again:
function ) onlyOnions accomplish this. "For each layer of th e Shishv, except for
Onion, the corresponding method knows
whether it is go od or bad. The method for
Onion needs to determine whether the
remaining layers are only Onions sitting on

a Skewer."

Is 67 Yes.
new Tomato(
new SkewerO)
a Shishv?

68
Is The obj ect
new Onion( new Tomato(
new Tomato( new SkewerO)
new Skewer()))
is an instance of Shishv, so we can also wrap
a Shishv? an Onion around it.

And how about another Tomato? 69 Sure.

24 Chapter 2
Copyrighted Material
Is 70 Of course, there is no Lamb on it.
new Tomato(
new Onion(
new Tomato(
new SkewerO»)
a vegetarian shish kebab?

71
And Yes, it is a vegetarian shish kebab, because it
new Onion( only contains Onions.
new Onion(
new Onion(
new Skewer())))?

72
Define the methods (i.e., the function) That's no big deal now.

is Vegetarian,
abstract boolean is Vegetarian 0;
which return true if the given object does not
contain Lamb. Shish
Hint: The method for tomatoes is the same
as the one for onions.
boolean isVegetarian 0 {
return true; }

Skewer

boolean isVegetarian 0 {
return s.isVegetarianOi }

Onion

boolean isVegetarian 0 {
return false; }

Lamb

boolean isVegetarian 0 {
return s.isVegetarianOi }

Tomato

Methods to Our Madness Copyrighted Material 25


How many methods have we defined? 73 Five: one abstract, the others concrete.

74
Do abstract methods belong to the Yes, they always do.
abstract class?

75
Does each variant of Shishv contain a Yes, because Shishv contains an abstract
method called isVegetarian? method called isVegetarian.

Is this always the case? 76 Always.

What do these concrete methods consume? 77 Nothing, just as the abstract method says.

What do these concrete methods produce? 78 booleans, just as the abstract method says.

The Second Bit of Advice


When writing a function over a
datatype, place a method in each of the
variants that make up the datatype. If
a field of a variant belongs to the same
datatype, the method may call the
corresponding method of the field in
computing the function.

26 Chapter 2
Copyrighted Material
79
Collect all the pieces of Shishv. Here is the There are two methods per variant.
datatype.
class Skewer extends Shishv {
abstract class Shishv { boolean onlyOnionsO {
abstract boolean onlyOnions(); return true; }
abstract boolean isVegetarian(); boolean isVegetarian 0 {
} return true; }
}

class Onion extends Shishv {


Shishv s;
Onion(ShishV _8) {
s = _8; }

boolean onlyOnionsO {
return s.onlyOnionsO; }
boolean is Vegetarian 0 {
return 8.isVegetarianO; }
}

class Lamb extends Shishv {


Shishv s;
Lamb(ShishV _s) {
s = _s; }

boolean onlyOnions () {
return false; }
boolean is Vegetarian 0 {
return false; }
}

class Tomato extends Shishv {


Shishv s;
Tomato(ShishV _8) {
s = _s; }

boolean onlyOnionsO {
return false; }
boolean isVegetarian 0 {
return s.isVegetarianO; }
}

Methods to OUT Madness 27


Copyrighted Material
80
What do the following define? They define a datatype an d four variants
that are similar in shape to Sh ish'D
abstract class Kebabv {
-- ( Kebab ) --
}

class Holder extends Kebab'D {


Object OJ
Holder( Object 0 ) {
_

0= O J }
_

-- C H_o_ld_e_
___ r _�)--
}

class Shallot extends Kebab'D {


Kebab'D k;
Shallot(KebabV k ) { _

k = kj }
_

-- ( Shallot )-
}

class Shrimp extends Kebab'D {


Kebab'D k;
Shrimp(KebabV _k) {
k _kj }
=

-- ( Shrimp
)-
}

class Radish extends Kebab'D {


Kebab'D k;
Radish(KebabV _k) {
k _kj }
=

-- ( Radish
)-
}

Don't forget the picture.

28 Chapter 2
Copyrighted Material
81
W hat is different about them? They are placed onto different Holders.

82
Here are some holders. Sure, a rod is a kind of holder, and every rod
is an Object, so 0 in Holder can stand for any
I abstract class RodD {} rod. Is it necessary to draw another picture?

I class Dagger extends RodD {}


I class Sabre extends RodD {}
I class Sword extends RodD {}
Are they good ones?

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.

abstract class PlateD {}

class Gold extends PlateD { }

class Silver extends PlateD { }

class Brass extends PlateD { }

I class Copper extends PlateD {}


I class Wood extends PlateD {}
84
What is It's a KebabD.
new Shallot(
new Radish(
new Holder(
new Dagger())))?

Methods to Our Madness 29


Copyrighted Material
85
Is Sure it is. It only contains radishes and
new Shallot( shallots.
new Radish(
new Holder(
new Dagger())))
a vegetarian Kebabv?

86
Is Sure, because Gold is a PlateV, PlateV is used
new Shallot( as a Holder, and radishes and shallots can be
new Radish( put on any Holder.
new Holder(
new Gold())))
a Kebabv?

87
Is Sure it is. It is basically like
new Shallot( new Shallot(
new Radish( new Radish(
new Holder( new Holder(
new Gold()))) new Dagger()))),
a vegetarian kebab? except that we have moved all the food from
a Dagger to a Gold plate.

8
Let's define the methods ( i. e., the function) 8 If you can, you may rest now.

is Veggie,
which check whether a kebab contains only
vegetarian foods, regardless of what Holder it
is on.

Write the abstract method is Veggie. 89 That's possible now.

abstract boolean is Veggie ()

Kebab

Of course, is Veggie belongs to Kebabv and


is Vegetarian to Shishv

30 Chapter 2
Copyrighted Material
90
The concrete methods are similar to those Except for the names of the methods and
called isVegetarian. Here are two more; fields, the definitions are the same as they
define the remaining two. were for Shishv

boolean is VeggieO { boolean is VeggieO {


return true; } return false; }

Holder Shrimp

boolean is VeggieO { boolean isVeggieO {


return k.isVeggieO; } return k.isVeggieO; }

Shallot Radish

What is t he value of 91 true.


new Shallot(
new Radish(
new Holder(
new Dagger())))
.is Veggie O?

92
What is It is a Kebabv, but we also know that it is an
new Shallot( instance of the Shallot variant.
new Radish(
new Holder(
new Dagger())))?

What is the value of 93 It is true, too.


new Shallot(
new Radish(
new Holder(
new Gold())))
.isVeggieO?

94
And what is It is also a Kebabv, because any kind of
new Shallot( Holder will do.
new Radish(
new Holder(
new Gold())))?

Methods to Our Madness 31


Copyrighted Material
What type of value is 95 boolean.
new Shallot(
new Radish(
new Holder(
new I nteger( 52))))
.isVeggieO?

What type of value is 96 boolean.


new Shallot(
new Radish(
new Holder(
new OneMoreThan(
new Zero()))))
. is Veggie O?

What ty pe of value is 97 boolean.


new Shallot(
new Radish(
new Holder(
new Boolean(false))))
. is Veggie O?

98
Does that mean is Veggie works for all five Yes, and all other kinds of Objects that we
kinds of Holders? could possibly think of.

What is the holder of 99 All the food is on a D agg er .

new Shallot(
new Radish(
new H older(
new Dagger())))?

100
What is the holder of All the food is now on a Gold plate.
new Shallot(
new Radish(
new Holder(
new Gold() )))?

32 Chapter 2
Copyrighted Material
101
What is the holder of All the food is on an Integer.
new Shallot(
new Radish(
new Holder(
new Integer(52))))?

102
What is the value of The dagger.
new Shallot(
new Radish(
new Holder(
new DaggerO)))
·whatHolderO?

103
What is the value of The gold plate.
new Shallot(
new Radish(
new Holder(
new Gold())))
·whatHolder()?

104
What is the value of An Integer, whose underlying int is 52.
new Shallot(
new Radish(
new Holder(
new Integer(52))))
·whatHolderO?

105
What type of values do the methods (i.e., They produce rods, plates, and integers. And
the function ) of whatHolder produce? it looks like they can produce a lot more.

106
Is there a simple way of saying what type of They always produce an Object, which is also
values they produce? the type of the field of Holder.

107
Here is the abstract method whatHolder. If we add this method to KebabD, then we
must add a method definition to each of the
abstract Object whatHolder() four variants.

Kebab

Methods to Our Madness 33


Copyrighted Material
108
What is the value of new Integer(52).
new Holder(
new Integer(52))
.whatHolderO?

109
What is the value of new SwordO.
new Holder(
new Sword())
.whatHolderO?

110
What is the value of It is b.
new Holder( b)
.whatHolderO
if b is some object?

111
Define the concrete method that goes into With these kinds of hints, it's easy.
the space labeled Holder.
Object whatHolderO {
return 0; }

Holder

112
What is the value of new Integer(52).
new Radish(
new Shallot(
new Shrimp(
new Holder(
new Integer(52)))))
.whatHolderO?

113
What is the value of new Integer(52).
new Shallot(
new Shrimp(
new Holder(
new Integer(52))))
.whatHolderO?

34 Chapter 2
Copyrighted Material
114
What is the value of new Integer(52).
ne w Shrimp(
new Holder(
new Integer(52)))
. whatHolderO?

115
Does that mean that the value of Yes, all four have the same answer:
new Radish( new Integer(52).
new Shallot(
new Shrimp(

new Holder(
new Integer(52)))))
.whatHolderO
is the same as
new Shallot(
new Shrimp(

new Holder(
new Integer(52))))
.whatHolderO,
which is the same as

new Shrimp(
new Holder(
new Integer(52)))
. whatHolder 0 ,
which is the same as

new Holder(
new Integer(52))
. whatHolderO?

116
Here is the method for Shallot. They are all the same.

Object whatHolderO { Object whatHolderO {


return k. whatHolderO; } return k.whatHolderO; }

Shallot Shrimp

Write the methods of whatHolder for Shrimp


and Radish. Object whatHolderO {
return k.whatHolderO; }

Radish

Methods to Our Madness 35


Copyrighted Material
117
Here is the datatype and one of its variants. There are only three left.

abstract class KebabD { class Shallot extends KebabD {


abstract boolean isVeggie 0 j Kebabv k;
abstract Object whatHolder()j Shallot(KebabD _k) {
} k = _kj }

boolean isVeggieO {
class Holder extends KebabD { return k. isVeggie 0 j }
Object OJ Object whatHolderO {
Holder( Object _0) { return k.whatHolderOj }
o OJ }
}
= _

boolean is VeggieO {
return truej } class Shrimp extends Kebabv {
Object whatHolderO { Kebabv k;
return 0; } Shrimp(KebabD _k) {
} k = kj }
_

Collect the remaining variants.


boolean isVeggieO {
return falsej }
Object whatHolderO {
return k.whatHolderOj }
}

class Radish extends KebabD {


Kebabv k;
Radish(KebabD _k) {
k = _kj }

boolean isVeggieO {
return k.isVeggieOj }
Object whatHolderO {
return k.whatHolderOj }
}

118
Are there any other Kebabv foods besides No, these are the only kinds of foods on a
Shallot, Shrimp, and Radish? Kebabv.

36 Chapter 2
Copyrighted Material
119
Can we add more foods? Sure. We did something like that when we
added Thyme and Sage to Seasoningv.

120
Let's define another Kebabv A concrete class that extends Kebabv must
define these two methods. That's what the
class Pepper extends Kebabv { abstract specifications say. We can define as
Kebabv k; many Kebabvs as we wish.
Pepper(KebabV _k) {
k = _k; } class Zucchini extends Kebabv {
Kebabv k;
boolean isVeggie 0 { Zucchini( KebabV _k) {
return k.isVeggieO; } k _k; }
=

Object whatHolderO {
return k.whatHolderO; } boolean isVeggieO {
} return k. is Veggie 0; }
Object whatHolderO {
W hy does it include isVeggie and whatHolder return k.whatHolderO; }
methods? }

121
Is it obvious how the new methods work? Totally. In both cases isVeggie just checks
the rest of the Kebabv, because green
peppers and zucchini are vegetables.
Similarly, whatHolder returns whatever
holder belongs to the rest of the Kebabv.

122
And then there were six. Yes, now Kebabv has six variants.

123
Which of these points is closer to the origin: The second one,

new ManhattanPt(3,4) because its distance to the origin is 6 while


the first point's distance is 7.
and

new ManhattanPt(1,5)?

12.
Good. Which of the following points is closer The first one, clearly. Its distance to the
to the origin: origin is 5, but the second distance is 13.
new CartesianPt(3,4)
or

new CartesianPt(12,5)?

Methods to Our Madness 37


Copyrighted Material
125
We added the method closerToO to The definitions are nearly identical. The
CartesianPt. It consumes another CartesianPt method forManhattanPt consumes a
and determines whether the constructed or ManhattanPt and determines which of those
the consumed point is closer to the origin. two points is closer to the origin.

class CartesianPt extends PointD { class ManhattanPt extends PointD {


int x; int Xj
int y; int y;
CartesianPt(int _x int _y) {
, ManhattanPt(int _x,int _y) {
x _Xj
= x = -x;
Y = -Yj } y = -y; }

int distanceToOO { int distanceToOO {


return lvx2 +y2J;} return x + y; }
boolean closerToO(CartesianPt p) { boolean closerToO(ManhattanPt p) {
return return
distanceToOO ::; p.distanceToOO; } distanceToOO ::;1 p.distanceToOO; }
} }

Add the corresponding method to


ManhattanPt. 1 This is the two character symbol <=.

126
What is the value of false.
new ManhattanPt(3,4)
.closerToO(new ManhattanPt(1,5»?

127
What is the value of true, obviously.
new ManhattanPt(1,5)
.closerToO(new ManhattanPt(3,4»?

128
What is the value of false, and true is the value of
new CartesianPt(12,5) new CartesianPt(3,4)
.closerToO(new CartesianPt(3,4»? .closerToO(new CartesianPt(12,5».

129
So finally, what is the value of That's nonsense.
new CartesianPt(3,4)
.closerToO(new ManhattanPt(1,5»?

38 Chapter 2
Copyrighted Material
130
Why? The method closerToO can only consume
CartesianPts, not ManhattanPts.

131
How can we fix that? We could replace

(CartesianPt p)
by

(PointD p)
in the definition of closerToO for CartesianPt.

132
Ifwe do that , can we st ill determine the Yes, because the definition of PointD
value of obl igates every variant to provide a method
p.distanceToO()? named distanceToO.

133
Why does it help to replace (CartesianPt p) EveryCartesianPt is a PointD, and every
by (PointD p)? ManhattanPt is a PointD, too.

13_
Here is the improved CartesianPt. Easy.

class CartesianPt extends PointD { class ManhattanPt extends PointD {


int x; int Xi
int y; int Yi
CartesianPt(int x int -y) {
_ , ManhattanPt(int _x,int _y) {
x = _x; x =x; _

Y = -Yi } y = -Y; }

int distanceToOO { int distanceToOO {


return lvlx2+y2Ji} return x + Yi }
boolean closerToO(PointD p) { boolean closerToO(PointD p) {
return return
distanceToOO � p.distanceToOO; } distanceToOO � p.distanceToOOi }
} }

Improve the definition of ManhattanPt.

135
Is the definit ion of closerToO in CartesianPt Yes, they are identical.

the same as the one in ManhattanPt?

Methods to Our Madness 39


Copyrighted Material
136
Correct, and therefore we can add a copy to Looks correct.
the abstract class PointV and delete the
definitions from the variants.

abstract class PointV {


boolean closerTo01 (PointV p) {
return
distanceToOO ::; p. distanceToOOj }
abstract int distanceToOOj
1 The method closerToO is a template and the method
} distanceToO is a hook in the template method pattern
instance [4].

What else do the two point variants have in 137 Their fields. Shouldn't we lift them, too?
common?

138
Yes. It's tricky, but here is a start. This not only lifts x and Y, it also introduces
a new constructor.
abstract class PointV {
int Xj
int Yj
PointV(int _x,int _y) {
x _Xj
=

Y -Yj }
=

boolean closerToO(PointV p) {
return
distanceToOO ::; p.distanceToOOj }
abstract int distanceToOOj
}

139
Absolutely. And we need to change how a Mimicking this change is easy. But what
CartesianPt is built. Define ManhattanPt. does sup er(_ x ,_ y) mean?

class CartesianPt extends PointV { class ManhattanPt extends PointV {


CartesianPt(int _x,int _y) { ManhattanPt(int _x,int _y) {
super(_x,_Y)j } super( _ x ,_ y)j }

int distanceToOO { int distanceToOO {


return l VX2 + y2 J j } return x + Yj }
} }

40 Chapter 2
Copyrighted Material
140
The expressions super(_x,_y) in the That's simple.
constructors CartesianPt and ManhattanPt
c reate a PointP with the appropriate fields,
and the respective constructor guarantees
that the point becomes a CartesianPt or a
ManhattanPt.

141
Do we now have everything that Yes, and those things that distinguish the
characterizes PointPs in the datatyp e? two var iants from each other reside in the
corresponding variants.

, ..
Is this a long chapter? Yes, let's have a short snack break.

Methods to OUf Madness 41


Copyrighted Material
Copyrighted Material
Do you like to eat pizza? Looks like good toppings. Let's add Sausage.

abstract class Pizzav { class Sausage extends Pizzav {


( Pizzav p;
-- Pizza ) --

Sausage(PizzaV -p) {
} �------

p = -Pi }

class Crust extends Pizzav {


( Sausage )
(
--

} -- Crust
�------
) -- }
-

class Cheese extends Pizzav {


Pizzav p;
Cheese(PizzaV -p) {
p -p; }
=

-- (
----�
Cheese ) --

class Olive extends Pizzav {


Pizzav p;
Olive(PizzaV -p) {
p -p; }
=

} --
(� _
O
_l_ i ve_ _�) --

class Anchovy extends Pizzav {


Pizzav p;
Anchovy(PizzaV -p) {
p -p; }
=

-- ( Anchovy ) -

What's New? Copyrighted Material 43


Here is our favorite pizza: This looks too salty.
new Anchovy(
new Olive(
new Anchovy(
new Anchovy(
new Cheese(
new Crust()))))).

3
How about removing the anchovies? That would make it less salty.

Let's remove them. What is the value of It should be a cheese and olive pizza, like
new Anchovy( this:
new Olive( new Olive(
new Anchovy( new Cheese(
new Anchovy( new Crust())).
new Cheese(
new Crust())))))
.remA10?

1 A better name for these methods would be removeAnchovy!


but then our definitions wouldn't fit into these columns.

What is the value of It should be a cheese, sausage, and olive


new Sausage( pizza, like this:
new Olive( new Sausage(
new Anchovy( new Olive(
new Sausage( new Sausage(
new Cheese( new Cheese(
new CrustO))))) new Crust())))) .
. remAO?

Does remA belong to the datatype PizzaD 6 Yes, and it produces them, too.
and its variants?

44 Chapter 3
Copyrighted Material
7
Define the methods that belong to the five We didn't expect you to know this one.
variants. Here is a start.

abstract PizzaD remAO;

Pizza

PizzaD remAO {
return new CrustO; }

Crust

Define the two methods that belong to Olive 8 The Olive and Sausage methods are similar
and Sausage. We've eaten the cheese already. to the Cheese method.

PizzaD remAO { PizzaD remAO {


return new Cheese(p.remA()); } return new Olive(p.remA()); }

Cheese Olive

PizzaD remAO {
return new Sausage(p.remA()); }

Sausage

Explain why we use The cheese, the olives, and the sausages on
new Cheese . . . , the pizzas must be put back on top of the
new Olive , and
. . . pi zza that p.remAO produces.
new Sausage ...
when we define these methods.

10
The methods remA must produce a PizzaD , Yes, and now the methods of remA produce
so let's use new CrustO, the simplest PizzaD, pizzas without any anchovies on them.
for the method in Anchovy.

PizzaD remAO {
return new CrustO; }

Anchovy

What's New? 45
Copyrighted Material
11
Let's try it out on a small pizza: That's easy. The object is an Anchovy. So
new Anchovy( the answer is new CrustO.
new Crust())
.remAO·

I'
Is Absolutely, but what if we had more
new CrustO anchovies?

like
new Anchovy(
new CrustO)

without anchovy?

13
No problem. Here is an example: That's easy again. As before, the object is an
new Anchovy( Anchovy so that the answer must still be
new Anchovy( new CrustO.
new Crust()))
.remAO·

14
Okay, so what if we had an olive and cheese Well, this object is an Olive and its p stands
on top: for
new Olive( new Cheese(
new Cheese( new Anchovy(
new Anchovy( new Anchovy(
new Anchovy( new CrustO))).
new CrustO))))
.remAO?

15
Then, what is the value of It is the pizza that

new Olive(p.remA()) new Cheese(


new Anchovy(
where p stands for
new Anchovy(
new Cheese(
new CrustO)))
new Anchovy(
.remAO
new Anchovy(
produces, with an olive added on top.
new CrustO)))?

46 Chapter 3
Copyrighted Material
16
What is the value of It is
new Cheese( new Cheese(p.remA()),
new Anchovy(
where p stands for
new Anchovy(
new CrustO)))
new Anchovy(
.remAO? new Anchovy(
new Crust())).

17
And what is the value of It is the pizza that
new Cheese( new Anchovy(
new Anchovy( newAnchovy(
new Anchovy( new Crust()))
new Crust())) .remAO
.remA())? produces, with cheese added on top.

18
Do we know the value of Yes, we know that it produces new CrustO.
new Anchovy(
new Anchovy(
new CrustO))
.remAO?

Does that mean that new CrustO is the 19 No, we still have to add cheese and an olive.
answer?

20
Does it matter in which order we add those Yes, we must first add cheese, producing
two toppings? new Cheese(
new Crust())
and then we add the olive.

21
So what is the final answer? It is
new Olive(
new Cheese(
new Crust())).

What's New? 47
Copyrighted Material
22
Let's try one more example: It should be a double-cheese pizza.
new Cheese(
new Anchovy(
new Cheese(

new CrustO»)

.remAO·
What kind of pizza should this make?

23
Check it out! The object is an instance of Cheese so the
value is
new Cheese(p.remAO)
wherep stands for
new Anchovy(

new Cheese(

new CrustO».

24
Doesn't that mean that the result is Yes, it puts the cheese on whatever we get
new Cheese( for
new Anchovy( new Anchovy(
new Cheese( new
Cheese(
new CrustO» new Crust()))

.remA())? .remAO·

2
What about 5 Now the object is an anchovy.
new Anchovy(
new Cheese(

new Crust(»)

.remAO?

6
2
And the answer is Yes, but we need to add cheese on top.

new CrustO?

21
Does that mean the final answer is Yes, though it's not the answer we want.
new Cheese(
new Crust(»?

48 Chapter 3
Copyrighted Material
2S
What do we want? A double-cheese pizza like
new Cheese(
new Cheese(
new Crust())),

because that's what it means to remove


anchovies and nothing else.

29
Which remA method do we need to change The one in Anchovy.
to get the cheese back?
Pizzav remAO {
return p.remAO; }

Anchovy

30
Does this remA still belong to Pizzav? Yes, and it still produces them.

The Third Bit of Advice

When writing a function that returns


values of a datatype, use new to create
these values.

31
We could add cheese on top of the anchovies. Yes, that would hide their taste, too.

32
What kind of pizza is Easy, it adds cheese on top of each anchovy:
new Olive( new Olive(
new Anchovy( new Cheese(
new Cheese( new Anchovy(
new Anchovy( new Cheese(
new Crust())))) new Cheese(
.topAwC10? new Anchovy(
new Crust())))))).
1 A better name for these methods would be
topAnchovyvi thCheese.

33
Did you notice the underlines? Yes, they show where we added cheese.

What's New? 49
Copyrighted Material
3.
And what is Here we don't add any cheese, because the
new Olive( pizza does not contain any anchovies:
new Cheese( new Olive(
new Sausage( new Cheese(
new Crust()))) new Sausage(
.topAwC()? new Crust()))).

35
Define the remaining methods. We expect you to know some of the answers.

abstract Pizzav topAwCO; Pizzav topAwCO {


return new Cheese(p.topAwC()); }
Pizza
Cheese

:
Pizzav topAwCO {
Pizzav topAwCO {
return new Crust(); }
return new Olive(p.topAwC()); }

Crust
Olive

Pizzav topAwCO {
return new Sausage(p.topAwC())j }

Sausage

36
Take a look at this method. With that definition, topAwC would give the
same results as remA. The method topAwC
Pizzav topAwCO { in Anchovy must put the anchovy back on
return p.topAwC(); } the pizza and top it with cheese.

Anchovy Pizzav topAwCO {


return
new Cheese(
new Anchovy(p. topA wC() ) ) ; }

Anchovy

50 Chapter 3
Copyrighted Material
37
How many layers of cheese are in the result One, because remA removes all the
of anchovies, so that topAwC doesn't add any
( new Olive( cheese.
new Anchovy(
new Cheese(
new Anchovy(
new Crust()))))
.remA())
.topAwC()?

38
How many occurrences of cheese are in the Three, because topAwC first adds cheese for
result of each anchovy. Then remA removes all the
( new Olive( anchovies:
new Anchovy( new Olive(
new Cheese( new Cheese(
new Anchovy( new Cheese(
new Crust())))) new Cheese(
. topAwC()) new Crust())))) .
.remA()?

Perhaps we should replace every anchovy 39 We just did something like that.
with cheese.

Is it true that for each anchovy in x 40 Yes, and it does more. Once all the cheese is

x.topAwC().remA() 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
Copyrighted Material
Here is a different description: 42 Here is a skeleton.
"The methods look at each topping of a

pizza and replace each anchovy with Pizza1J subAbC() {


cheese." return new CrustOi }
Define the methods that match this
Crust
description. Call them subAbC.1 Here is the
abstract method.
Pizza1J subAbC() {
abstract Pizza1J subAbCO; return new Cheese(p.subAbC())j }

Pizza Cheese

Pizza!) subAbCO {
return new Ofive(p.subAbCO); }

Olive

Pizza:/) subAbCO {
return ; }
Anchovy

Pizza!) subAbCO {
return new Sausage(p.subAbC())j }

1 A better name for these methods would be


Sausage
substi tuteAnchovybyCheese.

43
Does this skeleton look familiar? Yes, this skeleton looks just like those of
topAwC and remA.

Define the method that belongs in Anchovy. 44 Here it is.

Pizza:/) subAbC() {
return new Cheese(p.subAbC())j }

Anchovy

52 Chapter 3
Copyrighted Material
Collection time. 1 45 The classes are getting larger.

abstract class PizzaD { class Olive extends PizzaD {


abstract PizzaD remAO; PizzaD p;
abstract PizzaD topAwCO ; Olive(PizzaD -p) {
abstract PizzaD subAbCO; p = Pi }
-

}
PizzaD remAO {
return new Olive(p.remA())j }
class Crust extends PizzaD { PizzaD topAwCO {
Pi zzaD remAO { return new Olive(p.topAwC()); }
return new C ru stOj }
PizzaD subAbCO {
PizzaD topAwCO { return new Olive(p.subAbC()); }
return new CrustO ; }
PizzaD subAbCO { }
return new CrustO; }

} class Anchovy extends PizzaD {


PizzaD p;
Anchovy( Pi zzaD -p) {
class Cheese extends PizzaD { p = -Pi }
PizzaD p;
Cheese( PizzaD -p) { PizzaD remAO {
p = Pj }
-
return p.remAO; }
PizzaD t op AwC O {
PizzaD remAO { return
return new Cheese(p.remA())j }
new Cheese(
PizzaD topAwCO { new Anchovy(p.topAwC())); }
return new Cheese(p.topAwC())j }
PizzaD subAbCO {
PizzaD subAbCO { return new Cheese(p.subAbC()); }
return new Cheese(p.subAbC())j }
}
}

class Sausage extends PizzaD {


PizzaD p;
Sausage(PizzaD -p) {
p = -Pi }

PizzaD remAO {
return new Sausage(p.remA()); }
PizzaD topAwCO {
return new Sausage(p.topAwC()); }
PizzaD subAbCO {
return new Sausage(p.subAbC()); }

1 This is similar to the interpreter and composite }


patterns [41.

MIat's New? 53
Copyrighted Material
46
Let's add more Pizzav foods. Good idea.

47
Here is one addition: Spinach. Yes, we must define three concrete methods
for each variant of Pizzav
class Spinach extends Pizzav {
Pizzav p;
Spinach(PizzaV -p) {
p -p; }
=

Pizzav remAO {
return new Spinach(p.remA()); }
Pizzav topAwCO {
return new Spinach(p.topAwC()); }
Pizzav subAbCO {
return new Spinach(p.subAbC()); }
}

8
4
po we need to change Pizzav, Crust, Cheese, No. When we add variants to the datatypes
Olive, Anchovy, or Sa u sage? we have defined, we don't need to change
what we have.

49
Isn't that neat? Yes, this is a really flexible way of defining
classes and methods. Unfortunately, the
more things we want to do with Pizzavs, the
more methods we must add.

50
True enough. And that means cluttered That would be great, because these
classes. Is there a better way to express all definitions are painful to the eye. But we
this? don't know of a better way to organize these
definitions yet.

Don't worry. We are about to discover how 51 Great.


to make more sense out of such things.

And now you can replace anchovy with 52 We will stick with anchovies.
whatever pizza topping you want.

54 Chapter 3
Copyrighted Material
�.� .
� (()

@@r�J� {!� (@)��R'


�IDIT(®l���!l

Copyrighted Material
Wasn't this last collection overwhelming? It sure was. We defined seven classes and
each contained three method definitions.

Could it get worse? It sure could. For everything we want to do


with Pizzav, we must add a method
definition to each class.

Why does that become overwhelming? Because it becomes more and more difficult
to understand the rationale for each of the
methods in a variant and what the
relationship is between methods of the same
name in the different variants.

Correct. Let's do something about it. Take a 4 These methods look familiar. Have we seen
close look at this visitor class. them before?

class OnlyOnionsv {
boolean jorSkewerO {
return true; }
boolean jorOnion(ShishV s) {
return s.onlyOnionsO; }
boolean jorLamb(ShishV s) {
return false; }
boolean jorTomato(ShishV s) {
return false; }
}

V This superscript is a reminder that the class is a visitor


class. Lower superscripts when you enter this kind of
definition in a file: OnlyOnionsV.

Almost. Each of them corresponds to an That's right. The major difference is that
onlyOnions method in one of the va ria nt s of they are all in one class, a visitor, whose
Shishv. name is OnlyOnionsv.

Is only Onions different from OnlyOnionsV? The former is used to name methods, the
latter names a class.

Come to Our Carousel Copyrighted Material 57


7
W hat p o mt?

And that's the whole point.

We want all the methods in one class. 8 What methods?

Those methods that would have the same If we could do that, it would be much easier
name if we placed them into the variants of a to un derst and what action these methods
dat atype in one class. perform .

10
That's what we are about to do. We are It's about time.
going to separate the action from the
datatype.

11
What is t he difference between the method Everything following return is the same.
onlyOnions in the Onion variant and t he
method for Onion in t he visitor OnlyOnionsV?

12
Right. W hat is the difference? The difference is that onZyOnions in Onion is
followed by 0 and that forOnion in
OnlyOnionsv is followed by (ShishV s).

13
Yes, that is the difference. Are the other for Indeed, they are.
methods in OnlyOnionsv related to their
counterparts in the same way?

It is time to discuss the boring part. 14 What boring part?

IS
The boring part tells us how to make all of True, we still don ' t know how to make
this work . Shishv and its variants work with this visitor
class, which contains all the action.

58 Chapter 4
Copyrighted Material
16
Now take a look at this. This is a strange set of definitions. All the
only Onions methods in the variants look
abstract {
class Shishv alike. Each of them uses an instance of
I OnlyOnionsv ooFn new OnlyOnionsV();
= I OnlyOnionsv, which is created in the

abstract boolean onlyOnions(); datatype, to invoke a for method with a

} matching name.

class Skewer extends Shishv {


boolean onlyOnionsO {
return ooFn.forSkewerO; }
}

class Onion extends Shi shv {


Shishv s;
Onion(ShishV _s} {
8 = _S; }

boolean oniyOnionsO {
return ooFn.forOnion(s}; }
}

class Lamb extends Shishv {


ShishV 8;
Lamb(ShishV _s} {
s = _8; }

boolean onlyOnionsO {
return ooFn.forLamb(s); }
}

class Tomato extends Shishv {


Shishv s;
Tomato(ShishV _s) {
8 = _s; }

boolean onlyOnionsO {
return ooFn.forTomato(s); }
}

Come to Our Carousel 59


Copyrighted Material
What does the forOnion method in Onion 17 If "consume" refers to what follows the name
consume? between parentheses, the method consumes
s, which is the rest of the shish.

1S
That's what "consumption" is all about. Nothing, because a skewer is the basis of a
And what does the forSkewer method in shish and therefore has no fields.
Skewer consume?

19
So what does the ( ShishV s) mean in the It is always the rest of the shish, below the
definition of forOnion? top layer, which is an onion. In other words,
it is everything but the onion.

20
Very good. The notation ( ShishV s) means That makes sense and explains
thatforOnion consumes a Shishv and that s.onlyOnionsO·
between { and }, s stands for that shish.

21
Explain s.onlyOnionsO. Here are our words:
"s is a Shishv, and therefore s.onlyOnionsO
determines whether what is below the
onion is also edible by an onion lover."

22
Explain ooFn.forOnion(s). You knew we wouldn't let you down:
"ooFn.forOnion says that we want to use
the method we just described. It consumes
a Shishv, and s is the Shishv that
represents what is below the onion."

So what is the value of 23 It is still true.


new Onion (
new Onion (
new Skewer()))
.onlyOnionsO?

2.
And how do we determine that value with We start w ith the only Onions method in
these new definitions? Onion, but it immediately uses the forOnion
method on the rest of the shish.

60 Chapter 4
Copyrighted Material
And what does the for Onion method do? 25 It checks whether the rest of this shish is
edible by onion lovers.

26
How does it do that? It uses the method only Onions on s.

27
Isn't that where we started from? Yes, we're going round and round.

28
Welcome to the carousel. Fortunately, the shish shrinks as it goes
around, and when we get to the skewer we
stop.

9
And then the ride is over and we know that 2 That's exactly it.
for this example the answer is true.

Do we need to remember that we are on a 3D No! Now that we understand how the
carousel? separation of data and action works, we only
need to look at the action p art to understand
how things work.

Is one example enough? 31 No, let's look at some of the other actions on
shishes and pizzas.

Let's look at is Vegetarian next. Here is the 32 What about it?


beginning of the protocol. 1

abstract class Shishv {


OnlyOnionsv ooFn new OnlyOnionsvO;
=

IsVegetarianv ivFn new IsVegetarianvO;


=

abstract boolean only Onions 0;


abstract boolean is Vegetarian 0 ;
}

1 The Amenean Heritage Dictionary defines protocol as


" (tJ he form of ceremony and etiquette observed by diplomats
and heads of state." For us, a protocol is an agreement on
how classes that specify a datatype and its variants interact
with classes that realize functions on that datatype.

Come to Our Carousel 61


Copyrighted Material
Write the rest! 33 We must add two lines to each variant, and
they are almost the same as those for ooFn.

class Skewer extends Shish'D {


boolean onlyOnionsO {
return ooFn.forSkewer(); }
boolean is Vegetarian 0 {
return ivFn.forSkewerO ; }
}

class Onion extends Shish'D {


Shish'D s;
Onion(ShishV _s} {
s = _s; }

boolean onlyOnionsO {
return ooFn.forOnion(s}; }

� boolean is Vegetarian 0 {
eturn ivFn.forOnion(s); }

class Lamb extends Shishv {


Sh i sh'D s;
Lamb(ShishV s} {_

s _s; }
=

boolean onlyOnionsO {
return ooFn.forLamb(s); }
boolean is Vegetarian 0 {
return ivFn.forLamb(s}; }
}

class Tomato extends Shishv {


Sh i sh'D s;
Tomato(Shish v _s) {
s = s; }_

boolean o nlyOnionsO {
return ooFn.forTomato ( s); }
boolean is VegetarianO {
return ivFn.forTomato ( s); }
}

62 Chapter 4
Copyrighted Material
3.
That's why we call this part boring. True, the re ' s very little to think about. It
could be done automatically.

How do we define the visitor? 35 Does that refer to the class that contains the
actions?

36
Yes, that one. Define the visitor. It is like OnlyOnionsv except for the method
forTomato.

class IsVegetarian v {
boolean forSkewerO {
return true; }
boolean forOnion(ShishV s) {
return s. is Vegeta rian (); }
boolean forLamb(ShishV s) {
return false; }
boolean forTomato(ShishV s) {
return s. is Vegetarian(); }
}

37
Are we moving fast? Yes, but there are only a few interesting
parts. The protocol is always the same and
boring; the visitor is always closely related to
what we saw in ch apter 2.

How about a tea break? 38 Instead of coffee?

The Fourth Bit of Advice


When writing several functions for the
same self-referential datatype, use
visitor protocols so that all methods for
a function can be found in a single
class.

Come to Our Carousel 63


Copyrighted Material
39
Is No, it ' s a pizza.
newAnchovy(
new Olive(
new Anchovy(
I abstract class {} Pizzav

new Cheese(
new Crust())))) I class extends Pi z v { }
Crust z a

a shish kebab?
class Cheese extends P izzav {
Pizzav p;
Cheese(PizzaV -p) {
P -Pi }
=

class Olive extends Pizzav {


Pizzav p;
Oli ve (P izzaV -p) {
P = Pi }-

class Anchovy extends Pizzav {


Pizzav P;
Anchovy(PizzaV -p) {
P -Pi}
=

class Sausage extends Pi zza v {


Pi zza v p;
Sausage(PizzaV -p) {
P = -Pi}

40
So what do we do next? We can define the protocol for the methods
that belong to Pizzav and its extensions:
remA, topAwC, and subAbC.

64 Chapter 4
Copyrighted Material
Great! Here is the ab s ract
t portion of the 41 How innovative! The variants are totally
protocol. mindless, now.

-l
abstract class PizzaD { class Olive extends PizzaD {
RemAv remFn new R emAv O ;
= PizzaD p;
TopAwCv topFn new TopAwCvO;
= Olive(PizzaD -p) {
SubAbCv subFn = new SubAbCvO; p -p; }
=

abstract PizzaD remAO;


abstract PizzaD topAwCO; PizzaD remA() {
abstract PizzaD su bAbCO; return remFn.forOlive(p); }
} PizzaD topAwCO {
return topFn.forOlive (p); }
And here are some variants.
PizzaD subAbCO {
return subFn.forOlive (p); }
class Crust exte nds PizzaD { }
PizzaD remA() {
return remFn.forCrustO; }
class Anc hovy extends Pizzav {
PizzaD t opAwC O {
PizzaD p;
return topFn.jorCrustO; }
Anchovy(PizzaD -p) {
PizzaD subAbCO {
p = -p; }
return subFn.jorCrustO; }
} PizzaD remAO {
return remFn.forAnchovy(p); }
class Cheese extends PizzaD { PizzaD topAwCO {
PizzaD p; return topFn.forAnchovy(p); }
Cheese(PizzaD -p) { PizzaD s ubA b C ( ) {
p = -p; } return subFn.forAnchovy(p); }
}
PizzaD remAO {
return remFn.jorCheese(p); }
class Sausage extends Pi zzaD {
PizzaD topAwCO {
PizzaD p;
return topFn.jorCheese(p); }
Sausage(PizzaD -p) {
PizzaD su bA bCO {
p = -p; }
return subFn.forCheese(p); }
} PizzaD remAO {
return remFn.forSausage(p); }
Define the rest. PizzaD topAwCO {
return topFn.forSausage(p); }
PizzaD subAbCO {
return subFn.forSausage(p); }
}

Come to Our Carousel 65


Copyrighted Material
.2
We are all set. Is it time to define the visitors that
correspond to the methods remA, topA wC,
and subAbC?

43
Okay, here is R e m Av. By now, even this is routine.

class RemAv { class TopAwCv {


PizzaD forCrustO { Pizzav for CrustO {
return new C rust O j } return new CrustOj }
PizzaD forCheese(PizzaD p) { Pizzav for Cheese(PizzaV p) {
return new Cheese(p.remAO); } return new Cheese(p.topAw CO)j }
PizzaD forOlive(PizzaD p) { Pizzav forOlive(PizzaV p) {
return new Olive(p.remA())j } return new Olive(p.topAwC())j }
Pizzav forAnchovy(PizzaV p) { Pizzav forAnchovy(PizzaV p) {
return p. remAOj } return
PizzaD forSausage(PizzaD p) { new Cheese(
return new Sausage(p.remA())j } new Anchovy(p.topAw C()))j }
} Pizzav forSausage(PizzaV p) {
return new Sausage(p.topAwC()); }
}
Define TopAwCv.

••
The last one, SubAbCv, is a piece of cake. And we thought we were working with a
pizza pie.

class SubAbCv {
Pizzav forCrustO {
return new CrustOj }
Pizzav for Cheese(PizzaD p) {
return new Cheese(p.subAbC()); }
Pizzav forOlive(PizzaV p) {
return new Olive(p.subAbC())j }
Pizzav forAnchovy(PizzaD p) {
return new Cheese(p.subAbC()); }
PizzaD forSausage(PizzaV p) {
return new Sausage(p.subAbC())j }
}

66 Chapter 4
Copyrighted Material
�o
@�EvJJ@@{S� �Jf��
lP��@Yrr�Il� [) �@)@
"L-J

.' '7
7 .
� 7
-

--

Copyrighted Material
Have we seen this kind of definition before?l 1 What? More pizza!

abstract class PieD {


-( Pie
)-
} �---�

class Bot extends PieD {


-( Bot )-
}

class Top extends PieD {


Object t;
PieD r;
Top(Object _t,PieD _r) {
t _t;=

r = _r; }

-( �--=----�
Top )-
}

1 Better names for these classes would be PizzaPieD, Bottom


and Topping, respectively.

Yes, still more pizza, but this one is different. Yes, it includes only one variant for adding
toppings to a pizza, and toppings are Objects.

What kind of toppings can we put on these Any kind, because Object is the class of all
kinds of pizza? objects. Here are some fish toppings.

abstract class Fi shD {}

class Anchovy extends Fi shD {}

class Salmon extends FishD {}

class Tuna extends FishD {}

Objects Are People, Too 69


Copyrighted Material
Nice datatype. Is It is a pizza pie, and so is
new Top(new AnchovyO, new Top(new TunaO,
new Top(new TunaO , new Top(new Integer(42) ,
new Top(new An chovyO , new Top(new AnchovyO,
new Bot()))) new Top(new Integer(5),
a pizza pie? new Bot())))).

What is the value of It is this fishy pizza pie:


new Top(new Sa lmo n O, new Top(new SalmonO ,
new Top(new AnchovyO, new Top(new TunaO,
new Top(new TunaO, new Bot())).
new Top(new AnchovyO,
new Bot()))))
.remAO?

Is it true that the value of Yes. The pizza that comes out is the same as

new Top(new Sa lmonO , the one that goes in, because there are no
new Top(new T u naO , anchovies on that pizza.
new BotO))
.remAO
is
new Top(new SalmonO,
new Top(new T unaO ,
new Bot()))?

7
Does remA belong to PieD? Yes, and it produces pizza pies.

Define the protocol for RemAv. We provide This is easy by now.


the abstract part.
PieD remAO {
RemAV raFn = new RemAVOj return raFn.forBotOj }
abstract PieD remAO;
Bot
Pie
PieD remAO {
return raFn.forTop(t,r)j }

Top

70 Chapter 5
Copyrighted Material
Great. Isn't that easy? 9 Easy and boring.

10
What part of this exercise differs from Determining how many fields a variant
datatype to datatype? contains. In our case, we had zero and two.

11
Anything else? No, from that we know that raFn.forBot is
followed by 0 and raFn.forTop by ( t , r) .

12
Why (t,r)? Because these are the fields of Top.

13
Let's define the visitor Re m A v. Here are some guesses.

class RemA v { class RemAv {


PieD jorBotO { PieD jorBotO {
return ;} return new BotO ; }
PieD jorTop(Object t , PieD r) { PieD jorTop(Object t,PieD r) {
if (new AnchoVYO.equals(t)) if (new AnchovyO.equals(t))
return ____ return r.remAO;
else else
return ____ ; } return new Top(t,r.remA()); }
} }

Great guesses! What does 14 We guess:


if ( exprl) "This produces the value of either expr2 or
return expr2; expr3, depending on whether or not exprl
else is determined to be true or false,
return expr3; respectively. "
mean?

15
And what does We could guess:

new AnchovyO. equals ( t) "This expression determines whether t is


equal to new AnchovyO."
mean?

16
Not yet. It depends on what equals means. What?

Objects Are People, Too 71


Copyrighted Material
17
What is the value of The "Not yet." implies that the value is false.
new Anchovy().equals(new Anchovy())?

IS
Yes! And what is the value of false,
new AnchovyO.equals(new Tuna())? because no anchovy is a tuna.

19
The class Object contains a method called If we know that equals's answer is always
equals. This method compares one Object to false, why bother to use it?
another, and it always returns false.l

1 Not always, We explain the correct answer in chapter 10.

We must define it anewl for all classes whose 20 Okay. How?


instances we wish to compare.

1 In Java, redefining a method is called ·'overriding."

21
For Fishv and its variants it works like this. Assuming that

I abstract class Fishv {} (0 instanceof Tuna)


is true when 0 is an instance of Tuna, these
method definitions are obvious.
class Anchovy extends Fishv {
publicI boolean equals(Object 0 ) {
return ( 0 instanceof Anchovy); }
}

class Salmon extends Fishv {


public boolean equals(Object 0 ) {
return (0 instanceof Salmon); }
}

class Tuna extends Fishv {


public boolean equals(Object 0) {
return (0 instanceof Tuna); } I The class Object is defined in a separate package, called
} java.lang.Object. Overriding methods that reside in other
packages requires the word public.

72 Chapter 5
Copyrighted Material
22
Aren't they? Is every value constructed with Yes. Every such value is an Object, because
new an instance of Object? every class extends Object directly or
indirectly.

23
Ifclass A extends B, is every value created Yes, and of the class that B extends and so
by new A( ... ) an instance of class B? on.

Now, what is the value of 24 true,


because new AnchovyO is an instance of
new Anchovy(). equals ( new Anchovy())?
Anchovy.

25
Yet the value of Of course, because an anchovy is never a

new AnchovyO.equals(new Tuna()) tuna.


is still false.

26
Could we have written RemAv without using Absolutely, instanceof is enough.
equals?
class RemA v {
Piev JorBotO {
return new BotO; }
Piev JorTop(Object t,Piev r) {
if (t instanceof Anchovy)
return r.remA()j
else
return new Top(t,r.remAO); }
}

Why haven't we defined it this way?

27
Easy, because we want to generalize RemA v We can do that, but when we use the
so that it works for any kind of fish topping. methods of the more general visitor, we need
to say which kind of fish we want to remove.

28
What are good names for the more general How about remFish and Rem Fish v?
methods and visitor?

Objects Are People, Too Copyrighted Material 73


29
How do we use remFish? We give it a Fishv.

30
Add the protocol for RemFishv. We designed The rest is routine.
the abstract portion.
Piev remFish(FishV f) {
RemFishV rfFn new RemFishvO;
=
return rfFn.forBot(f); }
abstract Piev remFish(FishV f);
Bot

Pie
Piev remFish(FishV f) {
return rfFn.forTop(t,r, f); }

Top

31
Where do (f) and (t,r,J) come from? The f stands for the Fish v we want to
remove in both cases. The t and the r are
the fields of Top; Bot doesn't have any.

3.
Let's define RemFishv and its two methods. Instead of comparing the top layer t of the
pizza to Anchovy, we now determine whether
it equals the Fishv f, which is the additional
value consumed by the method.

class RemFishv {
PieV forBot(FishV f) {
return new BotO; }
PieV jorTop(Object t,PieV r,FishV f) {
if (t. equals ( t))
return r.remFish(f);
else
return new Top(t,r.remFish(f)); }
}

33
If we add another kind of fish to our Nothing, we just have to remember to add
datatype, what would happen to the equals to the new variant.
definition of RemFishv?

74 Chapter 5
Copyrighted Material
34
Let's try it out with a short example: The object is a topping, so we use forTop
new Top(new AnchovyO, from Rem Fishv.
new Bot())
.remFish(new Anchovy()).

5
3
Yes. What values does forTop consume? It consumes three values: new AnchovyO,
which is t, the top-most layer of the pizza;
new BotO, which is r, the rest of the pizza;
and new AnchovyO, which is f, the Fishv to
be removed.

3
And now? 6 Now we need to determine the value of
if (f. equals(t))
return r.remFish(f);
else
return new Top(t,r.remFish(f));
where t, r, and f stand for the values just
mentioned.

37
So? Given what f and t stand for, f.equals(t) is
true. Hence, we must determine the value of
r. remFish(f) .

What is the value of 38 It is the same as


new BotO forBot(f),
.remFish(new Anchovy())? where f is new AnchovyO.

39
What does forBot in Rem Fish v produce? It produces new BotO, no matter what f is.

All clear? 40 Ready to move on, after snack time.

Objects Are People, Too Copyrighted Material 75


41
Does Yes, it looks like what we just evaluated.
new Top(new Integer(2),
new Top(new Integer(3),
new Top(new Integer(2),
new Bot())))
. remlnt(new Integer(3))
look familiar?

42
What does remlnt do? Itremoves Integers from pizza pies just as

remFish removes fish from pizza pies .

Who defined equals for Integer? 43 The Machine decided


new Integer(O).equals(new Integer(O))
to be true, and the rest was obvious.

44
Define the visitor RemlntV Wonderful! We do the interesting thing first.
This visitor is almost identical to Rem Fishv
We just need to change the type of what the
two methods consume.

class RemlntV {
Piev forBot(lnteger i) {
return new BotO; }
Piev JorTop(Object t,Piev r,lnteger i) {
--

if (i.equals(t))
return r.remlnt(i);
else
return new Top(t,r.remlnt(i)); }
}

Does it matter that this definit ion uses i and 45 No, i is just a better name than J, no other
not J? reason. As long as we do such substitutions
systematically, we are just fine.

46
Where is the protocol? It is so simple, let's save it for later.

76 Chapter 5
Copyrighted Material
Can we remove Integers from PieDs? 47 Yes.

48
Can we remove FishD from PieDs? Yes, and we use nearly identical definitions.

49
Let's combine the two definitions. If we use Object instead of the underlined
Integer above, everything works out.

50
Why? Because everything constructed w i th new is
an Object.

51
Just do it! It's done.

class Re m v {
PieD jorBot( Object 0 ) {
return new Bot(); }
PieD jorTop(Object t,PieD r,Object 0 ) {
--

if (o.equals(t))
return r.rem(o);
else
return new Top(i,r.rem(o)); }
}

Should we do the protocol for all these 52 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
Why not Rem Fishv and RemA v and They are unnecessary once we have Rem v
Remlntv?

Objects Are People, Too 77


Copyrighted Material
55
Here is the abstract portion of Piev. And here are the pieces for Bot and Top .

abstract class Piev { class Bot extends Piev {


Rem v remFn new Rem v 0 j
=
Piev rem(Object 0) {
abstract Piev rem(Object 0); return remFn.forBot(o); }
} }

class Top extends Piev {


Object t;
Piev r;
Top(Object _t,Piev _r) {
t= _t;
r _r; }
=

Piev rem (Object 0) {


return remFn.forTop(t,r,o); }
}

Let's remove some things from pizza pies: 56 Works like a charm with the same result as

new Top(new Integer(2), before.


new Top(new Integer(3),
new Top(new Integer(2),
new Bot())))
. rem(new Integer(3)).

And how about 57 Ditto.


new Top(new AnchovyO,
new BotO)
.rem(new Anchovy())?

58
Next: No problem. This, too, removes 3 and leaves
new Top(new AnchovyO, the other layers alone:
new Top(new Integer(3), new Top(new AnchovyO,
new Top(new ZeroO, new Top(new ZeroO,
new Bot()))) new Bot())) .
. rem(new Integer(3)).

78 Chapter 5
Copyrighted Material
What is the value of 59 Oops. The answer is
new Top(new AnchovyO, new Top(new AnchovyO,
new Top ( new Integer(3) , new Top(new Integer(3),
new Top(new ZeroO, new Top(new ZeroO,
new 8ot()))) new 8ot()))) .
. rem(new Zero())?

60
What's wrong with that? We expected it to remove new ZeroO from
the pizza.

61
And why didn't it? Because equals for Numvs uses Object's
equals, which always produces false-as we
discussed above when we introduced equals.

62
Always? Unless we define it anew for those classes
whose instances we wish to compare.

63
Here is the version of Numv ( including Adding equals to Zero is easy. We use
OneMoreThan) with its own equals. Define instanceof to determine whether the
the new Zero variant. consumed value is a new ZeroO.

[ abstract class Numv {} class Zero extends Numv {


public boolean equals(Object 0 ) {
return (0 instanceof Zero); }
class OneMoreThan extends Numv { }
Numv predecessor;
OneMoreThan(NumV -p) {
predecessor _Pi }
=
But what is the underlining of
((OneMoreThan) 0 )
public boolean equals(Object 0 ) { about? Wouldn't it have been sufficient to
if ( 0 instanceof OneMoreThan) write o.predecessor?
return
predecessor
. equals (
((OneMoreThan)o)l.predecessor);
else
return false; }
1 In Java, this is called (downward) casting, because
} OneMoreThan extends NumD.

Objects Are People, Too 79


Copyrighted Material
64
No. What is the type of o? Object, according to
(Object 0 ) ,
which is what declares the type of o.

So what is o.predecessor? 65 Nonsense.

66
Correct. What do we know after if has We know that o's type is Object and that it
determined that is an instance of OneMoreThan.
(0 instanceof OneMoreThan)
is true?

67
Precisely. So what does ((OneMoreThan)o) It converts the type of 0 from Object to
do? OneMore Th a n .

6S
What is ((OneMoreThan) o ) s type?
' Its type is OneMoreThan, and now it makes
sense to write
((OneMoreThan) o).predecessor.

69
Are 0and ((OneMoreThan)o) The underlying object is the same. But no,
interchangeable? the two expressions are not interchangeable,
because the former's type is Object, whereas
the latter's is OneMoreThan.

Is this complicated? 70 Someone has been drinking too much coffee.

Did you also notice the 71 How do the two uses of predecessor differ?
predecessor
. equals (
(( OneMoreTha n) 0). predecessor)
in equals for OneMoreThan?

72
The first one, predecessor, refers to the So the second one,
predecessor field of the instance of ((OneMoreThan) o).predecessor,
OneMoreThan on which we are using equals. refers to the predecessor field of the instance
And that field might not be a OneMoreThan. of OneMoreThan consumed by equals.

80 Chapter 5
Copyrighted Material
73
Yes. Are these two objects equal? If they are similar1 to the same int, they are
equal. But most of the time, they are not.

1 Check chapter 1 for usimilar."

74
Time for lunch? That's just in time.

75
Did you have a good lunch break? Yes, thank you.

76
Now what is the value of Now we get
new Top(new AnchovyO, new Top(new AnchovyO,
new Top(new Integer(3), new Top(new Integer(3),
new Top(new Z eroO ,
new Bot() )) ,

new Bot()))) which is precisely what we want.


. rem(new Z ero())?

And why? 77 Because equals now knows how to compare


NumDs.

Do we always add equals to a class? 78 No, only if we need it.

Do we need equals when we want to 79 Yes, we do.


substitute one item for another on a pizza
pie?

80
What is the value of It is the same pizza pie with all the anchovies
new Top(new AnchovyO, replaced by salmon:
new Top(new TunaO, new Top(new SalmonO,
new Top(new AnchovyO, new Top(new TunaO,
new Bot()))) new Top(new SalmonO,
.substFish(new SalmonO, new Bot()))).
new Anchovy())?

81
What kind of values does substFish consume? It consumes two fish and works on PieDs.

Objects Are People, Too 81


Copyrighted Material
82
And what does it produce? It always produces a PieD.

83
What is the value of It is the same pizza pie with all 3s replaced
new Top(new Integer(3), by 5s:
new Top(new Integer(2), new Top(new Integer(5),
new Top(new Integer(3), new Top(new Integer(2),
new 80t()))) new Top(new Integer(5),
.substInt(new Integer(5), new 80t()))).
new Integer(3))?

8.
What kind of values does substInt consume? It consumes two Integers and works on PieDs.

85
And what does it produce? It always produces a PieD

86
We can define SubstFishv. To get from SubstFishv to SubstlntV, we just
need to substitute FishD by Integer
class SubstFishv { everywhere and 'Fish" by "Int" in the class
PieD JorBot (FishD n,FishD 0) { and method names.
return new 80tO; }
PieD JorTop(Object t, class SubstlntV {
PieD r, PieD JorBot(lnteger n,lnteger 0) {
FishD n, return new 80tO; }
FishD 0) { PieD JorTop(Object t,
if (0. equals (t)) PieD r,
return new Top(n,r.substFish(n,o)); Integer n,
else Integer 0) {
return new Top(t,r.substFish(n,o)); } if (o.equals(t))
} return new Top(n,r.substInt(n,o));
else
Define Substlntv. return new Top(t,r.substInt(n,o)); }
}

87
Did we forget the boring parts? Yes, because there is obviously a more
general version like Rem v

82 Chapter 5
Copyrighted Material
88
Yes, we call it Substv. D efine it. We substitute Object for Fishv and Integer.

class SubstV {
Piev JorBot(Object n,Object 0) {
return new BotOj }
Piev JorTop(Object t,
Piev r,
Object n,
Object 0) {
if (0. equals ( t))
return new Top(n,r.subst(n,o))j
else
return new Top(t,r.subst(n,o))j }
}

89
Now it is time to add the protocol for SubstV The abstract part is obvious.
to Piev Here are the variants.
abstract class Piev {
class Bot extends Piev { Rem v remFn new Rem v 0 j
=

Piev rem(Object 0) { SubstV substFn new SubstVOj


=

return remFn.forBot ( 0); } abstract Piev rem(Object 0);


Piev subst (Object n,Object 0) { abstract Piev subst(Object n,Object 0);
return substFn.forBot(n,o)j } }
}

class Top extends Piev {


Object tj
Piev r;
Top(Object _t,Piev s) {
t _tj=

r = _rj }

P i ev rem (Object 0) {
return remFn.forTop(t,r,o)j }
Piev subst(Object n,Object 0) {
return substFn.forTop(t,r, n, o)j }
}

90
So? That was some heavy lifting.

Objects Are People, Too 83


Copyrighted Material
Copyrighted Material
1 •

Are protocols truly boring? We acted as If they were.

But, of course they are not. We just didn't Okay, here are the variants again.
want to spend much time on them. Let's
take a closer look at the last one we defined class Bot extends Piev {
in the previous chapter. Piev rem(Object 0) {
return remFn.forBot(0); }
abstract class Piev { Piev subst(Object n,Object 0) {
Rem v remFn new Rem v 0;
= return substFn.forBot(n,o); }
SubstV substFn new SubstVO;
=
}
abstract Piev rem(Object 0);
abstract Piev subst(Object n,Object 0);
class Top extends Piev {
}
Object t;
Piev r;
Top(Object _t,Piev _r) {
t = _t;
r _r; }
=

Piev rem(Object 0) {
return remFn.jorTop(t,r,o); }
Piev subst(Object n,Object 0) {
return substFn.jorTop(t,r,n, 0) ; }
}

What is the difference between rem and The first one consumes one Object, the
subst in Piev? second one consumes two.

What is the difference between rem and Simple: rem asks for the
forBot service from
subst in the Bot variant? remFn Object it
and hands over the
consumes; subst asks for the forBot service
from substFn and hands over the two Objects
it consumes.

What is the difference between rem and Simpler: rem asks for the forTop service
subst in the Top variant? from remFn and hands over the field values
and the Object it consumes; subst asks for
the for Top service from substFn and hands
over the field values and the two Objects it
consumes.

Boring Protocols 85
Copyrighted Material
And that is all there is to the methods in the 6 But remFn and substFn defined in the
variants of a protocol. datatype are still a bit mysterious.

Let's not cre ate remFn and substFn in the This looks like an obvious modification. The
datatype. new rem and subst now consume a remFn
and a substFn , respectively. C an they still
abstract class PieD { find forBot and forTop, their corresponding
abstract PieD rem(Rem v remFn, carousel partners?
Object 0);
abstract PieD sub s t ( Subst V substFn,
Object n,
Object 0);
}

Yes, it is a straightforward trade-off. Instead The definition of the datatype says that they
of adding a remFn field and a substFn field are a Rem v and a Substv , respectively. And
to the datatype, we now have rem or subst every Remv defines forBot and f o r Top , and
consume such values. W hat kind of values so does every Substv .
are consumed by rem and subst?

Here is how it changes Top. In the same manner . We just need to change
each concrete method's description of what it
class Top extends PieD { consumes. The rest remains the same.
Object t;
PieD r; class Bot extends PieD {
Top(Object _t,PieD _ r ) { PieD rem(Rem v remFn,
t = _ t; Object 0) {
r = _r; } return remFn.forBot( 0); }
PieD subst ( Subst V substFn,
PieD rem(Rem v remFn, Object n,
Object 0) { Object 0) {
return remFn . fo r Top( t , r , o ) ; } return substFn.forBot(n,o); }
PieD subst(Substv substFn, }
Object n,
Object 0) {
return substFn.forTop(t,r,n,o); }
}
How does it affect Bot?

86 Chapter 6
Copyrighted Material
0
1
That's right. Nothing else changes in the We still have some work to do.
variants. Instead of relying on fields of the
datatype, we use what is consumed.

11
Like what? Consuming an extr a value here also affects
how the met hods rem and subst are used.

12
Where are they used? In Remv and SubstV, the interesting parts ,
for example.

Yes. Here is Rem v . 13 That takes all the fun out of it.

class Remv { class SubstV {


PieD JorBot{ Object 0 ) { PieD JorBot(Object n,
return new BotO; } Object 0 ) {
PieD JorTop{Object t, return new BotO; }
PieD r, PieD JorTop(Object t,
Object 0 ) { PieD r,
if (0. equals ( t)) Object n,
return r. rem (this, 0 ); Object 0) {
else if (0. equals ( t))
, return new Top(t,r.rem(this,o)); } return
I} new Top(n,r.subst(this,n,o));
else
return
Modify SubstV accordingly.
new Top(t,r.subst(this,n,o)); }
}

1
What is this all about? 4 Yes, what about it. Copying is easy.

Understanding is more difficult. The word 15 Which object ?


this refers to the object itself.

16
How did we get here? The p rot ocol is that rem in Bot and Top asks
for the JorBot and JorTop methods of
remFn, respectively.

Boring Protocols 87
Copyrighted Material
How does that happen? 17 It happens with
re�JCn·for13ot( ... )
and
re�JCn·forTop( ... ) ,
respectively.

18
Correct. And now for13ot and forTop can Oh, so inside the methods of Rem v, this
refer to the object re�JCn as this. stands for precisely that instance of Rem v
that allowed us to use those methods in the
first place. And that must mean that when
we use r.re�(this, ... ) in forTop, it tells re�
to use the same instance over again.

That's it. Tricky? 19 Not really, just self-referential.

20
Why? Because this is a Rem v, and it is exactly
what we need to complete the job.

21
What is the value of We did the same example in the preceding
new Top ( new AnchovyO , chapter, and the result remains the same.
new Top ( new Integer(3),
new Top ( new Z e roO ,
new BotO)))
.re� ( new RemvO,
new Z e ro ()) ?

22
And how does the underlined part relate to It creates a Rem v object, which corresponds
what we did there? to the remFn in the old Piev

23
What is the value of We did the same example in the preceding
new Top ( new Integer(3), chapter, and the result remains the same.
new Top ( new Integer(2),
new Top ( new Integer(3),
new Bot())))
.subst ( new SubstVO,
new Integer(5),
new Integer(3))?

88 Chapter 6
Copyrighted Material
24
And how does the underlined part relate to It creates a SubstV obj ect, which corresponds
what we did there? to the remFn in the old PieD.

25
So what is the underlined part about? We changed the methods in PieD, which
means that we must also change how it is
used.

26
Ready for the next protocol? Let's grab a quick snack.

27
How about some ice cream? Cappuccino crunch sounds great. The more
coffee, the better.

28
Take a look at subst in Top and at forTop in Nothing really. They get handed back and
Substv. What happens to the values that forth, though forTop compares 0 to t.
they consume?

29
Is the handing back and forth necessary? We don't know any better way, yet.

30
1
Here is a way to define SubstV that avoids the Wow. This visitor has two fields.
handing back and forth of these extra values.

class Subst v {
Object n;
Object 0;
SubstV(Object _n,Object _0) {
n = _n;
0= _0; }

PieD forBotO {
return new BotO; }
PieD forTop(Object t,PieD r) {
if (0. equals ( t))
return new Top(n,r.subst(this));
else
return new Top(t,r.subst(this)); }
1 In functional programming, a visitor with fields is called a
} closure (or a higher-order function), which would be the
result of applying a curried version of 5ubst.

Boring Protocols 89
Copyrighted Material
31
How do we create a Substv? We use
new SubstV(new Integer(5),
new I nteger(3) ).

32
What does that do? It creates a Substv whose methods know how
to substitute new Integer(5) for all
occurrences of new Integer(3) in Piev

How do the methods know that without 33 The values have now become fieldsof the
consuming more values? SubstVobject to which the methods belong.
They no longer need to be consumed.

Okay, so how wou ld we substitute all 34 We write


new Integer(3) with 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 O )) )? .subst(new SubstV(
new Integer(5),
new Integer(3»).

And if we want to su sb titute all 35 We write


new Integer(2) with new Integer(7) in the new Top(new Integer(3),
same pie? new Top(new Integer(2),
new Top(new Integer(3),
new Bot())))
.subst(new SubstV(
new Integer(7),
new Integer(2»).

36
Does all that mean we have to change the Of course, because the methods subst in the
protocol, too? Bot and Top variants consume only one value
now.

90 Chapter 6
Copyrighted Material
37
That's right. Here are the datatype and its In the Top variant, we still need to hand over
Bot variant. Define the Top variant. both t and r.

abstract class PieD { class Top extends PieD {


abstract PieD rem(RemV remFn); Object t;
abstract PieD subst(SubstV substFn); PieD r;
Top( Object _t,PieD _r ) {
}
t _t;
=

r = -r'}
,
class Bot extends PieD {
PieD rem(RemV remFn) { PieD rem(Rem V remFn) {
return remFn-forBotO; } return remFn-forTop(t,r); }
PieD subst(Subst V substFn) { PieD subst(Substv substFn) {
return substFn-forBotO; } return substFn-forTop(t,r); }
}
}

Is there anything else missing? 38 We haven't defined Rem v for this new
protocoL But it is simple and hardly worth
our attention.

39
What is the difference between rem and Not much. The name of the respective values
subst in Bot? they consume and the corresponding types.

What is the difference between rem and 40 Not much. The name of the respective values
subst in Top? they consume and the corresponding types.

"
Can we eliminate the differences? It is easy to make them use the same names.
It doesn't matter whether rem is defined as

it is or as
PieD rem(RemV substFn ) {
return substFn-forTop(t,r); }.

42
True, because substFn is just a name for a Both Remv and SubstV are visitors that
value we don't know yet. But how can we contain the same method names and those
make the types the same? methods consume and produce the same
types of values. We can think of them as
extensions of a common abstract class.

Boring Protocols 91
Copyrighted Material
Yes! Do it! 43 Here it is.

abstract class PieV i sitorD {


abstract PieD JorBotO;
abstract PieD JorTop(Object t,PieD r);
}

Great job, except that we will use interface 44 Okay, that doesn't seem to be a great
for specifying visitors like these. difference. Can a class extend an interface
the way it extends an abstract class?
interface PieVisitorI {
PieD forBotO;
PieD forTop(Object t,PieD r);
}

I This superscript is a reminder that the name refers to an


interface. Lower superscripts when you enter this kind of
definition in a file: PieVisitorI.

No. A class implements an interface; it 45 Fine.


does not extend it.

46
Now that we have an interface that describes Yes, we can. Assuming we can use
the type of the values consumed by rem and interfaces like abstract classes, we can
subst, can we make their definitions even write
more similar? PieD rem ( PieV i sitorI pvFn) {
return pvFn.forTop(t,r); }
and
PieD subst(PieVisitorI pvFn) {
return pvFn.forTop( t,r); }
in Top.

Correct. What is the difference between rem 47 There isn't any. We can use the same name
and subst, now? for both, as long as we remember to use it
whenever we would have used rem or subst.

48
What is a good name for this method? The method accepts a visitor and asks for its
services, so we call it accept.

92 Chapter 6
Copyrighted Material
And what is a better name for pvFn? 49 Easy: ask, because we ask for services.

50
Now we can simplify the protocol. Here is Here we go.
the new Remv .
abstract class PieD {
class Rem v implements PieVisitorr { abstract PieD accept(PieVisitorI ask);
Object 0; }
RemV(Object 0) { _

0= _ 0; }
class Bot extends PieD {
public PieD forBotO { PieD accept(PieVisitorr ask) {
return new BotO; } return ask.forBotO; }
public PieD forTop(Object t,PieD r) { }
if ( 0. equals ( t))
return r.accept(this);
class Top extends PieD {
else
Object t;
return new Top (t,r.accept(this)); }
PieD r;
}
Top (Object _t,PieD _r) {
t t;
= _

Supply the protocol. r = _r; }

PieD accept(PieVisitorr ask) {


return ask.jorTop(t,r); }
}

Did you notice the two underlined 51 Yes, what about them?
occurrences of public?

When we define a class that impIements 52 Why?


aninterface, we need to add the word
public to the left of the method definitions.

It's a way to say that these are the methods 53 Looks weird, but let's move on.
that satisfy the obligations imposed by the
interface.

54
Correct. They are just icing. Okay, we still won't forget them.

Boring Protocols 93
Copyrighted Material
55
Now define the new Substv. Here it is.

class SubstV implements PieVisitorI {


Object n;
Object 0;
SubstV(Object _n,Object _0) {

public PieD JorBotO {


return new Bot() ; }
public PieD JorTop(Object t,PieD r) {
if (0. equals(t))
return
new Top (n,r.accept(this));
else
return
new Top (t,r.accept(this)); }
}

Draw a picture of the interface PieVisito� 56 Here is our picture.


and all the classes: PieD, Bot, Top , Remv,
and Substv.
accept
- PieV i sito�

57
Why is there is a line, not an arrow, from The SubstV visitor implements P ieVisito�,
SubstV to PieVisito�? it doesn't extend it. Arrows mean "extends,"
lines mean "implements."

And the dashed line? 58 It tells us the name of the method that
connects the datatype to the visitors.

94 Chapter 6
Copyrighted Material
What is the value of 59 Easy:
new Top (new AnchovyO, new Top(new Salmon(),
new Top(new TunaO, new Top( new Tuna O ,
new Top(new AnchovyO, new Top (new SalmonO,
new Top(new TunaO, new Top(new TunaO,
new Top(new AnchovyO, new Top(new AnchovyO,
new Bot()))))) new Bot()))))) .
. accept(new LtdSubstv (2,
new SalmonO,
new Anchovy()))?

60
Explain what LtdSubstv produces.1 The methods of LtdSubstV replace one fish on
a pie by another as many times as specified
1 A better name is LimitedSubstitutionV, and that is how by the first value consumed by LtdSubstV
we pronounce it.

61
Good. Define LtdSubstv That's easy. We have such a flexible protocol
that we only need to define the essence now.

class LtdSubstV implements PieVisitorL {


int Cj
Object nj
Object OJ
LtdSubstv (int _c,Object _n,Object _0) {
C = _Cj
n = _nj
0= _OJ}

public PieD jorBotO {


return new Bot O j }
public PieD jorTop(Object t,PieD r) {
if(c == 0)
return new To p(t , r ) j
else
if (o.equals(t))
return
new Top(n,r.accept(this))j
else
return
new Top(t,r.accept(this))j }
}

Boring Protocols 95
Copyrighted Material
62
W hat is the value of Oops, there are too few anchovies on this
new Top(new AnchovyO, pizza pie:
new Top(new TunaO, new Top(new SalmonO,
new Top(new AnchovyO, new Top(new Tun a O ,
new Top(new TunaO, new Top(new SalmonO,
new Top(new AnchovyO, new Top(new Tu naO ,

new Bot()))))) new Top(new SalmonO,


.accept (new LtdSubstV(2, new Bot()))))).
new SalmonO,
new Anchovy()))?

How come? 63 Because c, the counting field, never changes.

64
Why doesn't c ever change? Because this, the LtdSubstV that performs
the substitutions, never changes.

65
Can we fix this? We can't change this, but we can replace
this with a new LtdSubstv that reflects the
change.

66
If c stands for the current count, how do we Simple, we use
create a LtdSubstV that shows that we have new LtdSubstV(c - 1,n,0)
just substituted one fish by another.
in place of this.

The Sixth Bit of Advice


When the additional consumed values
change for a self-referential use of a
visitor, don't forget to create a new
visitor.

96 Chapter 6
Copyrighted Material
Define the new and improved version of 67 Voila.
LtdSubstv.
class LtdSubstV implements PieVisitorI {
int C;
Object n;
Object 0;
LtdSubst v (int _c,Object _n,Object _0) {
c _c;
=

n = _n;
o=-Dj }

public PieD fo rE otO {


return new BotO; }
public PieD forTop(Object t,PieD r) {
if (c == 0)
return new Top(t,r);
else
if ( 0. equals (t) )
return
new Top(n,
r.accept(
new LtdSubstV(c l,n,o)));
-

else
return
new Top(t,
r.accept(
this)); }
}

68
How does They are two different LtdSubstVs. One
this replaces c occurrences of ° by n in a pizza
pie, and the other one replaces only c 1 of
differ from
-

them.
new LtdSubstV(c -l,n,o)?

How do you feel about protocols now? 69 They are exciting. Let's do more.

Boring Protocols 97
Copyrighted Material
aterial
Copyrighted M
Is 1 Yes.
new Flat(new AppleO,
new Flat(new PeachO,
new Bud()))
a flat TreeD?

2
Is Yes, it is also a flat TreeD.
new Flat(new Pea rO ,

new Bud())
a flat TreeD?

And how about 3 No, it is split, so it can't be flat.


new Split(
new BudO,
new Flat(new FigO,
new Split(
new BudO,
new Bud())))?

Here is one more example: 4 No, it isn't flat either.


new Split(
new Split(
new BudO,
new Flat(new LemonO,
new Bud())),
new Flat(new FigO,
new Split(
new BudO,
new Bud()))).
I s it flat?

Is the difference between flat trees and split Unless there is anything else to TreeD, it's
trees obvious now? totally clear.

Good. Then let's move on. 6 Okay, let's.

OhMy! 99
Copyrighted Material
Here are some fruits. It does not differ too much from what we
have seen before.
I abstract class FruitV {}
I abstract class Treev {}
class Peach extends FruitV {
public boolean equals(Object 0) { I class Bud extends Treev {}
return ( 0 instanceof Peach); }
}
class Flat extends Tr eev {
FruitV I;
class Apple extends FruitV { Treev t;
public boolean equals(Object 0 ) { Flat(FruitV _1,Treev -t) {
return ( 0 instanceof Apple); } I = -I;
t = -t·}
,
}

}
class Pear extends FruitV {
public boolean equals(Object 0 ) {
return ( 0 instanceof Pear); } class Split extends Treev {
} Treev I;
Treev 1';
Split(TreeV _1,Treev _1') {
class Lemon extends FruitV { I = _I;
public boolean equals(Object 0 ) { l' = _1'; }
return (0 instanceof Lemon); }
} }

class Fig extends FruitV {


public boolean equals(Object 0) {
return (0 instanceof Fig) ; }
}

Let's say all Treevs are either fiat, split, or


bud. Formulate a rigorous description for
Treevs.

Did you notice that we have redefined the That probably means that we will need to
method equals in the variants of Fruitv? compare fruits and other things.

Do Treev,s variants contain equals? No, which means we won't compare them,
but we could.

100 Chapter 7
Copyrighted Material
10
How does the datatype TreeD differ from all The name of the new datatype occurs twice
the other datatypes we have seen before? in its Split variant.

11
Let's add a visitor interface whose methods That just means extending what we have
produce booleans. with one method each.

interface bTreeVisitorI { class Bud extends TreeD {


boolean lo1'BudO; boolean accept(bTreeVisitorI ask) {
boolean lo1'Flat(FruitD I,TreeD t); return ask.fo1'BudO; }
boolean lo1'Split(TreeD l,TreeD 1'); }
}
class Flat extends TreeD {
Here is the new datatype definition. FruitD I;
TreeD t;
abstract class TreeD { Flat(FruitD _1,TreeD _t) {
abstract I = -I;
boolean accept(bTreeVisitorI ask); t = _t; }
}
boolean accept(bTreeVisitorI ask) {
Revise the variants. return ask.fo1'Flat(f , t); }
}

class Split extends TreeD {


TreeD I;
TreeD 1';
Split(TreeD _l,TreeD _1') {
Il' _I;
=

_1'; }
=

boolean accept(bTreeVisitorI ask) {


return ask.fo1'Split(I,1'); }
}

But isn't bTreeVisitorI a pretty unusual


name?

12
Yes, it is. Hang in there, we need unusual Okay.
names for unusual interfaces. Here b reminds
us that the visitor's methods produce
booleans.

OhMy! 101
Copyrighted Material
13
How many methods does the definition of Three, because it works with Tree'Ds, and the
blsFlatV contain, assuming it implements datatype definition for Tree'Ds has three
bTreeVisito,.z? variants.

What type of values do the methods of 14 booleans.


blsFlatV produce?

15
What visitor does blsFlatv remind us of? OnlyOnionsv

16
Here is a skeleton for blsFlatV That's easy now.

class blsFlatv implements b TreeV i sito,.z { class blsFlatV implements bTreeVisito,.z {


public public
boolean jorBudO { boolean jorBudO {
return ; } return true; }
public public
boolean lorFlat(Fruit'D I.Tree'D t) { boolean lorFlat(FruitD I.TreeD t) {
return i} return t.accept(this); }
public public
boolean jorSplit(Tree'D l,TreeD r) { boolean jorSplit(TreeD l,TreeD r) {
return ; } return false; }
} }
Fill in the blanks.

17
Define the blsSplitV visitor, whose methods Here is the easy part.
check whether a Tree'D is constructed with
Split and Bud only. class b lsSp li t V implements bTreeVisito,.z {
public
boolean jorBudO {
return true; }
public
boolean lorFlat(FruitD I,TreeD t) {
return false; }
public
boolean jorSplit(TreeD l,TreeD r) {
--- }
}

102 Chapter 7
Copyrighted Material
18
What is difficult about the last line? We need to check whether both 1 and rare
split trees.

19
Isn't that easy? Yes, we just use the methods of blsSplitV on I
and r.

And then? 20 Then we need to know that both are true.

21
If Yes, because if both are true, we have a split
l.accept(this) tree.
is true, do we need to know whether
r. accept(this)

is true?

22
If No, then the answer is false.
I. accept(this)
is false, do we need to know whether
r. accept (this)

is true?

2
Finish the definition of blsSplitV using 3 Now we can do it.
if ( . )
. .

class blsSplitV implements bTreeVisitorI {


return ...
public
else
boolean forBudO {
return ...
return true; }
public
boolean forFlat(FruitD f.TreeD t) {
return false; }
public
boolean forSplit(TreeD I.TreeD r) {
ifl (l.accept(this))
return r.accept(this);
else
return false; }
}

We could have written the if . .. as


return 1. accept (this) && r. accept (this).

OhMy! Copyrighted Material 103


Give an example of a TreeD for which the 24 There is a trivial one:
methods of bls S pl itV respond with true. new BudO.

How about one with five uses of Split? 25 H ere


is one:
new Split(
new Split(
new BudO,
new Split(
new BudO,
new Bud())),
new Split(
new BudO,
new Split(
new BudO,
new Bud()))).

26
Does this TreeD have any fruit? No.

Define the bHasFruitV visitor. 27 Here it is.

class bHasFruitV
implements bTreeVisitorr {
public
boolean Jo1'BudO {
return false; }
public
boolean Jo1'Flat(FruitD J,TreeD t) {
return true; }
public
boolean Jo1'Split(TreeD I,TreeD 1') {
if! (l.accept(this))
return true;
else
return 1'.accept(this); }
}

We could have written the if. as


1. accept (this)
.

return II r.accept(this).

104 Chapter 7
Copyrighted Material
28 3.
What is the height of
new Split(
new Split(
new BudO,
new Flat(new LemonO,
new BudO)),
new Flat(new FigO ,

new Split(
new BudO,
new B u d())))?

29
What is the height of 2.
new Split(
new BudO,
new Flat(new LemonO,
new Bud()))?

What is the height of 30 1.


new Flat(new LemonO,
new B u d())?

What is the height of 31 o.

new BudO?

32
So what is the height of a TreeP? Just as in nature, the height of a tree is the
distance from the beginning to the highest
bud in the tree.

33
Do the methods of iHeightV work on a TreeP? Yes, and they produce an into

34
Is that what the i in front of Height is all It looks like i stands for int, doesn't it?
about?

OhMy! Copyrighted Material 105


What is the value of 35 4.
new Split(
new Split(
new BudO,
new Bud()),
new Flat(new Fi gO ,

new Flat(new LemonO,


new Flat(new AppleO,
new Bud()))))
.accept(new iHeightV())?

36
Why is the height 4? Because the value of
new Split(
new BudO,
new Bud())
.accept(new iHeightV())
is 1; the value of
new Flat(new F i gO,

new Flat(new LemonO,


new Flat(new AppleO,
new Bud())))
.accept(new iHeightV())
is 3; and the larger of the two numbers is 3.

37
And how do we get from 3 to 4? We need to add one to the larger of the
numbers so that we don't forget that the
original Treev was constructed with Split and
those two Treevs.

u picks the larger of two numbers, x and y. 1 38 Oh, that's nice. What kind of methods does
iHeight v define?
1 When you enter this in a file, use
Math. max (x, y) .
Math is a class that contains max as a (static) method.

39
i He i ghtV , s
methods measure the heights of Now that's a problem.
the Treevs to which they correspond.

106 Chapter 7
Copyrighted Material
40
Why? We defined only interfaces that produce
booleans in this chapter.

'
4
So what? The methods of iHeightV produce ints, which
are not booleans.

Okay, so let's define a visitor interface that 42 It's almost the same as bTreeVisjto�
produces ints.
interface iTreeVisitorI {
int jorBudO;
int jorFlat(FruitD j, TreeD t);
int jorSplit(TreeD l,TreeD r);
}

Yes, and once we have that we can add 43 Does that mean we can have two methods
another accept method to TreeD with the same name in one class?l

abstract class TreeD {


abstract
boolean accept(bTreeVisitorI ask);
abstract
int accept(iTreeVisitorI ask);
} 1 In Java, defining mUltiple methods with the same name
and different input types is called lIoverloading."

We can have two methods with the same 44 bTreeVisitorI is indeed different from
name in the same class as long as the types iTreeVisitorI, so we can have two versions of
of the things they consume are distinct. accept in TreeD.

4
Add the new accept methods to TreeD's 5 It is easy.
variants. Start with the easy one.
class Bud extends TreeD {
boolean accept(bTreeVisitorI ask) {
return ask.forBudO; }
int accept(iTreeVisitorI ask) {
return ask.forBudO; }
}

OhMy! Copyrighted Material 107


46
The others are easy, too. We duplicate We must also change the type of what the
accept. new accept method consumes and produces.

class Flat extends TreeD { class Split extends TreeD {


FruitD I; TreeD l;
TreeD t; TreeD r;
Flat(FruitD -I ,TreeD _i) { Split(TreeD _l,TreeD _r) {
f -/j
= 1= _Ij
t _tj }
= r = _rj }

boolean accept(b TreeVisitorI ask) { boolean accept(bTreeVisitorI ask) {


return ask.lorFlat(f ,i); } return ask.lorSplit(l,r)j }
int accept(iTreeVisitorI ask) { int accept(iTreeVisitorI ask) {
return ask.lorFlai(f,t)j } return ask.forSplit(l,r)j }
} }

47
Here is iHeightV That's easy now.

class iHeightV implements iTreeVisitorI { class iHeightV implements iTreeVisitorI {


I
. public int lorBudO { public int lorBudO {
return ; }
return 0; }
public int lorFlat(FruitD I,TreeD t) { public int lorFlat(FruitD j,TreeD t) {
return ; } return t.accept(this) + 1; }
public int lorSplit(TreeD I,TreeD r) { public int jorSplit(TreeD I,TreeD r) {
return ; }
return
} (l.accept(this) U r.accept(this))
+ 1; }

Complete these methods. }

4S
What is the value of 1, of course.
new Split(
new BudO,
new Bud())
.accept(new iHeightV())?

And why is it I? 49 Because


new Bud O. accept (new i Height v () )
is 0, the larger of 0 and 0 is 0, and one more
is 1.

108 Chapter 7
Copyrighted Material
50
What is the value of If the visitor tSubstV substitutes apples for
new Split( figs, here is what we get:
new Split( new Split(
new Flat(new Fig(), new Split(
new Bud()), new Flat(new Apple(),
new Flat(new Fig(), new Bud()),
new Bud())), new Flat(new Apple(),
new Flat(new Fig(), new Bud())),
new Flat(new Lemon(), new Flat(new Apple(),
new Flat(new Apple(), new Flat(new Lemon(),
new Bud())))) new Flat(new Apple(),
. accept ( new Bud())))).
new tSubstV (
new Apple(),
new Fig()))?

Correct. Define the tSubstV visitor. 51 It's like SubstFishV and SubstlntV from the
end of chapter 5, but we can't do it just yet.

52
What's the problem? Its methods produce TreeDs, neither ints nor
booleans, which means that we need to add
yet another interface.

interface tTreeVisitorI {
TreeD JorBudO;
TreeD JorFiat(FruitD J,TreeD t);
TreeD JorSpiit(TreeD i,TreeD r);
}

53
Good job. How about the datatype TreeD Easy. Here is the abstract one.

abstract class TreeD {


abstract
boolean accept(bTreeVisitorI ask);
abstract
int accept(iTreeVisitorI ask);
abstract
TreeD accept(tTreeVisitorI ask);
}

OhMy! Copyrighted Material 109


54
Define the variants of TreeD. No problem.

class Bud extends TreeD {


boolean accept (bTreeVisito,.x ask) {
return ask.forBudOj }
int accept(iTreeVisito,.x ask) {
return ask.forBudOj }
TreeD accept(tTreeVisito,.x ask) {
return ask.forBudOj }
}

class Flat extends TreeD {


FruitD f;
TreeD t;
Flat(FruitD _f,TreeD _t) {
f -fj
=

t = _tj }

boolean accept(bTreeVisito,.x ask) {


return ask.forFlat(J,t)j }
int accept(iTreeVisito,.x ask) {
return ask.forFlat(J,t)j }
TreeD accept(tTreeVisito,.x ask) {
return ask.forFlat(J,t)j }
}

class Split extends TreeD {


TreeD I;
TreeD r;
Split(TreeD _I,TreeD _r) {
1= _lj
r = _rj }

boolean accept (bTreeVisito,.x ask) {


return ask.forSplit(l,r)j }
int accept(iTreeVisito,.x ask) {
return ask.forSplit(l,r)j }
TreeD accept(tTreeVisito,.x ask) {
return ask.forSplit(l,r)j }
}

110 Chapter 7
Copyrighted Material
55
Then define tSubst v That's easy, too. It has two fields, one for
the new FruitD and one for the old one, and
the rest is straightforward.

class tSubst v implements tTreeVisito,x {


FruitD n;
FruitV 0;
tSubstV(FruitD _n,FruitD _0) {
n =_n;
0 = _0; }

public TreeD jorBudO {


return new BudO; }
public TreeD jorFlat(FruitD j,TreeD t) {
if ( 0. equals(t))
return new Flat(n,t.accept(this));
else
return new Flat(t,t.accept(this)); }
public TreeD jorSplit(TreeD I,TreeD r ) {
return new Split(l.accept(this),
r. accept(this)); }

56
Here is a TreeD that has thr ee Figs: Even the visitors are no longer interesting.

new Split(
new Split( class iOccursv implements iTreeVisito,x {
new Flat(new FigO, FruitD a;
new Bud()), iOccursV(FruitD _a) {
new Flat(new FigO, a = _a; }
new Bud())),
new Flat(new FigO, public int jorBudO {
new Flat(new LemonO, return 0; }
new Flat(new AppleO, public int jorFlat(FruitD j,TreeD t) {
new Bud())))). if (t. equals(a ))
return t.accept(this) + 1;
Now define iOccursv, whose methods co unt else
how often some FruitD occurs in a tree. return t.accept(this); }
public int jorSplit(TreeD I,TreeD r ) {
return
l.accept(this) + r.accept(this); }
}

OhMy! 111
Copyrighted Material
57
Do you like your fruit with yogurt? We prefer coconut sorbet.

58
Is it disturbing that we have three nearly Copying definitions is always bad. If we
identical versions of accept in TreeDs and its make a mistake and copy a definition, we
variants? copy mistakes. If we modify one, it's likely
that we might forget to modify the other.

Can we avoid it? 59 If boolean and int were classes, we could


use Object for boolean, int, and TreeD.
Unfortunately, they are not.

60
Remember Integer and Boolean? They make Yes, Boolean is the class that corresponds to
it possible. boolean, and Integer corresponds to into

61
Here is the interface for a protocol that Here they are.
produces Object in place of boolean, int,
and TreeD. class Flat extends TreeD {
FruitD f;
interface TreeVisito� { TreeD t;
Object forBudOj Flat(FruitD _f,TreeD _t) {
Object forFlat(FruitD f,TreeD t); f = -fj
Object forSplit(TreeD l,TreeD r); t _tj }
=

}
Object accept(TreeVisito� ask) {
Here is the datatype and the Bud variant. return ask.forFlat(f,t)j }
}
abstract class TreeD {
abstract
class Split extends TreeD {
Object accept(TreeVisito� ask);
TreeD l;
} TreeD r;
Split(TreeD _l,TreeD _r) {
class Bud extends TreeD { l = _lj
Object accept(TreeVisito� ask) { r _rj }
=

return ask.forBudOj }
} Object accept(TreeVisito� ask) {
return ask.forSplit(l, r)j }
Define the remaining variants of TreeD. }

112 Chapter 7
Copyrighted Material
62
Good. Now define IsFlatV, an Object That's no big deal.
producing version of blsFlatv.
class IsFlatV implements TreeVisitorr {
public Object forBudO {
return new Boolean(true); }
public Object forFlat(FruitV f ,Treev t) {
return t.accept(this); }
public Object forSplit(TreeV l,Treev r ) {
return new Boolean(false); }
}

63
And how about IsSplitv? Now that's different. Here we need a way to
determine the underlying boolean of the
Boolean that is produced by l.accept(this) in
the original definition.

64
Okay, here it is. Oh, because l.accept(this) produces an
Object, we must first convert 1 it to a Boolean.
class IsSplitV implements TreeVisitorr { Then we can determine the underlying
public Object forBudO { boolean with the boolean Value method. We
return new Boolean(true); } have seen this in chapter 5 when we
public Object forFlat(FruitV f,Treev t) { converted an Object to a OneMoreThan.
return new Boolean(false); }
public Object for Split(TreeV l,Treev r ) {
if (( (Boolean) (l. accept(this)))
. boolean Value())
return r. accept(this);
else 1 If Java had parametric polymorphism for methods, no

return new Boolean(false); } downward cast would be necessary for our visitors (Martin
Odersky and Philip Wadler, Pizza into Java: Translating
} Theory into Practice, Conference Record on Principles of
Programmmg Languages, 146-159. Paris, 1997).

65
Will the conversion always work? Yes, because the Object produced by
l. accept ( this) is always a Boolean.

The Seventh Bit of Advice


When designing visitor protocols for
many different types, create a unifying
protocol using Object.

OhMy! 113
Copyrighted Material
66
Did you think that was bad? Then study Oh my.,
this definition during your next break.

class Occursv implements TreeVisitoiI {


Fruit'D a;
OccursV (Fruit'D _a) {
a = _a; }

public Object jorBudO {


return new Integer(O); }
public Object jorFlat(Fruit'D j,Tree'D t) {
if (I. equals ( a))
return
new Integer(((lnteger)
(t. accept(this)))
.intValueO
+ 1);
else
return t.accept(this); }
public Object jor Split(Tree'D l,Tree'D r) {
return
new Integer(((lnteger)
(l. accept(this)))
.intValueO
+
((Integer)
(r.accept(this)))
.intValue())j }
}

114 Chapter 7
Copyrighted Material
Copyrighted Material
What is the value of 1 12.

(7 + ((4- 3) x5))?

What is the value of 12,

(+ 7 (x (- 4 3 ) 5))? because we have just rewritten the


previous expression with prefix operators.

What is the value of new Integer(12),


new Plus( because we have just rewritten the
new Const(new Integer(7)), previous expression using I nteger and
new Prod( constructors.
new Diff(
new Const(new Integer(4)),
new Const(new Integer(3 ))),
new Const(new Integer(5))))?

Where do the constructors come from? A datatype and its variants that represent
arithmetic expressions.

5
Did you like that? So far, so good.

6
What is the value of {7,5}.
({7,5} U (({4} \ {3}) n {5}))?

What is the value of {7,5},


(u {7,5} (n (\ {4} {3}) {5} ))? we just went from infix to prefix notation.

What is the value of {7,5},


(+ {7,5} ( x (- {4} {3}) {5}))? we just renamed the operators.

Like Father, Like Son 117


Copyrighted Material
What is the value of new EmptyO
new P lu s( .add(new Integer(7))
new Const(new EmptyO .add(new Integer(5)),
.add(new Integer(7)) because we have just rewritten the
.add(new Integer(5))), previous expression using the constructors.
new Pr od(
new Diff(
new Const(new EmptyO
.add(new Integer(4))),
new Const(new EmptyO
.add(new Integer(3)))),
new Const(new EmptyO
. add (new Integer(5)))))?

10
Where do the constructors come from? A datatype and its variants that represent
set expressions.

11
Do you still like it? Sure, why not.

12
Does the arithmetic expression look like the Yes, they look the same except for the
set expression? constants:
new Plus(
new Const(e),
new Prod(
new Diff(
new Const(e),
new Const(e)),
new Const(e))).

13
Let's say that an expression is either That's a tricky question.

a Plus(exprl,expr2),
a Diff(exprl, expr2)' interface Exp rVi si t o,x {
a Prod(exprl,expr2), or Object jorPlus(ExprD l,ExprD r);
a constant, Object jorDijJ(ExprD l,ExprD r);
Object jorProd(ExprD l,ExprD r);
where exprl and expr2 stand for arbitrary
Object jorConst(Object c);
expressions. What should be the visitor
interface?
}

118 Chapter 8
Copyrighted Material
14
Good answer. Here is the datatype now.
class Plus extends ExprD {
abstract class ExprD { ExprD l;
abstract ExprD r;
Object accept(ExprVisito.x ask); Plus(ExprD _1,ExprD _r ) {
} I _Ii
=

r =_r; }
Define the variants of the datatype and equip
them with an accept met hod that produces Object accept(ExprVisito.x ask) {
Objects. return ask.forPlus(I,r); }
}

class Diff extends ExprD {


ExprD I;
ExprD r;
Diff(ExprD _1,ExprD _r) {
I = _Ii
r = -ri }

Object accept(ExprVisito.x ask) {


return ask.forDiff(I,r)j }
}

class Prod extends ExprD {


ExprD I;
ExprD r;
Prod(ExprD _1,ExprD _r) {
I _Ij
=

r = -r', }

Object accept(ExprVisito.x ask) {


return ask.forProd(I,r); }
}

class Const extends ExprD {


Object C;
Const(Object _c) {
c = _Cj }

Object accept(ExprVisito.x ask) {


return ask.forConst( c ) j }
}

Like Father, Like Son 119


Copyrighted Material
15
Can we now define a visitor whose methods Yes, we can. It must have four methods, one
determine the value of an arithmetic per variant, and it is like Occursv from the
expression? previous chapter.

16
How do we add We have done this before. We use the

new Integer(3) method intValue to determine the ints that


and correspond to the Integers, and then add
new Integer(2)? them together.

17
But what is the result of An int, what else?
new Integer(3).intValueO
+
new Integer(2).intValueO?

18
How do we turn that into an Integer? We use new Integer(... ) .

19
Okay, so here is a skeleton of IntEvalv That's an interesting skeleton. It contains
five different kinds of blanks and two of them
class IntEvalv implements ExprVisito,x { occur three times each. But we can see the
public Object forPlus(ExprV l,Exprv r) { bones only. Where is the beef?
return plus(l.accept(this),
r. accept (this)); }
public Object forDiff(ExprV l,Exprv r) {
return diff(l.accept(this),
r.accept(this)); }
public Object forProd(ExprV l,Exprv r ) {
return prod(l.accept(this),
r.accept(this)); }
public Object forConst(Object c) {
return c; }
Object plUS(_l l,-2 r ) {
return -3;}
Object diff(-l l'-2 r) {
return ---4; }
Object Prod(_l l,-2 r ) {
return -5;}
}

120 Chapter 8
Copyrighted Material
20
How does !oTPlus work? It consumes two ExprDs, determines their
respective values, and pl us es them.

21
How are the values represented? As Objects, because we are using our most
general kind of (and most recent) visitor.

22
So what kind of values must plus consume? Objects,
because that's what

l.accept(this)
and

T.accept(this)
produce.

What must we put in the first and second 23 Object.


blanks?

Can we add Objects? 24 No, we must convert them to Integers first


and extract their underlying ints.

25
Can we convert all Objects to Integers? No, but all Objects produced by IntEvalV are
made with new Integer(.. . ) so that this
,

conversion always succeeds.

26
Is that true? What is the value of Wow. At some level, this is nonsense.
new Plus(
new Const(n ew Empty()),
new Const(new Integer(5)))
.accept(new IntEvalV ()) ?

27
Correct, so sometimes the conversion may What should we do?
fail, because we use an instance of IntEvalV
on nonsensical arithmetic expressions.

Like Father, Like Son Copyrighted Material 121


28
We agree to avoid such arithmetic And their set expressions, too.
expressions. 1

1 In other words, we have unsafe evaluators for our


expressions. One way to make them safe is to add a method
that checks whether constants are instances of the proper
class and that raises an exception [ l:chapter 7). An
alternative is to define a visitor that type checks the
arithmetic expressions we wish to evaluate.

29
If we want to add l and r, we write Now it's easy. Here we go.
new Integer(
((Integer)l).int Value 0 class IntEvalV implements ExprVisitorr {
+ public Object forPlus(ExprV l,Exprv r) {
((Integer)r). int Value 0 ). return plus(l. accept(this),
r.accept(this)); }
Complete the definition now. public Object forDiff(ExprV l,Exprv r) {
return diff (l. accept(this) "
r.accept(this)); }
public Object forProd(ExprV l,Exprv r) {
return prod(l.accept(this),
r. accept (this)) ; }
public Object forConst(Object c ) {
return c; }
Object plus(Object l,Object r) {
return
new Integer(
((Integer)l). intValue 0
+
(( Integer) r ) . intValue ()); }
Object diff(Object l,Object r ) {
return
new Integer(
((Integer)l).intValue 0

((lnteger)r).intValue()); }
Object prod(Object l,Object r ) {
return
new Integer(
((Integer)l).int Value 0
*

((lnteger)r).intValue()); }
}

122 Chapter 8
Copyrighted Material
That one was pretty easy, wasn't it? 30 Yes. Let's implement an ExprVisitorX for sets.

31
plusing,

What do we need to Implement one for sets? We certainly need methods for
d iff ing, and proding sets.

That's correct, and here is everything. 32 Whoa.

abstract class Set:D {


Set:D add(lnteger i) {
if (mem(i»
return this;
else
return new Add(i,this); }
abstract boolean mem(lnteger i);
abstract Set:D plus(Set:D s);
abstract Set:D diff ( Set:D s);
abstract Set:D prod(Set:D s);
}

Explain the method in the nested box in 33 We use our words:


your own words. "As its name says, add adds an element to
a set. If the element is a member of the
set, the set remains the same; otherwise, a
new set is constructed with Add."

3.
Why is this so tricky? Constructors always construct, and add does
not always construct.

3'
Do we need to understand that? Not now, but feel free to absorb it when you
have the time.

Like Father, Like Son 123


Copyrighted Material
36
Define the variants Empty and Add for SetD Here we go.
�------�

class Empty extends SetD {


boolean mem(lnteger i) {
return false; }
SetD plus ( Set'D s) {
return s; }
SetD diff(SetD s) {
return new EmptyOj }
SetD prod(SetD s) {
return new EmptyOj }
}

class Add extends SetD {


Integer i;
SetD s;
Add(lnteger _i,SetD _s) {
i = _i;
S _Sj }
=

boolean mem(lnteger n) {
if (i . equals( n ) )
return true;
else
return s.mem(n)j }
SetD plus(Set'D t) {
return s.plus(t.add(i)); }
SetD diff(SetD t) {
if (t.mem(i))
return s.diff(t)j
else
return s.diff( t}.add(i)j }
SetD prod(SetD t} {
if (t.mem(i))
return s.prod(t).add(i);
else
return s.prod(t}; }
}

124 Chapter 8
Copyrighted Material
Do we need to understand these definitions? 37 Not now, but feel free to think about them
when you have the time. We haven't even
used visitors to define operations for union,
set-difference, and intersection, but we trust
you can.

38
What do we have to change in IntEvalV to Not much, just plus, dijJ, and prod.
V
obtain Set E v al , an evaluator for set
expressions?

39
How should we do that? Oh, that's a piece of pie. We just copy the
definition of IntEvalV and replace its plus,
dijJ, and prod methods.

That's the worst way of doing that. 40 What?

41
Why should we throw away more than half That's true. If we copied the definition and
of what we have? changed it, we would have identical copies of
JorPlus, JorDijJ, JorProd, and JorConst. We
should reuse this definition. 1

1 Sometimes we do not have license to see the definitions1 so


copying might not even be an option.

42
Yes, and we are about to show you better That part is easy:
ways. How do we have to change plus, dijJ, Object plus(Object I Obj ect r) {
,

and prod? return ((SetD)l).plus((SetD)r); }


and
Obj ect dijJ(Object l Obj ect r) {
,

return ((SetD)I). dijJ((SetD)r); }


and
Object prod(Object l,Object r) {
return ((SetD)I).prod((SetD)r); }.

Like Father, Like Son 125


Copyrighted Material
43
Very good, and if we define SetEvalV as an Now that's much easier than copying and
extension of IntEvalV, that ' s all we have to modi fy in g .
put inside of SetEvalV

class SetEvalV extends IntEvalV {


Object plus(Object l Obj ect r) {
,

return ((SetV)l).plus((SetD)r); }
Object diff(Object I,Object r) {
return ((SetV)I).diff{{SetV)r); }
Object prod(Object I,Object r) {
return ((SetV)l).prod{{SetV)r); }
}

44
Is it like equals? Yes, when we include equals in our class
definitions, we override the one in Object.
Here, we override the methods plus, diff, and
prod as we extend IntEvalV

How many methods from IntEvalV are 45 Three.


overridden in SetEvalv?

46
How many methods from IntEvalv are not Four: forPlus, forDiff, forProd, and
overridden in SetEvalv? forConst.

47
Does SetEvalV implement ExprVisito,x? It doesn't say so.

48
Does SetEvalv extend IntEvalv? It says so.

49
Does IntEvalv implement ExprVisito,x? It says so.

50
Does SetEvalv implement ExprVisito,x? By implication.

126 Chapter 8
Copyrighted Material
51
That's correct. What is the value of Interesting question. How does this work
new Prod( now?
new Const(new EmptyO
.add(new Integer(7»),
new Const(new EmptyO
.add(new I ntege r(3» »
.accept(new SetEvaIV())?

52
What type of value is It is a Prod and therefore an E xprD
new Prod(
new Const(new Empty()
. add (new I ntege r ( 7 » ) ,
new Const(new Empty()
. add (new Int ege r (3 »» ) ?

3
5
And what does accept consume? An instance of SetEvalV, but its type is
ExprVisito�

What is 54 That's what we need to determine the value


new SetEvaIV().forProd( of next, because it is
new Const(new Empty() ask.forProd{ l,r),
.add(new Integer(7»),
with ask, l, and r replaced by what they
new Const(new Empty()
stand for .
. add(new Integer(3»))?

5
Where is the definition of SetEvalV's method 5 It is in IntEvalV
forProd?

Suppose we had the values of 56 If their values were A and B, we would have
new Const(new EmptyO to determine the value of
. add(new I nteger(7») prod{A,B) .
. accept (this)
and
new Const(new EmptyO
.add(new Integer(3»)
. accept (this).
What would we have to evalu ate next?

Like Father, Like Son 127


Copyrighted Material
Isn't that strange? 57 Why?

So far, we have always used a method on a 58 That ' s true. What is the object with which
particular object. we u se prod(A,B)?

59
It is this object. Oh, does that mean we should evaluate

new SetEvaIVO.prod(A,B)?

Absolutely. If the use of a method omits the 60 That clarifies things.


object, we take the one that we were working
with before.

61
Good. And now what? Now we still need to determine the values of
new Const(new EmptyO
.add(new Integer(7)))
. accept (this)
and
new Const(new Empty()
.add(new Integer(3)))
.accept(this).

62
The values are obviously It, too, is in IntEvalv.
new EmptyO
add (new Integer(7))
.

and
new EmptyO
.add(new Integer(3)).
Where is the definition of forConst that
determines these values?

63
Here is the next expression in our sequence: The object is an instance of SetEvalv, which
new SetEvalvO overrides the prod method in IntEvalv with
.prod(new EmptyO its own .
. add(new Integer(7)),
new EmptyO
.add(new Integer(3))).
Where does prod come from?

128 Chapter 8
Copyrighted Material
64
What next? Next we need to determine the value of
((SetV)(new EmptyO
. add(new Integer(7))))
. prod((SetV)new EmptyO
.add(new Integer(3))),
because it is
(( Se tV) ( I. accept( this)))
. prod ((S etV) r. accept ( this))
with l. accept(this) and r.accept(this)
replaced by their respective values.

65
Is Of course it is, but the type of l.accept(this),
new EmptyO.add(new Integer(7)) which is where it comes from, is Object.
an instance of Setv?

66
And how about It's the same.
new EmptyO.add(new Integer(3))?

7
And that is why the method must contain a 6 This example makes the need for conversions
conversion from Object to SetVs. obvious again.

68
Time for the last question. Where does this This one belongs to SetV or more precisely
prod come from now? its Empty and Add variants.

And what does prod do? 69 It determines the intersection of one SetV
with another S etV, but didn't we agree that
the previous question was the last question
on that topic?

70
We overrode that, too. Thanks, guys.

Is it natural that SetEvalV extends IntEvalV? 71 No, not at all.

Like Father, Like Son Copyrighted Material 129


72
Why did we do that? Because we defined IntEvalV first.l

1 Sometimes we may need to extend classes that are used in


several different programs. Unless we wish to maintain
multiple copies of the same class, we should extend it.-Java
is object-oriented, so it may also be the case that we acquire
the object code of a class and its interface, but not its source
text. If we wish to enrich the functionality of this kind of
class, we must also extend it.

73
But j ust because something works, it doesn't Yes, let's do better. We have defined all
mean it's rational. these classes ourselves, so we are free to
rearrange them any way we want.

7'
What distinguishes IntEvalV from SetEvalV? The methods plus, diff, and prod.

7S
What are the pieces that they have in They share the methods forPlus, forDiff,
common? jorProd, and jorConst.

78
Good. Here is how we express that. Isn't this abstract class like Pointv?

abstract class Evalv


implements ExprVisito..x {
public Object jorPlus(ExprV I,Exprv r ) {
return plus(l.accept(this),
r.accept(this)); }
public Object jorDiff(ExprV l,Exprv r) {
return diff(l. accept (this),
r.accept(this)); }
public Object jorProd(ExprV I,Exprv r) {
return prod (I. accept(this),
r.accept(this)); }
public Object jorConst(Object c) {
return C; }
abstract
Object plus(Object l,Object r);
abstract
Object diff(Object I,Object r};
abstract
Object prod(Object I,Object r);
}

130 Chapter 8
Copyrighted Material
77
Yes, we can think of it as a datatype for What do we do now?
EvarD visitors that collects all the common
elements as concrete methods. The pieces
that differ from one variant to another are
specified as abstract methods.

78
We define IntEvalV extending Eva I!> . Itis basically like the original but extends
Eval!>, not IntEvalv.
class IntEvalV extends Eval"V {
Object plus(Object I,Object r) { class SetEvalv extends Eval"V {
return Object plus(Object I,Object r) {
new Integer( return « SetD)l).plus«SetD)r); }
((Integer) l). intValue 0 Object diff(Object I,Object r) {
+ return «SetD)l).diff« SetD)r); }
«Integer)r). intValue()); } Object prod(Object l,Object r) {
Object diff(Object I,Object r ) { return « SetD)l).prod( SetD)r); }
return }
new Integer(
«Integer)l). intValue 0

«lnteger)r).intValue())j}
Object prod(Object I,Object r) {
return
new Integer(
((Integer) l). int Value 0
*

«lnteger)r). intValue()); }
}

Define SetEvalv.

79
Is it natural for two evaluators to be on the Much more so than one extending the other.
same footing?

80
Time for supper. If you are neither hungry nor tired, you may
continue.

Like Father, Like Son 131


Copyrighted Material
81
Remember SubstV from chapter 6? Yes, and LtdSubstV, too.

class SubstV implements PieVisitorI { class LtdSubstV implements PieVisitorI {


Object n; int c;
Object 0; Object n;
Substv (Object _n,Object _0 ) { Object 0;
n = _n; LtdSubstV(int _c , Obj ect _n,Object _ 0) {
o = _0; } c =_c;
n _n;
=

public PieD forBotO { 0= _0; }

return new Bot(); }


public PieD forTop(Object t , Pi eD r) { public PieD forBotO {
if (0. equals(t)) return new Bot(); }
return public PieD forTop(Object t, Pi eD 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 (0. equals (t))
} return
new Top ( n,
r.accept(
new LtdSubstV(c -l,n,o)));
else
return
new Top(t,r.accept(this)); }

82
What do the two visitors have in common? Many things: n, 0, and forBot.

83
Where do they differ? They differ in forTop , but LtdSubstV also has
an extra field.

84
And where do we put the pieces that two We put them into an abstract class.
classes have in common?

85
What else does the abstract class contain? It specifies the pieces that are different if
they are needed for all extensions.

132 Chapter 8
Copyrighted Material
86
Define the abstract class Subst!>, which It's not a big deal, except for the fields.
contains all the common pieces and specifies
what a concrete pie substituter must contain abstract class Subst!>
in addition. implements PieVisito,x {
Object n;
Object 0;
public Pie!> jorBot O {
return new Bot(); }
public
abstract Pie!> jorTop(Object t,Pie!> r);
}

87
We can define SubstV by extending Subst!> It also extends Substv.

class SubstV extends Subst!> { class LtdSubstV extends Subst!> {


SubstV(Object _n,Object _0) { int c;
n= _n; LtdSubstV(int _c,Object _n,Object _0) {
0= _0; } n = _n;
0=_0;
public Pie!> jorTop(Object t,Pie!> r) { c _c ; }
=

if (o.equals{t))
return public Pie!> jorTop(O bj ect t,Pie!> r) {
new Top{n, r. accept{thi s)); if (c == 0)
else return new To p{t, r);
return else
new Top{t, r.accept(this)); } if (o.equals{t))
} return
new Top(n,
r.accept(
Define LtdSubstv. new LtdSubstV(c - 1,n,0)));
else
return
new To p{t , r. ac ce pt{this) ) ; }
}

88
Do the two remaining classes still have things No, but the constructors have some overlap.
in common? Shouldn't we lift the SubstV constructor into
Subst!>, because it holds the common
elements?

Like Father, Like Son 133


Copyrighted Material
89
That's a great idea. Here is the new version We must use super in the constructors.
of Subst'D
class Substv extends Subst'D {
abstract class Subst'D SubstV (Object _n,Object _0 ) {
implements PieVisito� { super(_n,_o)j}
Object nj
Object OJ public Piev JorTop(Object t,Piev r) {
SubstV(Object _n,Object _0) { if ( 0. equals ( t))
n = _nj return
0= _O J } new Top(n,r.accept(this))j
else
public Pie'D jorBotO { return
return new BotO; } new Top(t,r.accept(this))j }
public }
abstract Pie'D jorTop(Object t,Pie'D r};
}
class LtdSubstV extends Subst'D {
Revise SubstV and LtdSubstV int Cj
LtdSubstv(int _c, Object _n,Object _0 ) {
super(_n,_o)j
c _c; }=

public Pie'D jorTop(Object t,Pie'D r) {


if(c 0)
==

return new Top(t,r)j


else
if( 0. equals ( t))
return
new Top(n,

r.accept(
new LtdSubstV(c -l,n,o)));
else
return
new Top(t,r.accept(this)); }
}

Was that first part easy? 90 Pili pie.

134 Chapter 8
Copyrighted Material
91
That's neat. How about some art work? Is this called a pie chart?

accept
- PieVisitorT

No, but the picture captures the important 92 Fine.


relationships.

93
Is it also possible to define LtdSubst v as an It may even be better. In some sense,
extension of Substv? LtdSubst v j ust adds a service to Subst v: It
counts as it substitutes.

94
IfLtdSubstv is defined as an extension of As we just said, c is an addition and forTop
SubstV, what has to be added and what has is different.
to be changed?

The Eighth Bit of Advice


When extending a class, us e overriding
to enrich its functionality.

Like Father, Like Son 135


Copyrighted Material
95
Here is the good old definition of SubstV The rest follows naturally, just as with the
from chapter 6 one more time. evaluators and the previous version of these
two classes.
class SubstV implements PieVisito..x {
Object n; class LtdSubstV extends SubstV {
Object 0; int c;
SubstV(Object _n,Object _0) { LtdSubstV(i nt _c,Object _n,Object _0) {
n = _n; super( _n,_o);
0= _0; } c= _c; }

public PieD forE ot O { public PieD forTop(Object t,PieD r) {


return new BotO ; } if (c==O)
public PieD forTop(Object t,PieD r) { return new Top(t,r);
if (0. equals (t)) else
return if (0. equals (t))
new Top(n, r.accept(this)); return
else new Top(n,
return r.accept(
new Top(t, r.accept(this)); } new LtdSubstV(c - 1, n,0)));
} else
return
new Top(t,r.accept(this)); }
Define LtdSubstV as an extension of Substv. }

96
Let's draw a picture. Fine, and don't forget to use lines, rather
than arrows, for implements.

accept
- PieVisito..x

97
You deserve a super-deluxe pizza now. It's already on its way.

136 Chapter 8
Copyrighted Material
@o
ru� � ��@dl ��rr

Copyrighted Material
Remember Pointv? If not, here is the It has been a long time since we discussed
datatype with one additional method, minus. the datatype PointV and its variants, but
We will talk about minus when we need it, they are not that easy to forget.
but for now, just recall PointV, s variants.
class CartesianPt extends PointV {
abstract class PointV { CartesianPt (int _x,int _y) {
int x; super(_x,_y); }
int y;
PointV ( int _x,int _ y ) { int distance ToOO {
x -Xj = return l VX2 + y2J; }
Y = -Yj } }
V
boolean closerToO(Point p) {
return class ManhattanPt extends PointV {
distanceToO() :S p.distance ToOO; } ManhattanPt ( int _x,int _y) {
PointV minus ( PointV p) { super(_x,_y); }
return
new CartesianPt (x - p.x,y - p.y); } int distanceToOO {
abstract int distance ToOO; return x + y; }
} }

Good. Take a look at this extension of It uses


ManhattanPt. b.x = _b.x;
b.y = _b.y;
class ShadowedManhattanPt
in addition to super( _x ,_ y ) .
extends ManhattanPt {
int b.x;
int b.y;
ShadowedManhattanPt ( int _x,
int _y,
int _b.x,
int _b.y) {
super(_x,_y);
b.x _b.xj =

b.y _b.yj }
=

int distanceToOO {
return
super.distanceToOO + b.x b.yj }
+
}

What is unusual about the constructor?

Be a Good Visitor 139


Copyrighted Material
And what does that mean? By using super on the first two values
consumed, the constructor creates a
ShadowedManhattanPt with proper x and y
fields. The rest guarantees that this newly
created point also contains values for the two
additional fields.

Okay. So what is a ShadowedManhattanPt? It is a ManhattanPt with two additional


fields: b..x and b..y. These two represent the
information that determines how far the
shadow is from the point with the fields x

and y.

5
ShadowedManhattanPt:
Is this a Yes.
new ShadowedManhattanPt(2,3,1,O)?

What is unusual about distance ToO? Unlike any other method we have seen
before, it contains the word super. So far,
we have only seen it used in constructors.
What does it mean?

Here, super. distance ToO refers to the Okay. That means we just add x and y when
method definition o f distance ToO that is we evaluate super.distanceToO().
relevant in the class that
ShadowedManhattanPt extends.

Correct. But what would we have done if Then we would refer to the definition in the
ManhattanPt had not defined distance ToO? class that ManhattanPt extends, right?

Yes, and so on. What is the value of It is 6, because 2 + 3 is 5, and then we have
new ShadowedManhattanPt(2,3,1,O) to add 1 and O.
distanceToO()?
.

140 Chapter 9
Copyrighted Material
10
Precisely. Now take a look at this extension Nothing. We just discussed this kind of
of Cartesian Pt. constructor for Shadowed Manhattan Pt.
class ShadowedCartesianPt
extends CartesianPt {
int .:l",;
int .:ly;
ShadowedCartesianPt(int -x,
int _y,
int �x,
int �y) {
super(-x,_y);
.:l", �"';=

.:ly �y; } =

int distanceToOO {
return
super.distanceToOO
+
L J.:l� + .:l�J; }
}

What is unusual about the constructor?

11
Is this a ShadowedCartesianPt: Yes.

new ShadowedCartesianPt(12,5,3,4)?

12
And what is the value of It is 18, because the distance of the
new ShadowedCartesianPt(12,5,3,4) Cartesian point (12,5) is 13, and then we add
distanceToOO?
. 5, because that is the value of

. 1.:l2 + .:l2y
V x
with .:lx replaced by 3 and .:ly replaced by 4.

13
W hat do we expect? 17, obviously.

Be a Good Visitor 141


Copyrighted Material
Why 17? 14 B ecause we need to think of th i s point as if it
were

new CartesianPt(15,9).

15
We need to add L\ to x and L\y to y when And indeed , the value of
x
we t hi nk of a ShadowedCartesianPt. new CartesianPt(15,9)
.distanceToOO
is 17.

16
Does this explain how distanceToO should Completely. It should make a new
measure the distance of a CartesianPt by adding the corresponding
ShadowedCartesianPt to the origin? fields and should then measure the distancf'
of that new point to the origin.

11
Revise the definit ion of ShadowedCartesianPt Okay.
accordingly.
class ShadowedCartesianPt
extends CartesianPt {
jnt L\x;
jnt L\y;
ShadowedCartesianPt(int _x,
jnt _y,
int _L\x,
int _L\y) {
super(_x,_y);
L\x = ..L\x;
L\y =
_L\y; }

int distanceToOO {
return
new CartesianPt(x + L\x,y + L\y)
.distanceToOO; }
}

18
Do we still need the new CartesianPt after No, once we have the distance, we have no

distanceToO has d et erm i ned the distance? need for this point. 1

1 And neither does Java. Object-oriented languages manage


memory so that programmers can focus on the difficult parts
of design and implementation.

142 Chapter 9
Copyrighted Material
Correct. W hat is the value of 19 true,
new CartesianPt(3,4) because the distance of t he CartesianPt to
.closerToO( the origin is 5, while that of the
new ShadowedCartesianPt(1,5,1,2))? ShadowedCartesianPt is 7.

20
How did we determine that value? That's obvious.

21
Is the rest of this chapter obvious, too? W hat ?

22
That was a hint that now is a good time to Oh. Well, that makes the hint obvious.
take a break.

Come back fully rested. You will more than 23 Fine.


need it.

2'
Are sandwiches square meals for you? They can be well-rounded.

25
Here are circles and squares. Then this must be the datatype that goes
with it.
class Circle extends ShapeD {
int r; abstract class ShapeD {
Circle(int _r) { abstract
r = _r; } boolean accept(ShapeVisito,x ask);
}
boolean accept(ShapeVisito,x ask) {
return ask.forCircle(r); }
}

class Square extends ShapeD {


int s;
Sq u a re ( int s ) { _

s = _ s; }
boolean accept(ShapeVisitorI ask) {
return ask.forSquare(s); }
}

Be a Good Visitor 143


Copyrighted Material
26
Very good. We also need an interface, and It suggests that there is another variant:
here it is. Trans.

interface ShapeVisito,x {
boolean forCircle(int r);
boolean forSquare(int s);
boolean forTrans(PointV q,Shapev s);
}

27
Yes and we will need this third variant. Okay, now this looks pretty straightforward,
but what's the poi nt?
class Trans! extends Shapev {
PointV q;
ShapeV s;
Trans(PointV _q,ShapeV _s) {
q = -q;
s = _s; }

boolean accept(ShapeVisito,x ask) {


return ask.forTrans(q,s); }
}

1 A better name is Translation.

28
Let's create a circle. No problem:
new Circle(10).

How should we think about that circle? 29 We should think about it as a circle wi t h
radius 10.

Good. So how should we think about 30 Well, that's a square whose sides are 10 units

new Square(lO)? long.

Where are our circle and square located? 31 What does that mean?

144 Chapter 9
Copyrighted Material
32
Suppose we wish to determine whether some In that case, we must think of the circle as

CartesianPt is inside of the circle? being drawn around the origin.

33
And how about the square? There are many ways to think about the
location of the square.

34
Pick one. Let's say the square's southwest corner sits
on the origin.

35 . .
That will do. Is the CartesianPt with x Yes, It IS. but barely.
coordinate 10 and y coordinate 10 inside the
square?

36
And how about the circle? Certainly not, because the circle's radius is
10, but the distance of the point to the origin
is 14.

Are all circles and squares located at the 37 We have no choice so far, because Circle and
origin? Square only contain one field each: the radius
and the length of a side, respectively.

38
This is where Trans comes in. What is Aha. With Trans we can place a circle of
newTrans( 10 at a point like
radius
new CartesianPt(5,6),
new CartesianPt(5,6).
new Circle(10))?

How do we place a square's southwest corner 39 Also with Trans:


at new CartesianPt(5,6)? new Trans(
new CartesianPt(5,6),

new Square(10)).

Is new CartesianPt(10,10) inside either the 40 It is inside both of them.


circle or the square that we just referred to?

Be a Good Visitor 145


Copyrighted Material
41
How do we determine whether some point is If the circle is located at the origin, it is
inside a circle? simple. We determine the distance of the
point to the origin and whether it is smaller
than the radius.

42
How do we determine whether some point is If the square is located at the origin, it is
inside a square? simple. We check whether the point's x

coordinate is between 0 and s, the length of


the side of the square.

43
Is that all? No, we also need to do that for the y
coordinate.

44
Aren't we on a roll? We have only done the easy stuff so far. It is
not clear how to check these things when the
circle or the square are not located at the
origin.

45
Let's take a look at our circle around We can if we translate all other points by an
new CartesianPt(5,6) appropriate amount.

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.

47
How could we translate the points by an We could subtract the appropriate amount
appropriate amount? from each point.

48
Is there a method in PointP that Yes. Is that why we included minus in the
accomplishes that? new definition of PointP?

146 Chapter 9
Copyrighted Material
49
Indeed. And now we can define the visitor The three methods put into algebra what we
HasPtv, whose methods determine whether just discussed.
some Shapev has a PointV inside of it.

class HasPt v implements ShapeVisitorI {


PointV p;
HasPtv (PointV _p) {
p -p; }
=

public boolean forCircle(int r) {


return p.distanceToOO ::; r; }
public boolean forSquare(int s) {
if! (p.x ::; s)
return (p.y ::; s);
else
return false; }
public
boolean for1Tans(PointV q,Shapev s) {
return s.accept(
new HasPtV(p.minus(q))); }

} We could have written the if .. as


return (p.x <= && (p.y <= s).
.

s)

50
What is the value of We said that this point wasn't inside of that
new Circle(lO) circle, so the answer is false.
. accept (
new HasPtV(new CartesianPt(lO,lO)))?

Good. And what is the value of 51 true.


new Square(lO)
.accept(
new HasPtV (new CartesianPt(lO,lO)))?

52
Let's consider something a bit more We already considered that one, too. The
interesting. What is the value of value is true, because the circle's orig i n is at
new Trans( new CartesianPt(5,6).
new CartesianPt(5,6),
new Circle(lO))
. accept (
new HasPtV(new CartesianPt(lO,lO)))?

Be a Good Visitor 147


Copyrighted Material
53
Right. And how about this: Now that is t ricky. We used Trans twice,
new Trans( which we should have expected given Trans's
new CartesianPt(5,4), definition.
new Trans(
new CartesianPt(5,6),
new Circle(lO)))
. accept (
new Ha sPt 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 fa Ise.

And then? 55 Secon d , we need to look at


new Circle(lO)
. accept (
new H asPtV ( new CartesianPt(O,O))),
but the value of this is obviously true.

56
Very good. Can we nest Trans three ti mes? Ten times, if we wish, because a Trans
contains a ShapeD, and that all ows us to nest
things as oft en as needed.

Ready to begin? 57 What? Wasn't that it?

58
No. The exciting part is about to start. We are all eyes.

59
How can we proj ect a cube of cheese to a It becomes a square, obviously.
piece of paper?

60
And the orange on top? A circle, Transed appropriately.

148 Chapter 9
Copyrighted Material
61
Can we think of the two objects as one? We can, but we have no way of saying that a
circle and a square belong together.

62
Here is our way. That looks obvious after the fact. But why is
there a blank in accept?
class Union extends Shapev {
Shapev s;
Shapev t;
Union(ShapeV _s,Shapev _t) {
s _s; =

t _t; }
=

boolean accept(ShapeVisito,x ask) {


return ; }
}

63
What do we know from Circle, Square, and We know that a ShapeVisito,x contains one
Trans about accept? method each for the Circle, Square, and Trans
variants. And each of these methods
consumes the fields of the respective kinds of
objects.

6.
So what should we do now? We need to change ShapeVisito,x so that it
specifies a method for the Union variant in
addition to the methods for the existing
variants.

65
Correct, except that we won't allow ourselves Why can't we change it?
to change ShapeVisito,x

66
Just to make the problem more interesting. In that case, we're stuck.

Be a Good Visitor 149


Copyrighted Material
We would be stuck, but fortunately we can 67 Which means that we extend interfaces the
extend interfaces. Take a look at this. way we extend classes.

interface UnionVisito,x
extends ShapeV isito,x {
boolean forUnion(ShapeD s,ShapeD t);
}

68
1 Does that mean accept in Union should
Basically. This extension produces an
interface that contains all the obligations ,
receive a U n ionV i sito,x so that it can use the
(i. e., names of methods and what they for Union method?
consume and produce of ) ShapeV is ito,x and
the additional one named for Union.

1 Unlike a class, an interface can actually extend severa1


other interfaces. A class can implement several different
interfaces.

69
Yes it should, but because UnionVisito,x We have been here before. Our accept
extends ShapeV is ito,x , it is also a method must consume a
ShapeVisito,x and
S hapeV isito,x . fortunately every UnionVisito,x implements a
S hapeV isito,x too. But if we know that
,
accept consumes a UnionV is ito ,x , we can
convert the ShapeVisito,x to a UnionVisito,x
and invoke the for Union method.

70
Perfect reasoning. Here is the completed And it makes complete sense.
definition of Union.

class Union extends ShapeD {


ShapeD s ;
ShapeD t;
U n ion (ShapeD _s,ShapeD t ) { _

s = _ s;
t = _t; }

boolean acce pt(ShapeVisito,x ask) {


return
((UnionVisito,x) ask ).forUnion( s, t); }
}

150 Chapter 9
Copyrighted Material
Let's create a Union shape. 71 That '8 trivial.
new Trans(
new CartesianPt(12,2),
new Union(
new Square(10),
new Trans(
new CartesianPt(4,4),
new Circle(5)))).

That's an interesting shape. Should we check 72 We can't. HasPtV is only a ShapeVisito�, it


whether is not a UnionVisito�.
new CartesianPt(12,16)
is inside?

73
Could it be a UnionVisito�? No. It does not provide the method
forUnion.

74
Define UnionHasPtV, which extends HasPtV Here it is. Its method checks whether the
with an appropriate method forUnion. point is in one or the other part of a union.
The other methods come from HasPt v

class UnionHasPtV extends HasPtV {


UnionHasPtV(PointD -p) {
super ( _p) ; }

boolean forUnion(ShapeD s , Sha peD t) {


if! ( s. accept (this))
return true;
else
return t.accept(this); }
}

We could have written the if . .. as


return s.accept(this) II t.accept(this).

75
Does UnionHasPtV contain for Union? Of course, we just put it in.

Be a Good Visitor 151


Copyrighted Material

Is UnionHasPtV a UnionVisitorr? It provides the required methods: forCirc le,
forSquare, forTrans, and forUnion.

Correct, but unfortunately we have to add 77 The first two additional words have an
three more words to make this explicit. obvious meaning. They explicitly say that
this visitor provides the services of
class UnionHasPtV UnionVisitorr. And, as we have said before,
extends HasPtV the addition of public is necessary, because
implements UnionVisitorr { this visitor implements an interface.
UnionHasPtV(Point'D -p) {
super(_p); }

public
boolean forUnion(Shape'D s,Shape'D t) {
if (s.accept(this))
return true;
else
return t.accept(this); }
}

78
Good try. Let's see whether it works. What We know how for Trans works, so we're really
should be the value of asking whether
new Trans( new CartesianPt(lO,lO)
new CartesianPt(3,7),
is inside the Union shape.
new Union(
new Square(10),
new Circle(10)))
accept (
.

new UnionHasPtV(
new CartesianPt(13,17)))?

79
So? Which means that we're asking whether

new CartesianPt(lO,lO)
is inside of

new Square(10)
or inside of

new Circle{lO).

152 Chapter 9
Copyrighted Material
80
Okay. And what should be the answer? It should be true.

81
Let's see whether the value of Usually we start by determining what kind of
new Trans( object we are working with.
new CartesianPt(3,7),

new Union(

new Square(lO),

new Circle(lO)))

. accept(
new UnionHasPtV(

new CartesianPt(13,17)))

is true?

82
And? It's a ShapeD

8
How did we construct this shape? 3 With Trans.

84
Which method should we use on it? forTrans, of course.

8
5
Where is for Trans defined? It is defined in HasPtV

86
So what should we do now? We should determine the value of
new Union(
new Square(lO),
new Circle(lO))

. accept(
new HasPtV(

new CartesianPt(lO,lO))).

87
What type of object is It's a ShapeD.
new Union(
new Square(lO),

new Circle(lO))?

Be a Good Visitor 153


Copyrighted Material
88
How did we construct this ShapeD? With Union.

89
So which method should we use on it? jorUnion, of course.

90
How do we find the appropriate jorUnion In accept, which is defined in Union, we
method? confirm that
new HasPtV(
new Cartes i anPt ( lO, lO ) )
is a UnionVisitorr and then invoke its
jorUnion.

91
Is an instance of HasPtV a UnionVisitorr? No!

Does it contain a method jorUnion? 92 No!

Then what is the value of 93 It doesn't have a value. We are stuck. 1


new Union(
new Square(lO), 1 A Java program raises a RuntimeException1 indicating
new Cir cl e( lO ) ) that the attempt to confirm the UnionVisitorlness of the

. accept ( object failed. More specifically, we would see the following


when running the program:
newHasPtV( java. lang . ClassCastException: UnionHasPtV
new CartesianPt(lO,lO)))? at Union.accept( ... java:.,.)
at UnionHasPtV.forTrans(' .. java: ... )
at Trans. accept ( .. java: ... )
. .

9.
What do we do next? Relax. Read a novel. Take a nap.

95
Which of those is best? You guessed it: whatever you did is best.

We should have prepared this extension in a 96 How could we have done that?
better way.

154 Chapter 9
Copyrighted Material
97
Here is the definition of HasPtV that we In two ways. First, it contai n s a new
should have provide d if we wanted to extend method: newHasPt. Second, it uses the new
it without making changes. method in place of new HasPtV in for7rans.

class HasPtV implements ShapeVisito,x {


PointD p;
HasPtV(PointD -p) {
p -p; } =

I I
ShapeVisito,x newHasPt(PointD p) {
return new HasPtV(p); }

public boolean forCircle(int r ) {


return p.distanceToOO :::; r; }
public boolean forSquare(int s) {
ifl (p. x :::; s)
return (p.y :::; s);
else
return false; }
public
boolean for7rans(PointlJ q,ShapelJ s) {
return
s. accept(newHasPt(p. minus(q))); }

How does this definition differ from the


previous one?

98
Good. What does newHasPt produce? A new ShapeVisitorI, as its i nterface implies .

99
And how does it produce that? By constructing a new instance of HasPtv

100
Is newHasPt like a constructor? It is virtually i ndistinguishable from a
constructor, which is why it is above the line
that separates constructors from methods.

Be a Good Visitor 155


Copyrighted Material
101
Does that mean the new definition of HasPt v They are mostly indistinguishable. Both
and the previous one are really the same?l forTranses, the one in the previous and the
one in the new definition of HasPt v, produce
the same values when they consume the
I A functional programmer would say that newHasPt and
HasPtV are 1]-equivalent. same values.

102
Very well. But how does that help us with That's not obvious.
our problem?

103
Can we override newHasPt when we extend Yes, we can override any method that we
HasPtv? wish to override.

104
Let's override newHasPi in UnionHasPtV When we override it, we need to make sure it
produces a ShapeVisito�

105
That's true. Should it produce a HasPtV or a The latter. Then for Trans in HasPtV keeps
UnionHasPtv? producing a UnionHasPtV, if we start with a
Uni onHas Ptv

106
we
'

Good answer. Should repeat it? Let s just reread·It.

The Ninth Bit of Advice

If a datatype may have to be extended,


be forward looking and use a
constructor-like (overridable) method
so that visitors can be extended, too.

156 Chapter 9
Copyrighted Material
And that's exactly what we need. Revise the 107 Here it is.
definition of UnionHasPtV.1 ,------,
class UnionHasPtV
extends HasPtv
implements UnionVisitorr {
UnionHasPtV(PointD -p) {
super(_p); }
ShapeVisitorr newHasPt(PointD p) {
return new UnionHasPtV(p); }

public
boolean jorUnion(ShapeD s,ShapeD t) {
if ( s. accept(this))
return true;
else
return t.accept(this); }
}
1 The is an instance of the factory method pattern [4].

108
If we assemble all this into one picture, what A drawing that helps our understanding of
do we get? the relationships among the classes and
interfaces.

accept
- ShapeVisitorr

accept

109
W hat does the box mean? Everything outside of the box is what we
designed originally and considered to be
unchangeable; everything inside is our
extension.

Be a Good Visitor 157


Copyrighted Material
110
Does the picture convey the key idea of this No. It does not show the addition of a
chapter? constructor-like method to HasPtV and how
it is overridden in UnionHasPt v .

111
Is anything missing? Square, but that's okay.

112
Let's see whether this definition works. We remember that the shape was built with
What is the value of Trans.
new Trans(
new CartesianPt(3,7),

new Union(
new Square(lO),
new Circle(lO)))
. accept(
new UnionHasPtV(
new CartesianPt(13,17)))?

113
Which method should we use on it? for Trans , of course.

11.
Where is forTrans defined? It is defined in HasPt v

115
So what should we do now? We should determine the value of
new Union(
new Square(lO),
new Circle(lO))
. accept(
this. newHasPt(
new CartesianPt(lO,lO))).

116
What is this? The current visitor, of course.

117
And how does that work? We determine the value of
this. newHasPt(
new CartesianPt(lO,lO))
and then use accept for the rest.

158 Chapter 9
Copyrighted Material
118
And what do we create? The new UnionVisitorI:
new Un ionH asPt V (
new Ca rtes ianPt ( 10 , 10)).

119
What is the value of UnionHasPt v also satisfies the interface
new Union( ShapeVisitor1", so now we can invoke the
new S qu a re ( 10) , forUnion method.
new Circle(10))
. accept (
new Uni on H asPt V (
new CartesianPt(1O,10)))?

120
How do we do that? We first determine the value of
new Square(10)
. accept (
new UnionH asPt V (
new CartesianPt(10,10))).
If it is true, we're done.

121
Is it true? It is. So we're done and we got the value we
expected.

122
Are we happy now? Ecstatic.

123
Is it good to have extensible definitions? Yes. People should use extensible definitions
if they want their code to be used more than
once.

124
Very well. Does this mean we can put Yes, we can and should always do so.
together flexible and extensible definitions if
we use visitor protocols with these
constructor-like methods?

125
And why is that? Because no program is ever finished.

126
Are you hungry yet? Are our meals ever finished?

Be a Good Visitor 159


Copyrighted Material
fl(@ o
���) �{�'i!J� �
(Jli��!i>� � @@iW�

rfIl :·
111,'
11
d I

Copyrighted Material
Have you ever wondered where the pizza pies 1 You should have, because someone needs to
come from? make the pie.

2
Here is our pizza pieman. This is beyond anything we have seen before.

class PiemanM implements PiemanI {


Piev p = new BotO;
public int addTop (0 bject t) {
p new Top(t,p)
=

return occTop(t); }
public int remTop(Object t) {
P = (PieV)p.accept(new RemV(t»

return occTop(t); }
public int substTop(Object n,Object 0) {
p = (PieV)p.accept(new SubstV(n,o»

return occTop(n); }
public int occTop(Object 0) {
return
((Integer)p.accept(new Occursv (0»)
.intValueO; }
}

M This superscript is a reminder that the class manages a


data structure. Lower superscripts when you enter this kind
of definition in a file: PiemanM.

3
How so? Haven't we seen Piev, Top, and Bot We have seen t hem.
before?

And haven't we seen visitors like Rem v, Yes, yes. But what are the stand-alone
SubstV, and Occursv for various datatypes? semicolons about?

Let's not worry about them for a while. 5 Fine, but t hey are weird.

The State of Things to Come Copyrighted Material 161


6
Here is the interface for PiemanM Isn't it missing p?

interface PiemanI {
int addTop(Object t)j
int remTop (Object t)j
int substTop(Object n,Object o)j
int occTop(Object o)j
}

We don't specify fields in interfaces. And in 7 W hatever.


any case, we don't want anybody else to
see p.

Here are PieVisitorr and PieD They are very familiar.

interface PieVisitorr { class Bot extends PieD {


Object forBot()j Object accept ( PieVisitorr ask) {
Object forTop (Object t, PieD r); return ask.forBot()j }
} }

abstract class PieD { class Top extends PieD {


abstract Object tj
Object accept ( PieVisitorr ask); PieD r;
} Top(Object _t,PieD _r) {
t = _tj
Define Bot and Top. r = _rj }

Object accept( PieVisitorr ask) {


return ask. forTop (t, r) j }
}

162 Chapter 10
Copyrighted Material
Here is Occurs V. It counts how often some And this little visitor substitutes one good
topping occurs on a pie. topping for another.

class Occursv implements PieVisitorI { class Subst v implements PieVisitorI {


Object a; Object n;
OccursV(Object _a ) { Object 0;
a = a; }_
SubstV(Object _n,Object 0 ) { _

n = n; _

public Object forBotO { 0 = 0; } _

return new Integer(O); }


public Object forTop(Object t,PieV r) { public Object forBotO {
if (t. equals ( a ) ) return new BotO; }
return public O bject forTop(Object t,PieV r) {
new Integer(((lnteger) if ( o. equals (t ) )
(r. accept (this))) return
.intValueO new Top(n,(PieV)r.accept(this));
+ 1); else
else return
return r.accept(this); } new Top(t,(PieV)r.accept(this)); }
} }

10
Great! Now we have almost all the visitors We remember that one, too.
for our pieman. Define Rem v, which removes
a topping from a pie. class Rem v implements PieVisitorI {
Object 0;
Rem v (Object 0 ) {_

0 = _0; }

public Object forBotO {


return new Bot(); }
public Object forTop(Object t,PieV r) {
if ( o. equals (t ) )
return r. accept(this);
else
return
new Top(t,(PieV)r.accept(this)); }
}

The State of Things to Come


Copyrighted Material
163
11
Now we are ready to talk. What is the value We first create a PiemanM and then ask how
of many anchovies occur on the pie.
new PiemanMO.occTop(new Anchovy())?

12
Which pie? The pie named p in the new PiemanM.

And how many anchovies are on that pie? 13 None.

14
And what is the value of That's where those stand-alone semicolons

new PiemanMO.addTop(new Anchovy())? come in again. They were never explained.

15
True. I f we wish t o determine the value of Yes, we must understand that. There is no
number x in the world for which
new PiemanM O.addTop(new Anchovy()),
x = x + 1,
we must understand what
p = new Top(new AnchovyO,p) so why should we expect there to be a Java p
such that
return occTop(new Anchovy()) p = new Top(new Anchovy(),p)?
means?

That's right. But that's what happens when 16 So what does it mean?
you have one too many double espressos.

17
Here it means that p changes and that future And the change is that p has a new topping,
references to p reflect the change. right?

IS
When does the future begin? Does it begin below the stand-alone
semicolon?

19
That's precisely what a stand-alone It produces the number of anchovies on p.
semicolon means. Now do we know what

return occTop(new Anchovy())


produces?

164 Chapter 10
Copyrighted Material
20
And how many are there? We added one, so the value is 1.

21
And now what is the value of It's 2, isn't it?

new PiemanMO.addTop (new Anchovy ())?

22
No, it's not. Take a close look. We created a Oh, isn't there a way to place several
new pieman, and that pieman added only requests with the same pieman?
one anchovy to his p.

23
Yes, there is. Take a look at this: Okay, y stands for some pieman.

PiemanL y = new PiemanMO.

What is the value of 24 1. We know that.

y.addTop (new AnchovyO)?

25
And now what is the value of Still 1. According to the rules of semicolon
and =, this replaces all anchovies on p with
y. substTop (new TunaO,new Anchovy ())?
tunas, changes p, and then counts how many
tunas are on p.

26
Correct. So what is the value of 0, because y ' s pie no longer contains any
anchovies.
y.occTop(new Anchovy ())?

27
Very good. And now take a look at this: What are the

PiemanL yy = new PiemanMO.


What is the value of doing at the end?

yy.addTop (new Anchovy ())


,

yy.addTop (new Anchovy ())


,
yy.addTop (new Salmon ())

The State of Things to Come 165


Copyrighted Material
28
Because this is only half of what we w ant to 4. First we add two anchovies, then a
look at. Here is the other half: salmon, and two tunas. Th en we substitute
yy.addTop(new Tuna ( ) ) the two anchovies by two tunas. So y y ' s pie
co ntai ns four tunas.
yy.addTop(new Tuna ())

yy.substTop(new TunaO,new Anchovy ( )) ?

29
And what is the value of It's 0, because remTop first r e moves all tunas
yy.remTop(new Tuna ( )) and then counts how many there are left.

after we are through with all that ?

Does that mean rem Top always produ ces O? 30 Yes, it alw ays does .

Now what is the value of 31 1.

yy.occTop(new SalmonO)?

And how about 32 0, b ec aus e y and yy are two different piemen.

y.occTop(new Salmon ( )) ?

Is yy the same pieman as before? 33 No, it changed.

34
So is it the same on e? When we eat a pizza pie , we chang e , but we
are still the same .

When we asked yy to substitute all anchovies 35 The p in yy changed , nothing else.


by tunas, did the pie change?

36
Does that mean that anybody can w rite No, because y y ' s type is PiemanI, p isn't
yy.p = new Bot O available. Only addTop, remTop, substTop,
and oce Top are visible.
and thus change a pieman like yy?

166 Chapter 10
Copyrighted Material
37
Isn't it good that we didn't include p in Yes, with this trick we can prevent others
PiemanI? from changing p ( or parts of p) in strange
ways. Everything is clear now.

38
Just hke chIcken soup.
• •
Clear like soup?

Can we define a different version of Subst V so 39 We can't do that yet.


that it changes toppings the way a pieman
changes his pies?

40
And that's what we discuss next. Do you No, a cup of coffee will do.
need a break?

41
Compare this new PieVisitorI with the first It isn't all that different. A PieVisitorI must
one in this chapter. still provide two methods: for-Bot and
for-Top, except that the former now
interface PieVisito� { consumes a Bot and the latter a Top.
Object for-Bot(Bot that);
Object for-Top(Top that);
}

42
True. Here is the unchanged datatype. The definition is straightforward.

abstract class PieD { class Bot extends PieD {


abstract Object accept(PieVisito� ask) {
Object accept(PieVisitorI ask); return ask.for-Bot(this); }
} }

Define the Bot variant.

Is it? Why does it use this? 43 We only have one instance of Bot when we
use for-Bot, this, so for-Bot is clearly
namely
supposed to consume this.

The State of Things to Come 167


Copyrighted Material
That's progress. And that's what happens in 44 Interesting.
Top, too.

class Top extends PieD {


Object tj
PieD r;
Top(O bject _t,Pie1J _r) {
t t;
= _

r = _rj }

Object accept(PieVisito,x a sk) {


return ask.forTop(this); }
}

45
Modify this version of Occurs v so that it The forBot method basically stays the same,
implements the new PieVisito,x but forTop changes somewhat.

class Occursv implements PieVisito,x { class Occursv implements PieVisito,x {


O bject aj Object a;
OccursV(Object _a) { Occursv (Object _a) {
a = a; }
_ a _a; }
=

public Object forBotO { public O bj ec t forBot(Bot that) {


return new Integer(O); } return new Integer(O)j }
public O bject forTop(O bject t,PieD r) { public Object forTop(Top that) {
if (t. equals ( a) ) if ( th at. t. equals ( a))
return ret urn
new I ntege r ( «Integer) new Integer«(lnteger)
( r. ac cept (this) )) (that. r. accept(this)))
. intValue 0 . int Value 0
+ 1); + 1)j
else else
return r.accept(this); } return that. r. accept (this); }
} }

46
How does forBot change? It now consumes a Bo t , which is why we had
to add (Bot that) behind its name.

168 Chapter 10
Copyrighted Material
47
How does JorTop change? It no longer receives the field values of the
corresponding Top. Instead it consumes the
entire object, which makes the two fields
available as that. t and that. r.

48
And? With that, we can replace t he fields t and r

with that. t and that. r.

49
Isn't that easy? This modification of Occursv certainly is.

Then try Rem v 50 It's easy; we use the same trick.

class Rem v implements PieVisitorI" {


Object 0;
RemV(Object _0) {
0= _0; }

public Object JorBot(Bot that) {


return new Bot(); }
public Object JorTop(Top that) {
if (0. equals ( that.t))
return that.r.accept(this)j
else
return
new Top(that.t,
(Pie'D)that.r.accept(this)); }
}

51
Do we need to do SubstV? Not really. It should be just like Remv.

52
And indeed, it is. Happy now? So far, so good. But what's the point of this
exercise?

53
Oh, Point'Ds? They will show up later. Seriously.

The State of Things to Come 169


Copyrighted Material
54
Here is the point. What is new about this There are no news.
version of Substv?

class SubstV implements PieVisito,x {


Object n;
Object 0;
SubstV (Object _n,Object _0) {
n = _n;

0= _0; }

public Object JorBot (Bot that) {


return that; }
public Object JorTop (Top that) {
if ( o. equals (that. t)) {
that.t =n

that. r. accept ( this)

return that; }
else {
that. r. accept ( this)

return that; }
}
}

55
news
• •
Don't they say "no is good news?" Does thIS saymg apply here, too?

56
Yes, because we want to define a version of That's a way of putting it.
SubstV that modifies toppings without
constructing a new pie.

57
What do the methods of Substv always They always return that, which is the object
return? that they consume.

58
So how do they substitute toppings? By changing the that before they return it.
Specifically, they change the t field of that to
n when it equals o.

170 Chapter 10
Copyrighted Material
59
What? The

that.t = n
does it.

60
Correct. And from here on, that. t holds the In the previous Su bstV, r.accept(this)
new topping. What is created a new pie from r with all toppings
that. r. accept(this) appropriately substituted. In our new

about?
version, that.r.accept(this) modifies the pie r
so that below the following semicolon it
contains the appropriate toppings.

61
Is there anything else to say about the new Not really. It does what it does, which is
Su bstv? what we wanted. 1

1 This is a true instance of the vUlitor pattern [41. What we


previously called ('visitor" pattern instances, were simple
variations on the theme.

62
Do we have to change PiemanM? No, we didn't change what the visitors do,
we only changed how they do things.

63
Is it truly safe to modify the toppings of a Yes, because the PiemanM manages the
pie? toppings of p, and nobody else sees p.

64
Can we do LtdSu bstV now w ithout creating Now that's a piece of pie.
new instances of LtdSubstV or Top?

The Tenth Bit of Advice


When modifications to objects are
needed, use a class to insulate the
operations that modify objects.
Otherwise, beware the consequences of
your actions.

The State of Things to Come 171


Copyrighted Material
65
Here is a true dessert. It will help us The datatype has three extensions.
understand what the point of state is.
class CartesianPt extends PointV {
abstract class PointV { CartesianPt(int -x ,int _y) {
int x; super(_x,_y); }
int y;
PointV(int _x,int _y) { int distanceToOO {
x = -x; return l Jx 2 + y2J; }
y -y; }
=
}

boolean closerToO(PointV p) {
return class ManhattanPt extends PointV {
distanceToOO :S p.distanceToOO; } ManhattanPt(int _x,int _y) {
PointV minus(PointV p) { super(_x,_y); }
return
new CartesianPt(x - p.x,y - p.y); } int distanceToOO {
abstract int distanceToOO; return x + y; }
} }

class ShadowedManhattanPt
extends ManhattanPt {
int .0.x;
int .0.y;
ShadowedManhattanPt(int -x,
int _y,
int _.0.x,
int _.0.y) {
super(_x,_y);
.0.x _.0.x;
=

.0.y _.0.y; }
=

int distanceToOO {
return
super.distanceToOO+.0.x + .0.y; }
}

66
Aren't we missing a variant? Yes, we are missing ShadowedCartesianPt.

172 Chapter 10
Copyrighted Material
67
Good enough. We won't need it. Here is one Shouldn't we add a method that changes all
point: the fields of the points?

new ManhattanPt(1,4).
If this point represents a child walking down
the streets of Manhattan, how do we
represent his movement?

68
Yes. Add to PointV the method moveBy, First we must know what the method is
which consumes two ints and changes the supposed to produce.
fields of a point appropriately.

69
The method should return the new distance Now we know how to do this.
to the origin.
abstract class PointV {
int xii
int Yi
PointV (int _x,int _y) {
x = -Xj
Y = -Yi }

boolean closerToO(PointV p) {
return
distanceToOO :S p.distanceToOOi }
PointV minus(PointV p) {
return
new CartesianPt(x - p.x,y - P.Y)i }
int moveBy(int �x,int �y) {
x = x + �x

,
return distanceToOO; }
abstract int distanceToOOj
}

70
LetptChild stand for 5.

new ManhattanPt(1,4).
What is the value of

ptChild. distance ToOO?

The State of Things to Come


Copyrighted Material 173
What is the value of 71 15.
ptChild.moveBy(2,8)?

72
Good. Now let's watch a child with a 7.
helium-filled balloon that casts a shadow.
LetptChildBalloon be
new ShadowedManhattanPt(I,4,I,I).

What is the value of

ptChildBalloon. distance To 0 O?

What is the value of 73 17, of course.

ptChildBalloon. moveBy(2,8)?

74
Did the balloon move, too? Yes, it just moved along as we moved the
point.

Isn't that powerful? 75 It sure is. We added one method, used it,
and everything moved.

76
The more things change, the cheaper our Yes, but to get to the dessert, we had to
desserts get. work quite hard.

77
Correct but now we are through and it is Don't forget to leave a tip.
time to go out and to celebrate with a grand
dinner.

174 Chapter 10
Copyrighted Material
Copyrighted Material
You have reached the end of your introduction to computation with classes, interfaces, and
objects. Are you now ready to tackle a major programming problem? Programming requires
two kinds of knowledge: understanding the nature of computation, and discovering the lexicon,
features, and idiosyncrasies of a particular programming language. The first of these is the more
difficult intellectual task. If you understand the material in this book, you have mastered that
challenge. Still, it would be well worth your time to develop a fuller understanding of all the
capabilities in Java-this requires getting access to a running Java system and mastering those
idiosyncrasies. If you want to understand Java and object-oriented systems in greater depth,
take a look at the following books:

References

1. Arnold and Gosling. The Java Programming Language . Addison-Wesley, Reading, Mas­
sachusetts, 1996.

2. Buschmann, Meunier, Rohnert, Sommerlad, and Stal. A System of Patterns: Pattern­


Orie n ted Software Architecture. John Wiley & Sons, Ltd. Chichester, Europe, 1996.

3. Firesmith and Eykholt . Dictionary of Object Technolog y . SIGS Books, Inc., New York,
New York, 1995 and Prentice Hall, Englewood Cliffs, New Jersey, 1995.

4. Gamma, Helm, Johnson, and Vlissides. Design Patterns. Addison-Wesley, Reading,


M assachusett s , 1994.

5. Gosling, Joy, and Steele. The Java Language Specification. Addison-Wesley, Reading,
Massachusetts, 1996.

6. Pree. De sign Patterns for Object-Oriented Software Development. Addison-Wesley, Read­


ing, Massachusetts, 1994.

Commencement Copyrighted Material 177


Index
Add, 124 iO ccursv , 111
addTop, 161 IsFlat v, 113
Anchovy, 43, 53, 64, 65, 69, 72 IsSplit v, 113
App le, 100 is Vegetarian, 25, 27
IsVegetarian v, 63
Base, 10 is Veggie, 30, 31, 36
bHasFruit V, 104 iTreeVisito�, 107
blsFlatV, 102
blsSplit v, 102, 103 KebabD, 28, 36
Bot, 69, 78, 83, 85,86, 91,93, 162, 167
Brass, 29 Lamb, 16, 27, 28, 36, 59, 62
Bud, 100, 101, 107, 110,112 LayerD, 10
bTreeVisito�, 101 Lemon, 100
LtdSubstV, 95,97,132-136
CartesianPt, 5,13,37, 38-40, 139,172
Cheese, 43,53, 64, 65 ManhattanPt, 5, 13,38,39,139, 172

Circle, 143 mem,124


closerToO, 14,38-40,139,172,173 minus, 139
Const, 119 moveBy, 173
C op p er, 29
newHasPt, 155, 157
Crust , 43, 53, 64, 65
NumD, 7,79
Dagger, 29
ace Top, 161
diff, 124
Occursv , 114,163,168
Diff, 119
Olive, 43, 53, 64, 65
distance ToO, 14,38-40, 139, 141, 142,172
O n e M ore Than, 7,79
Empty, 124 Onion, 16, 27, 28, 36, 59, 62
equals, 72, 79, 100 onlyOnions, 18, 27
EvalD, 130 OnlyOnionsv, 57, 59

ExprD, 119
Peach, 100
ExprVisito�, 118
Pear, 100

Fig , 100 Pepper, 4, 37

FishD, 69,72 PieD , 69,78,83,85,86,91,93,162, 167

Flat, 100, 101, 108,110, 112 PieVisito�, 92,162, 167

F ru itD , 100 PiemanT, 162


PiemanM , 161
Gold, 29 P izzaD , 43, 53, 63-65
PlateD, 29
H asP tV , 147,155 plus, 124
H ol d er, 28, 36 Plus, 119
PointD, 5, 13, 40, 139, 172, 173
iHeightV, 108 prod, 124
IntEvalv, 120,122,131 Prod, 119

Copyrighted Material
Radish, 28, 36 subAbC, 52, 53
remA, 45, 49, 70 SubAbCv, 66
RemAv, 66,71,73 SubstV, 133, 134
RemFishv, 74 SubstFish v, 82
Remv, 77,87,93,163,169 SubstV, 83,87,89,94,132-136,163,170
RemlntV, 76 SubstlntV, 82
remTop, 161 substTop, 161
Rodv, 29 Sword, 29

Sabre, 29 Thyme, 5
Sage, 5 Tomato, 16, 27, 28, 36, 59, 62
Salmon, 69, 72 Top, 69, 78, 83, 85, 86, 91, 93, 162 168
Salt, 4 topAwC, 50, 53
Sausag e, 43, 53, 63-65 TopAwCv, 66
SeasoningV, 4 Trans, 144
SetV, 123 Treev. 100, 101, 107, 109, 112
SetEvalv, 126, 131 T reeVisito� . 112
ShadowedCartesianPt, 141, 142 tSubstV, 111
ShadowedManhattanPt, 139, 172 tTreeVisito�, 109
S hallot , 28, 36 Tuna, 69,72
Shapev, 143
ShapeVisito�. 144 Union, 149. 150
Shishv, 16,27,59,61 UnionHasPtv, 151, 152, 157
Shrimp, 28, 36 UnionVisito�, 150
Silver, 29
whatHolder, 33-36
Skewer, 16, 27, 59, 62
Wood, 29
Slice, 10
Spinach, 54
Zero, 7, 79
Split, 100, 101 , 108, 110 , 112
Zucchini, 37,
Square, 143

Copyrighted Material 179


Index
This is for the loyal Schemers and MLers. No, we wouldn't forget factorial.

interface TI { class MkFact implements oo-->ooI {


o -->oI apply(TI x); public O-->OI apply( o-->oI fact) {
} return ne w Fac t (fact ); }

}
interface O--> oI {
Object apply(Object x); class Fact implements o-->oI {
} o-->oI fact;
Fact(o-->oI _fact) {
fact =-fact; }
interface oo-->ooI { public Object apply(Object i) {
o --> oI apply ( o-->oI x);
int inti =((lnteger)i).intValue();
} if (inti ==0)
return new Integer(1);
else
interface oo-->oo-->ooI {
I return
O-->O apply ( oo-->ooI x);
new Integer(
} inti
*

class Y implements oo-->OO-->ooI { ((Integer)


public o-->oI apply ( oo-+ooI J) { fact.apply(new Integer(inti - 1)))
return new H(f).apply(new H(f)); } .intValue()); }
} }

class H implements TI {
I
OO-+OO f;
H ( oo-+ooI -J) {
f -f; }
=

public o --> OI apply(TI x) {


return J.apply(new G(x)); }
}

class G implements o-->oI {


TI x;
G(TI _x) {
X =_x; }
public Object apply ( Object y) {
return (x.apply(x)).apply(y); }

Copyrighted Material