Interactive Programming in Java
Interactive Programming in Java
in Java
Chapter Summary
Exercises
• Sidebar: Java Naming Syntax and Conventions
• Sidebar: Java Primitive Types
Chapter 13 Encapsulation
Chapter Overview
Objectives of this Chapter
13.1 Design, Abstraction, and Encapsulation
13.2 Procedural Abstraction
13.2.1 The Description Rule of Thumb
13.2.2 The Length Rule of Thumb
13.2.3 The Repetition Rule of Thumb
13.2.4 Example
13.2.5 Benefits of Abstraction
13.3 Protecting Internal Structure
13.3.1 private
13.3.2 Packages
13.3.2.1 Packages and Names
13.3.2.2 Packages and Visibility
13.3.3 Inheritance
13.3.4 Clever Use of Interfaces
13.4 Inner Classes
13.4.1 Static Classes
13.4.2 Member Classes
13.4.3 Local Classes and Anonymous Classes
Chapter Summary
Exercises (to come)
Appendices
Applets
Java.awt Quick Reference
Java Charts
About Java Charts
1 Program File
2 Class Declaration
3 Field Declaration
4 Method Declaration
5 Expression
6 Statement
7 Disclaimers, Notes, Amendments, etc.
Glossary
contents
To come…
Chapter 20 Synchronization
Chapter Overview
Objectives of this Chapter
20.1 Reads and Writes
20.2 An Example of Conflict
20.3 Synchronization
20.4 Java synchronized
20.4.1 methods
20.4.2 (blocks)
20.5 What synchronization buys you
20.6 Safety rules
20.7 Deadlock
20.8 Obscure Details
Chapter Summary
Exercises
Exercises
• Sidebar: Final
• Arithmetic Expressions
• class AnimatorThread
• class Math
• class Main
• Documentation (Style)
• Final
• Java Operators
Research Roots
In the early 1990s, the author worked to bring intuitions about computation into
the classroom through the use of simple, inexpensive robotics. The use of robots
enabled a focus on software life cycle, non-repeatability, and pragmatic software
engineering uncommon in traditional introductory classrooms. The curriculum
that developed from this experimentation marked a radical departure from the
traditional single-threaded, sequentialist story.
The use of robotics clearly forced a shift in perspective in the introductory
programming curriculum. In the first half of the decade, this shift was echoed, if
more subtly, in the popular software market through approaches such as event-
driven programming, client-server architectures, and enterprise computing. Those
techniques -- increasingly important to industry -- were still not deemed suitable
for an introductory computing classroom. Nonetheless, they were inescapably
changing the face of the computing sciences. Computing-in-the-raw is no longer
calculate-and-stop. Instead, it is made up of agents and services, communities of
ongoing interacting entities. Yet today's introductory classrooms shed little light
on these now-prevalent industry practices.
Courses taught during this period included MIT freshmen, MIT graduate students,
and international researchers in artificial intelligence. Spin-offs of these efforts
include robotics classes at a variety of universities and colleges as well as the
now-annual Robot-Building Laboratory at the National Conference on Artificial
Intelligence and the establishment of the KISS Institute for Practical Robotics (of
which the author is an Institute Fellow).
With the advent of the world-wide web and the popular adoption of Java, a new
avenue towards teaching these approaches has been opened. The current
Rethinking CS101 Project has shifted its focus away from physical robots and
towards the underlying principles of interactive computation as illustrated by
purely software systems. (A side effort within the project continues to pursue the
robot hook, both in software simulations and in the interests of capitalizing on the
newly emerging commodity robot market. Although robots are not central to the
curricular shift represented by this project, they are easily integrated into its
Classroom Experience
The curriculum presented in Interactive Programming in Java has been taught in
a variety of venues. The first course taught with the current set of materials was
held in the summer of 1996, in a one-week intensive minicourse using the Java
1.0 API and Sun's JDK, the only Java available at the time. Its students were
executives, managers, and a few software engineers enrolled in MIT's Summer
Professional Programs. The majority had no substantial prior programming
experience.
The course was subsequently taught twice in MIT's regular curriculum. Students
were largely first-semester freshmen and others with no prior programming
experience. (The course is also popular among advanced students in non-
computational fields who want a single semester of computational coursework.)
Student feedback has been resoundingly positive. The MIT course has been
adopted by the EECS Department as a regular offering and is listed in the catalog
as subject number 6.030, Introduction to Interactive Programming.
Precursors to this textbook were also used in teaching several other minicourses to
professional audiences. These include the 1997 and 1998 Professional Institutes at
MIT and a tutorial offered at the ACM SIGPLAN's Conference on Object
Oriented Programming Systems, Languages, and Applications (OOPSLA '97).
Students in these courses included software professionals, academics, and
trainers. Generally versed in traditional programming, they attended the
minicourses to learn a new way to think about computation.
Other instructors have used the beta release of the textbook. In the fall of 1998,
the course materials was used at a handful of undergraduate institutions with
student bodies substantially less sophisticated than MIT's, as well as an advanced
class in a secondary school. Serious beta testing began in the fall of 1999, when
over a thousand students at more than a dozen colleges and universities around
the world used Interactive Programming in Java as their primary text. Additional
non-traditional classroom tests are also underway. Ultimately, the textbook is
intended for deployment in mainstream undergraduate classrooms as well as
certain advanced secondary classes, perhaps AP.
The curriculum itself has attracted widespread attention. It has been presented at a
Part By Part
Part 1 is brief and introductory, providing an overview of the approach to
computer programming taken. Part 2 begins with the basic syntax and semantics
of programming constructs. At the same time, from the earliest examples, students
are introduced to concurrent, interactive, embedded programs. For example,
interfaces are introduced early as they specify a contract between two parts of a
computer system. By the middle of part 3, students have learned to write what
Chapter Overview
• What is a computer program?
• What are the parts of a program? How are they put together?
• What kinds of questions does a program designer ask?
In this chapter you will learn how a computer can be controlled by a set of
instructions called a program. This chapter introduces two different aspects of
computation: single-minded instruction following and coordination among
instruction followers. The programs in this book involve both aspects of
computation.
The first aspect of computation is as step-by-step instruction following, like the
process of making a single sandwich. This kind of computation is a sequence of
instructions that produce some desired result. The question that drives this part is
"What do I do next?" Pieces are put together using "Next,...", "If ... then ... else
...", and "until...". This kind of computation has an end goal that execution of
these instructions will accomplish. The programs in this book use short sequences
of instructions, executed over and over, to create entities that can provide services
©1999 Lynn Andrea Stein. This chapter is excerpted from a draft of Interactive Programming In Java, a
forthcoming textbook from Morgan Kaufmann Publishers. It is an element of the course materials developed
as a part of Lynn Andrea Stein's Rethinking CS101 Project at the MIT AI Lab and the Department of
Electrical Engineering and Computer Science at the Massachusetts Institute of Technology.
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
1~2 Introduction to Program Design Chapter 1
another. (Think of running the same word processing program on two different
occasions; the experiences are extremely different even though the computer
follows the same general-purpose instructions both times.)
When you sit down at a computer, someone else has already told it how to do a lot
of things. For example, when you press the power switch, it boots up, or gets
started running, in the way that it has been instructed to. Personal computers
typically come with a fairly sophisticated set of startup instructions already
installed. Simply turning on the computer causes the computer to execute this
startup program.1 Each computer has a program that it runs automatically. The
program that your desktop or laptop PC runs is called its operating system. A disk
drive -- which is really a separate computer plus the electronic equivalent of a
huge filing cabinet -- comes equipped with instructions for how to retrieve
information from (or store information in) that filing cabinet plus how to transmit
that information across the cable that connects the disk drive with your "main"
computer. A microwave oven comes with a computer that follows instructions for
how to tell time and how to turn on its microwave generator for specified periods.
The library's card catalog provides lookup services. Your car's cruise control
accelerates and decelerates to keep you car moving at a steady rate. A web
browser fetches and displays information it retrieves from the hard drives (file
cabinets) of computers scattered around the world (at your request) (with the
assistance of the "web server" programs running on those distant computers as
well as the network (transmission) services provided by a set of intervening
computers.
When you load a new piece of software onto your computer -- a cool new game,
for example -- what you are actually doing is giving your computer a copy of the
program -- the set of instructions that tells it how to do display graphics and make
appropriate sound effects or whatever it is that the particular piece of software
does. Writing down these instructions was the job of the person (or people) who
wrote the software, the programmer. Loading the software makes the
instructions (the script) available to your computer. Just having these instructions
lying around doesn't do you much good, though. To actually play the game
(perform the play), you need to do one more thing. You need to run the program.2
1
Starting a computer is called "booting it up", presumably from the phrase "pulling yourself up by
your bootstraps". The startup program that a computer executes each time that it is turned on is
called the computer's "boot sequence".
2
Some computer games can be run off of removable media, like CD ROMs. In this case, you don't
need to load the program onto the computer, but you do need to make sure that the disk is in the
drive, i.e., that the instructions are available to the computer.
Tomorrow, if you want to play the game again, you only have to run it; you don't
have to start by loading it onto your computer.
If there is an instruction here that the Martian does not understand, that instruction
needs to be rewritten in more detail so that the Martian will be able to execute it.
For example, "pick up a glop of peanut butter" might require further explanation:
1.
a. Insert the knife blade half-way into the jar of peanut butter.
b. Remove the knife from the jar of peanut butter at a slight angle so that
some peanut butter is carried out of the jar by the knife.
c. ....
An instruction that needs further explanation before the Martian (or computer)
can execute it is one that we call high level. We can use high level steps in our
programs only if we can supply additional instructions to explain how to actually
execute these higher level steps.
Although we don't know what instructions Martians are likely to understand, a
programmer knows what kinds of instructions are a part of the particular
programming language in which s/he is developing a computer program. In this
book, we will use a programming language called Java. As you read this book,
you will learn how to think like a programmer and how to write instructions that
computers can understand. You will also learn specifically about the kinds of
instructions that are part of the Java programming language.
As a programmer, you will design sequences of steps much like the peanut butter
and jelly sandwich instructions. The goal of such a sequence is to get something
done, to find an answer or to create something. In order to design a program like
this, you will need to repeatedly answer the question, "What do I do next?" until
you have reached your desired result. In many ways, this approach makes
computers seem much like sophisticated calculators. In fact, this is where
computers got their start: the word "computer" used to refer to people who did
(mathematical) computations, and the original mechanical computers were
designed to perform these computations automatically.
When you are designing a program, you should ask yourself, "What do I do
next?" You don't necessarily have to write out all of the basic steps in one long
sequence. You can group them together in bigger, more abstract, higher level
chunks:
I. Assemble the ingredients.
II. Spread the peanut butter.
[ Number is wrong; should continue list above. Same problem above and below.]
1. If the top of the slice of bread is covered in peanut butter, go to step 10.
Otherwise, go back to step 8.
This step contains a choice; the next step might be 8 or it might be 10, depending
on whether the slice of bread is full. The Martian (or computer) executing this
program will have to keep track of which step comes next. This kind of choice
step is called a conditional, and it is a common construct in programming
languages. It is especially useful when the answer to the question "What do I do
next?" depends on something you won't be able to figure out until you're
executing the program.
We might want to go further, replacing steps 8 and 9 with a new kind of step that
says
1. Repeat the following substeps until the top of the slice of bread is
completely covered in peanut butter
a. pick up a glop of peanut butter
b. spread it on the top of the slice of bread.
This step ("repeat until") is called a loop. It, too, is a common construct in
programming languages. Some loops tell you to keep going until something is
true (like the bread becoming full), while others tell you how many times to do
the steps inside the loop. Some loops even go on forever. For example, a clock is
basically a loop that moves its hand(s) (or changes its display) once a minute.
Loops are especially useful when part of "What do I do next?" is to repeat
(almost) the same thing several times.
Each of the techniques described above -- sequencing steps, conditionals, loops,
and grouping steps into new basic steps (also called procedural abstraction) -- is
an important part of building computer programs. You will learn more about how
to do these things in Part 2 of this book. These are the pieces that a programmer
uses to answer the questions "What do I do next?" and "How do I do each of these
things?" But this is only one part of the programming problem. The second part of
programming is coordinating the activities of many interdependent participants in
a computational community.
participants take this form, and we will look more closely at control loops in Part
3 of this book. Programs with ongoing central control loops like this are the
members of our interactive computational community.
3
How many customers the restaurant can handle is called its bandwidth. How quickly each one
can be served is called its latency. The number of customers per hour that the restaurant can
handle is its throughput. These quantities -- bandwidth, latency, throughput -- are common
made explicit, and we will also need to say what happens when they are violated.
(In this case, the timely service guarantee won't be upheld, but how slow the
service gets should be related to how overloaded the restaurant is.)
There are other assumptions we do not make about our program, and we can
articulate these as well.. We do not assume that only one customer will be served
at a time. Instead, we expect that multiple tables must be handled (roughly)
simultaneously. It certainly won't do to wait until the first has eaten, paid, and left
before addressing the second. We also permit different interactions with each
table to be handled simultaneously or at least overlapped; food may be cooking
while checks are being written up.
This description is still fairly general, and we can imagine making it more
specific. (For example, are customers constrained to ordering off of a menu?) In
general, the more detail you can give of what your program ought to do, the easier
your task will be in designing and building it.
inside" is closely related to "how do they interact." After all, specifying what
interactions each entity needs to support goes a far way towards telling you
whether the "what goes inside" meets the requirements of the community.
Some subsidiary questions to ask about how each of the entities is constituted
include:
• What responsibilities does it have?
• What guarantees (promises, commitments) does it make? Under what
assumptions?
• What resources does it control?
• How does it work?
• Is it a community, too?
For example, the restaurant's wait staff might be responsible for greeting the
customers in a timely fashion, supplying each one with a menu (a structure that
the program will have to provide and keep updated!), taking the order, delivering
it to the kitchen staff, picking up and serving the cooked meal, obtaining a price
from the accounting entity, and obtaining payment for that amount from the
customer. The wait staff might guarantee to communicate with (most of) the
customers within minutes, provided the total number of customers is limited and
the maximum time spent with each is under a certain amount. It might also
promise to deliver food within some small amount of time after it's done cooking,
provided that the kitchen staff notifies the wait staff in a timely manner. The wait
staff controls menus, knows which food items were ordered by which customers,
and is the only part of the restaurant that deals directly with the customers. And so
on.
When it comes to "How does it work?", there are two kinds of answers. One
answer is that the behavior of the entity is accomplished by a single rule-follower
running an interactive control loop. We saw an example of this when we
considered the Martian chef earlier. In this case, we ask "What does the Martian
do next?" over and over, until we wind up with a well-defined set of instructions
for this Martian to follow.
The other possible answer to the question "How does this entity work?" is that
this entity is itself a community. (The wait staff might be further divided into the
person who takes the order, the person who clears the table, and the person who
serves the wine.) In this case, we need to figure out how to build each of these
entities, asking again "What goes inside each one?" The problem of figuring out
4
Batch processing is like the old-fashioned computations in which you handed your program to a
computer operator and came back the next day for your results.
Building a simpler version of your system gives you an opportunity to test your
basic approach before you have built up too much complexity. It also means that
your bugs, or program errors, will be easier to find. Bugs come in many flavors,
ranging from simple syntactic errors such as spelling mistakes, to programming
errors such as incorrect variable scoping, to conceptual design problems such as
impossible-to-meet but critical guarantees.
Even after you've found the bugs that keep your program from running, you will
need to subject your code to rigorous testing. This means trying out not only the
"normal" expected behavior, but also checking how your program handles
unexpected or anomalous behavior. Think of your program as an opponent you're
trying to trick; see if you can get it to misbehave. This testing -- when done right -
- will lead you to modify your code or even your design.
This repeated cycling through and between the various stages of specification (or
design) development, implementation, and testing is a crucial skill for any good
programmer. Classroom programs are too often written once and tested on
obvious cases. Most of the time and money spent on real-world software is spent
on revision and maintenance rather than on initial development. Acquainting
yourself with this cycle -- and with writing clean, easy-to-read, reusable code --
may be the most important part of becoming a skilled programmer. These issues -
- together with a tour through the development cycle -- are the topic of the next
chapter.
Perhaps the simplest interactive control loop is an echo program. When run, this
program waits for the user to type something. When the user finishes typing, the
program simply repeats back what it has been given. That is, it's a loop that gets
some input, processes that input (in this case trivially), and then spits out its
result.
Although the echo program seems too trivial to be of much use, a minor variant of
it runs in almost every program you type to: it's what makes the characters appear
on the screen. Far more importantly, the basic structure of this program underlies
essentially every interactive computation. And it demonstrates many of the
important properties of an interactive computation:
• It is embedded in an environment (in this case involving a user's typing and
a display that the user can see).
• It is interactive (with that user, but we could have it talk to another program
or over a network instead).
• It is concurrent: other things happen at the same time that the program is
running. (In this case, the user might be typing the next line even while the
echo program is producing its output.)
The idea of an interactive control loop is the root of this approach to
programming. By putting together interactive control loops, you constitute a
community of interacting entities. Interactive control loops are what goes inside;
communication between them is how they interact. In other words, as they say, all
the rest is corollary....
Chapter Summary
• Computers follow special instructions, called a program, which is written in a
special programming language.
Exercises
1. Give step by step instructions for how to tie shoelaces.
2. Select your favorite recipe and give step by step instructions for how to cook it.
3. Give detailed directions for how to get from your classroom to where you live.
Include indications that will tell whether you've gone too far and how to get back
on track.
4. Specify the expected behavior for each of the following interrelated services
provided by a bank account:
1. A deposit.
2. A withdrawal request.
Overview
This interlude provides a whirlwind introduction to most of the basic concepts of
Java programming. It uses a simple community of word games and other String
transformers to illustrate this exploration.
This interlude is not intended to be read as standalone coverage of these ideas.
Instead, it introduces many concepts only briefly, but in context. Each of the
programming concepts presented here is reintroduced in much greater detail in the
chapters of section 2 of this book.
©1999 Lynn Andrea Stein. This chapter is excerpted from a draft of Interactive Programming In Java, a
forthcoming textbook from Morgan Kaufmann Publishers. It is an element of the course materials developed
as a part of Lynn Andrea Stein's Rethinking CS101 Project at the MIT AI Lab and the Department of
Electrical Engineering and Computer Science at the Massachusetts Institute of Technology.
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
I1~2 A Community of Interacting Entities Interlude 1
In addition to transformers such as Pig Latin and Ubby Dubby, we'll want
capitalizers ("HELLO"), name droppers ("Lynn says Hello", or "Chris says
Hello", or "Pat says How are you doing?"), even delayers (e.g., that don't produce
"Hello" until after they've already received "How are you doing?") or network-
senders (that can move one of these strings-of-words from one computer to
another). We'll also have some community members that can read information
that a user types to them or display information on a computer screen. And we'll
have transformers that can take listen to two different inputs, producing only one
output, as well as transformers that can produce two outputs from only one input.
(The first of these is a collector; the second is a repeater. The first is good when
you have lots of people trying to talk all at once; the second is a nice way to
circulate (or broadcast) information that needs to get to a lot of people.)
In the system that we're going to explore, we will need a way to create individual
transformer-boxes like the ones described above. We'll also need a way to connect
them together. Finally, the transformer-boxes will need to act by themselves, to
read inputs, do transformations, and produce outputs. The complete system will
be a community of interacting entities, many of which will themselves be
communities. At the most basic level, each of these entities will need to follow
specific instructions. In this interlude, we will explore both the design of the
community and the specific instructions that some of these entities will follow.
At the end of the design process, we should be able to sketch out a scenario for
each of the major interactions with our system, including what roles need to be
filled (i.e., the types of things in our system), who fills these roles (i.e., the
individual objects that make up the system), and how they communicate among
themselves (i.e., the flow of control among these objects).
1
Take two tin cans with one end removed from each. Punch a whole in the center of the intact end
of each can. With a long piece of string, thread the two cans so that their flat ends face each other.
Tie knots in the ends of the string. Pull the string tight, so that it is stretched between the two cans.
Talk into one can; have someone else listen at the other.
Specifically, the Control Panel will have buttons representing each kind of
2
Maybe more than one.
user interface). We've addressed the question of who our community members are
(UI, transformers, connections, and -- stepping back -- the user) and, to a first
approximation, how they interact. In terms of system design, transformers and
connectors represent kinds of things of which there may be many separate
instances. For example, a particular community of transformers may contain five
transformers and four connectors, or eight transformers and three connectors, or
twelve. Each community will contain only a single control panel, though.
The next step in a full design process would be to look inside each of these
entities to discover whether they are, themselves, monolithic or further
decomposable into smaller communities. We will not decompose the user
interface further in this chapter; much of the necessary background for this task
will not be introduced until part 3 of this book. Instead, the remainder of this
interlude will look inside the transformer type to see how these objects are built.
• Have its own instruction-follower that acts independently to read its input,
transform that input as appropriate, and write its output.
In fact, this Transformer is itself a community. The connection acceptors are each
entities that are activated only on a connection accept request; their jobs are to
remember the connections that they have been handed. For example, the
acceptInputConnection instructions basically say, "To accept an input connection
(let's call it in), simply store in away somewhere so that you can use it later."
There's also a little bit of additional code to say what to do if you've already got
an input connection stored away. Output connections -- another part of the
community inside an individual Transformer -- are handled in the same way as
input connections. Also, some kinds of Transformers will have code that needs to
be run when an individual Transformer is created. Finally, the independent
1.3.2 Strings
In Java, there is a special kind of object, called a String, that is designed to
represent these words or phrases. In fact, in Java a String can be almost any
sequence of characters typed between two double-quote marks, including spaces
and most of the funny characters on your keyboard. (The double quotes aren't
actually a part of the String itself; they simply indicate where it begins and ends.)
For example, legitimate Java Strings include "Hello" and "this is a String" and
even "&())__)&^%^^". (Strings don't have to make sense.) The Transformers that
we will build are really StringTransformers, since each one takes in a String at a
time and produces a corresponding, potentially new or transformed String as
output.
is for all intents and purposes the same as just typing the single String3
3
Note that there is no space between the g at the end of String and the % at the beginning of
So, for example, a NameDropper transformer might use + to create a new String
using the input it reads, the name of the particular dropper, and the word "says".
Pig Latin and Ubby Dubby might use +, too, but they'll have to pull apart the
String they read in first.
These and other useful functions are summarized in the sidebar on String
Methods.
%%^$^&&))
4
Computer scientists almost always number things from 0. This is apparently an occupational
hazard.
• toUpperCase() produces a String just like the String you start with, but
in which all letters are capitalized. For example,
"MixedCaseString".toUpperCase()
produces
"MIXEDCASESTRING"
• trim() produces a similar String in which all leading and trailing white
space (spaces, tabs, etc.) has been removed. So
" a very spacey String ".trim()
is just
"a very spacey String"
• replace( old, new ) requires two characters, old and new, and
produces a new String in which each occurrence of old is replaced by
5
new: For example,
"Tee hee!".replace('e', '*' )
produces
"T** h**!"
• charAt( pos ) requires an index into the String and returns the
character at that index. Recall that Strings are indexed starting at 0.
"Hello".charAt( 2 ) is the same as "Hello".charAt( 3 )
This rule describes the transformation rule for an UpperCaser. Note that theString
5
A character is, roughly, a single alphanumeric or symbolic character (one keystroke) inside
single quotation marks. For more detail on what exactly constitutes a character, see the chapter on
Java types.
Note that we have chosen a different temporary name to represent the String
argument. The parameter name doesn't matter; we can choose whatever (legal
Java) name we wish.[Footnote: Legal Java names are covered in the sidebar on
Java names in the chapter on Types.] It can be the same name in every
transformer rule, or different in each one. It is only important that we use the
same name in a particular rule both when we're specifying the parameter (in the
first line of the rule) and in the body of the rule.
Q. Can you think of another kind of Transformer and write its rule? Remember, it
should take a String and produce a String.
The rules as we've presented them aren't really Java code, but they are pretty
close. To make them legal Java, we need to add a bit more formality and syntax.
The formal name for a rule in Java is a method, just like the String methods --
toUpperCase(), substring( index ), etc. -- above. Somewhere, someone has
provided instructions for how to toUpperCase() so that you can use that method
without worrying how it is done. Here, we are providing the instructions for
transform, so that someone else can use it.
Q. Quick quiz: How would you write the pedantic Transformer's transform
method?
{
return thePhrase.toUpperCase();
}
}
This says that UpperCaser is a type (or class) that is very much like the more
general class StringTransformer. Its behavior differs from generic
StringTransformers by using the particular transform rule contained inside the
braces {} that delineate UpperCaser's body.
Pedant is similar:
class Pedant extends StringTransformer
{
String transform ( String whatToSay )
{
return "Obviously " + whatToSay;
}
}
Q. A class that uses your transformer rule should be very much like these. Can
you write it?
These classes are descriptions of what an UpperCaser or a Pedant should do. They
are not UpperCasers or Pedants themselves, though. They're really more like
recipes from which a particular UpperCaser or a particular Pedant can be made.
To make an UpperCaser, you use the special Java construction expression new
UpperCaser(). Thisk "cooks up" a particular UpperCaser using the recipe we just
wrote. A Pedant is created similarly, but using a different recipe: new Pedant().
Q. How about Pedant, then Pedant, then UpperCaser, then Pedant? Then
UpperCaser?
But my name here isn't a parameter. It isn't a piece of information that is supplied
to the NameDropper each time the NameDropper performs a transformation, the
way that thePhrase is. Instead, my name is a persistent part of the NameDropper.
And it is a part of the particular NameDropper instance, not a part of the
NameDropper type. After all, each NameDropper drops its own name.
So where does this name come from? As each individual NameDropper is
created, it must be supplied with a name. Then, the particular NameDropper
remembers its own name, and when it comes time to transform a String, the
NameDropper uses its own name.
To do this, we need to create a local storage spot that sticks around between
transformations. This is done using a special kind of name that is associated with
the NameDropper instance. Such a name is called a field. In this case, we'll use a
field called name, because that's what it will hold. To make it clear in our code
that we're referring to a field, we use a syntax sort-of like saying my name; we
refer to the field using this.name. In Java, this is a way of letting an individual
instance say "my own".
So the actual transform method for NameDropper should read:
String transform ( String thePhrase )
{
return this.name + " says " + thePhrase;
}
This way, if one NameDropper has the name Pat and another has the name Chris,
Pat would transform the String "Hello" into "Pat says Hello" while Chris would
make it "Chris says Hello".
When we translate this into Java using the special syntax for a constructor rule, it
looks like this:
NameDropper( whatMyNameShouldBe )
{
this.name = whatMyNameShouldBe;
}
So the whole NameDropper class reads:
class NameDropper extends StringTransformer
{
String name; // the persistent storage,
// a permanent part of each
NameDropper
NameDropper( whatMyNameShouldBe )
{ // the creation
rule
this.name = whatMyNameShouldBe;
}
1.4 Summary
In this chapter, you have been exposed to many of the most basic pieces of Java
programming. None of these has been presented in sufficient detail to achieve
mastery of it. Each of these topics will be revisited, most in the next part of the
book. But the example described above gives a context within which to place the
detail that occupies the next several chapters.
In the next chapter, we will explore the role of types in Java systems and the
relationship between types and names. The final chapter of this section looks at
interfaces, the contracts that one type of object makes with another. In the next
section, we turn to expressions -- such as method invocation, field access,
instance construction, and even String concatenation -- and learn how evaluating
an expression produces a value of a specified type. Expressions are combined to
make statements, the step-by-step instructions of Java code that produce behavior
and flow of control. Classes allow us to implement behavior and to encapsulate
both instructions and local state -- such as the NameDropper's name -- into
individual objects. And self-animating objects contain their own instruction
followers that execute sequences of instructions over and over, communicating
with other objects and interacting to provide desired behavior on an ongoing
basis.
Suggested Problems
See the text for things marked with a Q. Also:
1. Implement LowerCaser.
2. Implement SentenceCaser (1st letter capitalized, rest not).
3. Implement Pig Latin.
4. An improved Pig Latin would leave the first letter in place if it were a vowel,
and add -way instead. This requires understanding basic conditionals and flow of
control. (See Statements.)
5. Ubby Dubby is pretty hard. You may want to look carefully at the chapter on
Dispatch.
6. Combiners and Repeaters involve extending StringTransformer in other ways,
overriding acceptInputConnection or acceptOutputConnection. (See the online
code supplement for StringTransformer source code.)
7. Really challenging problem: extract words, one word at a time, only reading an
input when all words have been used up.
Chapter Overview
• What kinds of Things can computers talk about?
• How do I figure out what they can do (or how they interact)?
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
3~2 Things, Types, and Names Chapter 3
you can do with a thing). Types are like contracts that tell you what kinds of
interactions you can have with things. Sometimes, the same thing can be viewed
in different ways, i.e., as having multiple types. For example, a person can be
viewed as a police officer or as a mother, depending on the context. (When
making an arrest, she is acting as a police officer; when you ask her for a second
helping of dessert, you are treating her as a mother.) A thing's type describes the
way in which you are regarding that thing. It does not necessarily give the
complete picture of the thing.
Names are ways of referring to things that already exist. A name doesn't bring a
thing into existence, but it is a useful way to get hold of a thing you've seen
before. Every name has an associated type, which tells you what sorts of things
the name can refer to. It also tells you what you can expect of the thing that that
name refers to. In other words, the type describes how you can interact with the
thing that the name names. There are actually two different kinds of names in
Java: primitive (shoebox) names and reference (label) names.
Sidebars in this chapter cover the details of legal Java names, Java primitive
types, and other syntactic and language-reference details.
3.1 Things
What kinds of things can your programs involve? Almost anything, as it turns out.
But we'll start with some very simple things.
• 6
• 42
• 3.5
• -3598.43101
Details of Java numeric literals -- and of all of the other literals discussed here --
are covered in the sidebar on Java Primitive Types. As we will see in the next
chapter, you can perform all of the usual arithmetic operations with Java's
numbers.1
Java can also manipulate letters and other characters. When you type them into
Java, you have to surround each character with a pair of single quotation marks:
'a', 'x', or '%'. Note that this enables Java to tell the difference between 6 (the
integer between 5 and 7) and '6'(the character 6, which on my keyboard is a
lower case '^'). The first is something that you can add or subtract. The second is
not.
One character by itself is not often very useful, so Java can also manipulate
sequences of characters called strings. Strings are used, for example, to
1
Be warned, though, that non-integer values -- real numbers -- are represented only
approximately.
communicate with the user. Error message, user input (i.e., what you type to a
running Java program), titles and captions are all examples of Java strings. To
describe a specific string in Java -- for example, the message that your computer
prints to the screen when you boot it up -- you can write it out surrounded by
double quotes: "Hi, how are you?" or "#^$%&&*%^$" or even "2 + 2". Your
computer doesn't understand the string, it just remembers it. (For example, the
computer doesn't know of any particular relationship between the last example
and the number 4 -- or the string "4".)
It turns out that it's also useful for many programs to be able to manipulate
conditions, too, so Java has one last kind of primitive value. For example, if we
are making sandwiches, it might be important to represent whether we've run out
of bread. We can talk about what to do when the bread basket is empty:
if the bread basket is empty, buy some more bread....
Conditions like this -- bread-basket emptiness -- are either true or false. We call
this kind of thing a boolean value. Booleans are almost always used in
conditional -- or test -- statements to determine flow of control, i.e., what should
this piece of the program do next? Java recognizes true and false as boolean
literals: if you type one of them in an appropriate place in your program, Java will
treat it as the corresponding truth value.
There are lots of rules about how these different things work and how they are
used. For many of the detailed rules about the primitive things that we have just
covered, see the sidebar on Java Primitive Types.
3.1.2 Objects
The things described above are very specific kinds of things, and they have very
limited functionality. In the next chapter, we will see what we can do to
manipulate these primitive kinds of things. Most of what goes on in Java, though,
concerns another kind of thing. This kind of thing can include anything you might
want to represent in a computer program. Some examples of these other things
include the radio button that the user just clicked, the window in which your
program is displaying its output, or the url of your home page. These things --
everything else your program can talk about -- are called objects. In Java, objects
include everything that is not one of the aforementioned primitive types.2
2
In fact, in Java, strings are objects and not primitives.
There are many different kinds of objects, from buttons and windows to
dictionaries and factories. Each kind of object has a type associated with it.
Objects can be asked to do things, and each kind of object -- each object type --
determines what individual objects of that type can do. For example, windows can
close; dictionaries can do lookups. Each particular kind of object provides a
particular set of services or actions that objects of that kind can do. Further, each
individual object of that type can perform these actions. For example, if
myWindow and yourWindow are two different window-type objects, myWindow
can close, and so can yourWindow. But if myWindow closes, that doesn't in
general affect yourWindow.
Some objects can even act on their own without being asked to do anything; they
are "born" or created with the ability to act autonomously. For example, an
Animator may paint a series of pictures rapidly on a screen, so that it looks to a
human observer like the picture is actually moving. The animator may do this
independently, without being asked to change the picture every 1/30th of a
second. Similarly, an alarm clock may keep track of the time and start ringing
when a preset time arises.
As you can see, objects can be much more interesting than the kinds of things
represented by Java primitive types. However, objects are somewhat more
complex than Java primitives. In particular, there are no object literals:3 you can't
type an arbitrary object directly into your program the way that you can type 3 or
'x' or "Hello!" or false.
Almost everything that you do in Java uses objects, and you will hear much more
about them throughout this book. This chapter concentrates on how you identify
the Things in a program and how names can be used to refer to them. In the next
chapter, we will see in more detail how to use these Things to produce other
Things. Chapter 5 concentrates on combining these pieces into a full-blown
recipe, a single list of instructions that can be followed to accomplish a particular
job. The three chapters following (6-8) look at objects in more detail, describing
how to create and use the objects that are manipulated by these instructions, and
how these instructions themselves can be combined to form objects and entities
that interact in a community.
3
Except String literals.
Certain names in Java are reserved words. This means that they have
special meanings and cannot be used as names -- i.e., to refer to things,
other than any built-in meaning they may have -- in Java. Reserved words
are sometimes also called keywords. These are:
abstract default if private throw
boolean do implements protected throws
break double import public transient
byte else instanceof return try
case extends int short void
catch final interface static volatile
char finally long super while
class float native switch
const for new synchronized
continue goto package this
Java is case-sensitive. This means that double and Double are two
different words in Java. However, you can insert any amount of white
space -- spaces, tabs, line breaks, etc. -- between two separate pieces of
Java -- or leave no space at all, provided that you don't run words together.
You can't stick white space into the middle of a piece of Java -- a name or
number, for example -- though.
Punctuation matters in Java. Pay careful attention to its use. Note, however,
that white space -- spaces, tabs, line breaks, etc. -- do not matter in Java.
Use white space to make your code more legible and easier to understand.
You will discover that there are certain conventions to the use of white
space -- such as lining up the names in a column, as we did above --
although these tend to vary from one programmer to the next.
To actually assign a value to a name -- to create a binding between that name and
that value -- Java uses the syntax
name = value
For example,
myFavoriteNumber = 4
Once a particular name refers to a particular thing -- say greeting has the value
"Hi, how are you?" -- then we can use the name wherever we would use its
value, with the same effect. The name becomes a stand-in for the thing it refers to.
In the next chapter, we will see that a name is a simple kind of expression. But
before we can assign a value to a name, we need to know whether the name is
allowed to label values of that type.
3.3 Types
Up to now, we've been pretty casual about our things. Java, however, is a strongly
typed language, meaning that it is not at all casual about what kind of thing
something is. Each Java thing comes into the world with a type, i.e., an indication
of what kind of thing it is. Java names, too, are created with types, and a Java
name can only be used to label objects of the appropriate type. Before we can use
a name -- as myFavoriteNumber, above -- we have to declare it to be of a
particular type. Declaring a name means stating that that particular name is to be
used for labeling values (things, objects) of some particular type.
4
Barring weird interpersonal pileups, of course.
the first declaration here creates a name, myFavoriteNumber, suitable for naming
integers (or, in Java, ints). The second line creates the name
firstLetterOfMyName, suitable for naming single characters (i.e., things of Java
type char).
A name has a certain lifetime, sometimes called its scope. Within that scope --
over its lifetime -- the name may be bound to many different values, though it can
only be bound to one value at a time. (For example, myFavoriteNumber may
initially be 4, but later change to be 13.) The association between a name and a
type persists for the lifetime of the name, however. (myFavoriteNumber can only
name an int, not a String or a boolean.)
If you need a larger range of numbers, you can use the Java type long,
which can hold values between - 263 and 263 - 1. You can't just type in a
value like 80951151051778, though. Literals intended to be interpreted as
long end with the character L (or l): 80951151051778L. There are also two
smaller integer types: the 16-bit short and the 8-bit byte. There are no
short or byte literals. For most purposes, the int is probably the Java
integral type of choice.
Real valued numbers are represented using floating point notation. There
are two versions of real numbers, again corresponding to the amount of
space that the computer uses to store them. One is float, short for floating
point; the other is double, for double precision floating point. Both are
only approximations to real numbers, and double is a better approximation
than float. Neither is precise enough for certain scientific calculations. A
38 -45
float is 32 bits, from positive or negative 3.4e to +/-1.4e ; a double is
308 -324
64 bits, from 1.8e to 4.9e . The double type gives more precise
representations of numbers (as well as a larger range), and so is more
appropriate for scientific calculations. However, since errors are magnified
when calculations are performed, computations with large numbers of
calculations mean that unless you are careful, the imprecision inherent in
these approximations will lead to large accumulated errors.5
5
These issues are studied by the field of mathematics known as numerical analysis.
leading -. Real and rational numbers can be written using decimal notation,
as in the text, or in scientific notation (e.g., 9.87E-65 or 3.e4).
The Java character type is called char. Java characters are represented
using an encoding called unicode, which is an extension of the ascii
encoding. Ascii encodes English alphanumeric characters as well as other
characters used by American computers using 8 binary digits. Unicode is a
16-bit representation that allows encoding of most of the world's alphabets.
Character literals are enclosed in single quotation marks: 'x'.
Characters that cannot easily be typed can be specified using a character
escape: a backslash followed by a special character or number indicating
the desired character. For example, the horizontal tab character can be
specified '\t'; newline is '\n''; the single quote character is '\'', double
quote is '\"', and backslash is '\\'. Characters can also be specified by
using their unicode numeric equivalent prefixed with the \u escape.
The true-or-false type is called boolean. There are exactly two boolean
literals, true and false.
The names of Java primitive types are entirely lower case.
such as AnimateObject and DefaultFrame. And, in the rest of this book, you will
be learning to define object types--and create instances of those types--to do what
you want. These types -- whether a part of the Java language or of your own
definition -- are all kinds of objects. Note that, by convention, the name of each
object type -- each class -- starts with a capital letter. The names of the primitive
types start with lower case letters, as do (most) names and methods.
Object types may include KlingonStarship (if you're building a space battle
adventure game), IllustratedBook (if you're building an electronic library
system), or PigLatinTranslator (if you're building a networked chat program).
Each of these object types may describe many different individual objects -- the
three KlingonStarships visible on your screen, the five hundred and seven
IllustratedBooks in the children's library, or the particular
PigLatinTranslator that your particular chat program is using. (These
individual objects are sometimes called instances of their types. For example, the
KlingonStarship that you just destroyed is a different KlingonStarship
instance from the one that is getting ready to fire its phasers at you. We'll explore
this idea in greater detail in chapter 7.)
Each individual object comes ready-made with certain properties and behavior.
An IllustratedBook has an author and an illustrator, for example. A
PigLatinTranslator may be able to translate a word that we supply it into Pig
Latin. We ask objects to do things (including telling us about themselves) using
specific services that these objects provide. Often, these services are accessed by
giving the name of the object we're asking followed by a dot (or period), followed
by the request we're making of the object. So if theLittlePrince is the name of
an IllustratedBook, theLittlePrince.getAuthor() would be a request for
the name of the author of the book: "Maurice de Saint Exupery". Similarly, if
myTranslator is a PigLatinTranslator,
myTranslator.processString("Hello") might be a request to myTranslator
to produce the Pig-Latin-ified version of "Hello", which is "ello-Hay". These
requests are the most basic form of interaction among the entities in our
community.
One particularly useful object is Console. Console is an object that can print a
String to the Java console, a standard place where someone running a Java
program can look for information. Console can also readln a String that the user
types to the Java console.
Console
Console is a special cs101 object that knows how to communicate with the
user in some very basic ways. If your program says
Console.println( "Hello there!" );
then the String "Hello there!" will appear on the Java console. The
command
Console.print( "Hi" );
is similar, except that Console.print doesn't end the line of output, while
Console.print does. This means that
Console.print( "A " );
Console.print( "is for apple." );
would produce the output
A is for apple.
while
Console.println( "A " );
Console.println( "is for apple." );
would produce
A
is for apple.
You can of course combine prints and printlns arbitrarily. Printing a
String containing a newline character escape (\n) causes the line to end as
well.
You can also use Strings that are associated with names or any other
Strings you may have access to, not just String literals.
name is declared. The type associated with a particular name never changes. It
turns out that there are two rather different kinds of names in Java. In this section,
we will look at each in turn and see what it means for a name to be declared to be
of a particular type.
associates i with storage appropriate for a 32-bit integer. In fact, the declaration
of a shoebox-type name not only sets up an appropriately sized shoebox, it also
fills that shoebox with an appropriate value. If -- as in this declaration of i -- no
value is specified, the shoebox contains the default value for the type -- in this
case, 0. There is no such thing as an empty shoebox. You must give a name a
value before you can use it.6
Assigning a value to such a name replaces the value stored in the shoebox with a
new copy of the appropriate value. There is no sharing between shoeboxes.
Instead, there are multiple copies of, say, the int 3. Every shoebox always
contains exactly one thing. When a new value is assigned to a shoebox, any value
previously stored in that shoebox is discarded. So, for example,
i = 3;
6
Some special kinds of names get values by default. We will mention these values as the names
are introduced.
makes the i-shoebox hold 3; the 0 initially stored in the i-shoebox is discarded.
The declaration-plus-assignment definition
int j = i;
creates another int-sized shoebox, j. In this case -- because this is a definition, not
simply a declaration -- j starts out containing a copy of the value that happens to
be in i when the definition is executed. That is, this definition makes a copy of the
value currently in i -- 3 -- and creates a new shoebox, called j, to hold it. Once
this is done, there is no special relationship between i and j.
7
Note, however, that this does not extend to 3 and 3.0 and 3.0f, each of which is a different thing.
This is because each of these has a different type.
the same, they are still two different people. If one gets a haircut, the other's hair
doesn't automatically get shorter. If one takes a bath, the other doesn't get clean. 3,
on the other hand, has no internal structure that can be changed (the way that one
twin's hair can be cut). If you change 3, you don't have 3 any more.
Notice, though, that although 3 is 3 is 3 (i.e., there aren't "different" 3s the way
that there are different twins), there may be different shoeboxes that CONTAIN 3.
If myBox and yourBox are both int-sized shoeboxes, each containing 3, changing
the number in myBox doesn't automatically change the number in yourBox. So
after
int myBox = 3;
int yourBox = 3;
myBox = 5;
yourBox will still contain 3. Each shoebox is separate and (unless we find some
way to actively connect it to another) independent.
The only thing that remains to say about shoebox-type names is how to recognize
one. The rule is quite simple: All names with primitive type are shoebox-type
names.
A more formal term for shoebox types is value type.
not without raising an error. Unlike cornerstones or dog tags, though, labeling a
Java object doesn't actually change that object. It just gives you a convenient way
to identify (or grab hold of) the object.
In Java terms, if we declare
RadioButton myButton;
this creates a label, myButton, that can be stuck onto things of type RadioButton.
Note that is not currently so stuck, though. At the moment, myButton is a label
that isn't stuck to anything. (Cornerstones and badges and dog tags don't come
with buildings and people and dogs attached, either. Having a label is different
from having something to label with it.) Labels don't (necessarily) come into the
world attached to anything. The value of a label not currently stuck onto anything
is the special non-value null. (That is, null doesn't point, or refer, to anything.)
So the declaration above is (in most cases) the same as defining
RadioButton myButton = null;
myButton.isSelected()
If we now declare
RadioButton yourButton = myButton;
a new label is created. This new label is attached to the same object currently
labeled by myButton. Assignments of label-type names do not create new (copies
of) objects. In this case, we have two labels stuck onto exactly the same object,
and we say that the names myButton and yourButton share a reference. This
just like saying that "the morning star" and "the evening star" both refer to the
same heavenly body.
Because myButton and yourButton are two names of the same object, we know
that
myButton.isSelected()
and
yourButton.isSelected()
will be the same: either the button that both names label is pressed, or it isn't. But
we can separate the two labels -- say
myButton = someOtherButton
and
yourButton.isSelected()
Chapter Summary
• Literals are Things you can type directly to Java.
• int is the standard type for integers. Other integer types include
byte, short, and long.
• double is the standard type for real numbers. float is another real
type.
• String is the type for arbitrary text. String is not a primitive type, but
Java does have String literals.
• Object types have label names. Two label names can label the same
object. A label that is not currently stuck on anything is associated with
the non-value null.
Exercises
1. 42
2. -7.343
3. i
4. 'c'
6. b
7. false
8. "false"
9. c
10. 'b'
11. "b"
2. For each of the following definitions, fill in a type that would make the
assignment legal.8
__________ a = 3;
__________ b = true;
__________ c = 3.5;
__________ d = "true";
__________ e = "6";
__________ f = null;
__________ g = 0;
__________ h = '3';
__________ i = '\n';
__________ j = "\n";
8
There are several answers to some of these, but in each case only one "most obvious" type. It is
this "most obvious" type that we are after.
a = b;
which of a, b, c, or myObject is null? (The answer may be none, one, or more than
one.)
f. Assume one more time that myObject is a name bound to an object (i.e.,
myObject is not null). After the following statements are executed in order,
Object e = myObject;
myObject = null;
Now which of e, myObject (or neither, or both) is null?
4. Which of the following could legitimately be used as a name in Java?
3PO
R2D2
c3po
luke
jabba_the_hut
PrincessLeia
Han Solo
obi-wan
foo
int
Double
character
string
goto
elsif
fi
5. Assume that the following declarations have been made:
int i = 3;
int j;
char c = '?';
char d = '\n';
boolean b;
String s = "A literal";
String s2;
Object o;
s2
6. Assume that there is an already-defined object type called Date and that today
is an already-defined Date name with a value representing today's date. Declare a
new name, yesterday, and give it the value currently referred to by today. (This
would be useful, e.g., if it were midnight and we might soon want to update the
value referred to by today.)
Chapter Overview
• How do programs (and people) know what to expect?
©1999 Lynn Andrea Stein. This chapter is excerpted from a draft of Interactive Programming In Java, a
forthcoming textbook from Morgan Kaufmann Publishers. It is an element of the course materials developed
as a part of Lynn Andrea Stein's Rethinking CS101 Project at the MIT AI Lab and the Department of
Electrical Engineering and Computer Science at the Massachusetts Institute of Technology.
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
4~2 Specifying Behavior: Interfaces Chapter 4
doesn't tell you. For example, when you drop a letter off at the post office, you
don't necessarily know whether it's going by truck or by train to its destination.
You may not know when it is going to arrive. This chapter concludes with a
discussion of what isn't specified by an interface and how good documentation
can make some of these other assumptions explicit.
This chapter is supplemented by a reference chart on the syntax and semantics of
Java interfaces.
standard interface, with a particular contract, and as long as you live within the
parameters of that contract, the two sides of the interface can remain relatively
independent.
Of course, there are places where this contract breaks down. US appliances don't
generally work in European outlets, for example. There are several standard
electrical outlet interfaces throughout the world. It isn't clear that one of them is
particularly better than another, but it is unquestionably true that you can't use one
side of the US outlet interface (e.g., a US appliance) with the other side of the
European interface (a 220V outlet). If you want to mix and match disparate
interfaces, you will need a special adapter component. The same is true for
software.
There are also, even in the US, certain appliances that can't use standard wall
outlets. For example, an electric oven draws too much current, and so needs a
special kind of wall outlet. The physical connector -- the plug -- is different on
this appliance, to indicate that it fits a different interface. You can't plug an
electric oven in to a standard US wall outlet. This is because its needs don't meet
the (sometimes implicit) constraints of standard (15 or 25 amp) US circuits.
Sometimes this happens in software, too -- you need a different interface because
the standard one doesn't provide precisely the functionality that you need.
entities shows another: what services it provides, what information it expects. One
entity may, of course, have many interfaces, showing different "faces" to different
community members.
Interface is a piece of the answer to the question of how things interact.
User interface refers to the part of a computer program that the person using the
computer actually interacts with. For example, a graphical user interface (GUI)
is one that uses a certain interaction style, e.g., typically contains buttons and
menus and windows and icons. (Before GUIs, computer interfaces typically used
text, one line at a time, the way that some chat programs work now.) A good user
interface takes into account the properties of the program as well as those of
human users. Not surprisingly, humans and computers have different skill sets.
Like user interfaces, every interface should be designed bearing in mind the needs
of the entities on both sides. We will learn more about graphical user interfaces in
particular in Parts 3 and 4 of this book.
This Computer Science use of the word interface is one sense in which we will
use the term in this book. In Java, there is a second, related but much more limited
use of the word interface. A Java interface refers to a particular formal
specification of objects' behavior. The keyword interface is used to specify the
formal declaration of a particular kind of contract guaranteeing this behavior. (For
example, there might be an interface defining clock-like behavior.) The Java
language defines the rules for setting out that contract, including what can and
can't be specified by it. A particular Java interface is a particular promise.
In this book, when we use the term "Java interface" or the code keyword
interface, we are referring to this formal declaration. When we use the term
"generalized interface", we are referring to the more general computer science
notion of interfaces. A Java interface is one way to (partially) specify a
generalized interface. There may be things that are part of the general promise --
such as how long a particular request might take to answer -- that can't be
specified in a Java interface.
This chapter deals specifically with Java interfaces. The ideas of generalized
interfaces permeate all parts of this book; the generalized notion of an interface is
central to interactive program design. We will explicitly revisit this issue --
generalized interface design -- in the chapters on Protocols and Communication in
Part 4 of the book.
pages, recording the number of visitors. Most such counting objects have a very
simple interface. If you have a counting object, you expect to be able to increment
it -- add one to the number that the counting object keeps track of -- or to be able
to read -- or get -- its current value. This is true pretty much no matter how the
counting object actually works or what other behavior it might provide. In fact, by
this description, a stopwatch might be a special kind of counting object that
automatically increments itself. So we might say that increment and getValue
form a useful interface contract specifying what a (minimal sort of a) counting
object might be. In Java, we write this as:
interface Counting // gives the name of the interface
{
void increment(); // describes the increment contract
Your code should work even if I exchange my original counting object for one
that can be reset before each game or each time I rewrite my web page, since your
code depends only on being able to increment and read the value of my counting
object. In turn, I can go off and build a counting object using whichever internal
representations I wish to provide, so long as I meet the contract's commitments
(increment() and getValue()).
Of course, you may want to know more about my counting object than what the
increment/getValue interface tells you. Some of this information may be
contained in the documentation for Counting. (This counting object's value will
always be non-negative.) Other information may be contained in the
documentation for my particular implementation. (My BasicCounter Counting
object implementation is guaranteed to increase; its value cannot decrease.) If you
want to know whether my clock provides additional services, though, you may
need to use an interface that specifies this additional behavior (e.g., a Resetable
interface). We will discuss the kinds of information conveyed by an interface, and
that which should be included in interface documentation, later in this chapter.
is only that part of the promise that users of the object need to know: what request
to make, what things to give the rule, and what to expect back. The rule body --
how to do the rule -- is only needed by the rule implementor, not by the rule user.
In the particular case of the counting interface, there are two rules that every
counting object must implement: increment and getValue. So the Counting
Java interface would need to specify these two method signatures. Each method
signature has three parts: name, parameter specification, and return type.1
For each of the elements below, we describe both the obligations of the designer
of the interface and the ways in which the interface is used by another entity.
4.2.1 Name
When you are building an interface, a rule can have any name that you want to
give it. It is a good idea to give it a name that will help you (and the users of your
code) remember what it does. Remember the syntax of Java names --
alphanumeric and a few symbolic characters -- and that rule/method names should
start with a lower case letter.
When you are using an interface, the name of the rule is whatever name the
interface says it is. Hopefully, the name was chosen well so that it is easy to
remember and to figure out what that rule does.
1
There is actually one other part of some method signatures, the throws clause. Every method
signature must have a name, parameter list, and return type, but some methods do not have a
throws clause. The throws clause will be introduced in the chapter on Exceptions. In addition,
certain modifiers -- such as abstract, explained below -- may be included in a method
signature.
(including both primitive and object types); the name can be any Java-legal name
that you choose to give the parameter. It is advisable that you give your
parameters names that make it easy for the users and implementors of your rule to
figure out what role the particular parameter plays in the rule. Our convention is
to use names that begin with a lower-case letter for parameters.
The list of parameters is separated by commas: type-of-thing name-of-thing, type-
of-thing name-of-thing, and so on until the last type-of-thing name-of-thing which
doesn't have a comma after it. The whole list is enclosed in parentheses. You can
list your parameters in any order. Of course, some orders will naturally make
more sense than others, and although the choice is arbitrary, once chosen the
order is fixed. This means that users and implementors of the method will need to
follow the order declared in the interface.
The getValue and increment rules of Counting don't have any parameters, i.e.,
they don't need any information to begin operation. Their parameter lists are
empty: () StringTransformer's transform rule needs one parameter, a String.
We can call that String anything we want to. For example, transform's
parameter list might be: ( String whatToTransform ).
type of rules that don't return a value. The Counting interface's increment
method presumably doesn't return anything, so its return type would be void. The
return type of the getValue method is presumably int.
When you use a method, you may or may not want to do something with the
value returned. The return type of the method signature tells you what type of
thing you can expect to get back, e.g., so that you can declare an appropriate name
to store the result:
int counterValue = myCounting.getValue();
where myCounting is something that implements the Counting interface, i.e.,
satisfies the Counting contract (and therefore has an int-returning getValue
method). After this statement, counterValue is a name that refers to whatever
int myCounting's getValue method returned.
chapter.
Since interfaces always specify only method signatures, interface method
declarations are always abstract. If you don't say so explicitly, Java will still act
like the word abstract is there. However, if your method definition does not end
with a semi-colon, your Java interface will not compile.
• Are there any restrictions on the legal values for a particular parameter?
• What may you do with the object returned? What may you not do?
Other questions not included in the method signature:
2
In addition, method signatures may include visibility and other modifiers and any exceptions that
the method may throw.
Not all of these questions are relevant to every method. For example, the precise
amount of time taken by the counting object's getValue method is probably not
important; it is important that it return reasonably quickly, so that the value
returned will reflect the state at the time that the request was made. However, it is
important to recognize that these and other questions are not answered by your
method signatures alone, so you must be careful to document your assumptions
using Java comments.
Style Sidebar
Method Documentation
Documentation for a method should always include the following items:
Why would you want to use this method? What does it do? When is it
appropriate (or not appropriate) to use this method? Are there other
methods that should be used instead (or in addition)? Are there any other
"hidden assumptions" made by this method?
What does each parameter represent? Is it information supplied by the
caller to the method? Is it modified during the execution of the method?
What additional assumptions does the method make about these
parameters?
What does the return value of the method represent? How is it related to the
method's arguments or other Things in the environment? What additional
assumptions may be made about this return value?
What else might be affected by the execution of this method? Is something
printed out? Is another (non-parameter) value modified when it is run?
These non-parameter non-return effects are called side effects.
In addition, if there are other assumptions made by the method -- such as
how long it can take to run or what else can (or cannot) happen at the same
time -- these should be included in the method's documentation.
Java provides additional support for some of these items in its javadoc
utilities. See the appendix on javadoc for details.
4.3.1 Syntax
A Java interface is typically declared in its very own file. The file and the
interfaces generally have the same name, except that the file name ends with
.java. (For example, the Counting interface would be declared in a file called
COunting.java.)
In fact, that was so easy, let's try another interface. This one is Resetable, and it
is a very simple interface. (Good interfaces often are.) Resetable has a single
method:
interface Resetable
{
abstract void reset();
}
This interface is fine, but it could do with a little bit of documentation. After all,
For example, a reset() rule with no parameters (an empty parameter list, () )
has a different footprint from a reset( int newValue ) rule (with the parameter
list (int) ), and both are different from reset( String resetMessage )
(parameter list (String) ). Only the parameter type matters, though, not the
parameter names: reset( String resetMessage ) is the same as reset(
String whatToSay ).
As long as two methods have different footprints, they can share the same name.
This is very common and even has its own name: overloading. Overloading
allows an object to have two (or more) similar methods that do slightly different
things. For example, there are two very similar mathematical rounding methods.
One has the signature
int round( float f );
while the other has the signature
long round( double d );
The Math object has both of these methods, and if you pass Math.round a float,
you get back an int, while if you pass it a double, you get back a long. This is
very convenient -- in both cases, a floating point number is converted to an
integer, but in either case the more appropriate size is used.
An alternate kind of overloading might happen if our hypothetical
AlarmedCounting interface had, in addition to its
void setAlarm( int whatValue, String alarmMessage )
method, a second method that just allowed you to specify the alarm message,
without changing the value for which it was set:
void setAlarm( String alarmMessage )
If you called yourAlarm.setAlarm( 1000, "Capacity reached" ), you'd set
the alarm message to trigger at 1000, printing the message "Capacity reached".
yourAlarm.setAlarm( "Oops, all full" ) might then be used when to
change the warning to be issued when the AlarmedCounting reaches capacity.
Overloading method names is the choice of the interface builder. The interface
user simply makes use of the interface as it is given.
So, for example, the declared type of myCounting, above, was Counting:
Counting myCounting;
In this example, myCounting is declared to be of type Counting, i.e., something
that satisfies the Counting contract (interface) that we declared in the preceding
sections. For example, we might have an interface called Game that includes a
getScoreCounter() method that returns a Counting:
interface Game
{
abstract Counting getScoreCounter();
// maybe some other method signatures....
}
If theWorldCupFinal is a Game, then we might say
Counting myCounting = theWorldCupFinal.getScoreCounter();
In this case, we don't know anything more about the type of myCounting ; we just
know that it is a Counting. Often, as users of other people's code, interfaces are
the only types we need to know about.
Style Sidebar
Interface Documentation
An interface should be properly documented, typically using a multi-line or
javadoc comment immediately preceding its declaration.
Documentation for an interface should include the following information:
What kind of thing does this interface represent? Why would you want to
use an object of this kind? What could it do for you? What could you do
with it?
What kinds of assumptions or conditions does this kind of object need to do
its job? Are there any special objects that it might need to have around or to
work with?
What services does this kind of object provide, and how do you use them?
These questions are typically answered by the individual methods, but a
brief overview of what methods the interface provides is always useful. It is
may also be useful for the interface to document which method(s) to use
when, especially when multiple similar methods exist.
The interface's documentation should make it easy for a potential user to
find the method(s) s/he wants. It should also make it possible for someone
seeking to implement this interface to determine whether s/he has met the
intent as well as the formal specification of the interface. If I am building a
stopwatch, do I want to subscribe to the Clock interface?
Remember that an interface declaration is largely about what, not how. It
specifies contracts and promises, not mechanism.
Java provides additional support for some of these items in its javadoc
utilities. See the appendix on javadoc for details.
Chapter Summary
• An interface is a contract that a particular kind of object promises to keep.
• Every (public) interface must be declared in a Java file with the same
name as the interface.
• One interface may have multiple methods with the same name, as long as
they have different ordered lists of parameter types. Method name plus
ordered parameter type list is called the method's footprint.
Exercises
1. StringTransformer has a transform method. Declare an interface, Transformer,
that contains this single method specification, so that StringTransformer might be
an implementation of this interface.
2. A Clock is an object that needs a method to read the time (say, getTime) and
one to set the time (say setTime). Assuming that you have a type Time already,
write the interface for a Clock.
a. Write a type declaration for the name theWorldCup. Don't worry about
where its value comes from.
b. Write a type declaration
suitable for holding the result of
theWorldCup.getTimeCounter().
c. Write an expression that returns the object that counts player 5's fouls.
Chapter Overview
• How do I use the Things I have to get new (or other) Things?
This chapter and the next introduce the mechanics of executable code, the
building blocks for individual sequences of instruction-following. The previous
chapter's Things each come with a Type, which specifies how that Thing can
interact. An expression is a piece of code that can be evaluated to yield a value
and a type
Simple expressions include literals -- Things that java literally understands as you
write them -- and names, which stand in for the Things they refer to. More
complex expressions are formed by combining other Things according to their
types, or promised interactions.
To understand a complex expression, you must understand its parts (a basic form
of "what goes inside") and how they are combined (a basic "how they interact").
Sometimes, you have to understand this without knowing all of the details of
what's inside.
Sidebars in this chapter cover details of various Java operators, including casts
©1999 Lynn Andrea Stein. This chapter is excerpted from a draft of Interactive Programming In Java, a
forthcoming textbook from Morgan Kaufmann Publishers. It is an element of the course materials developed
as a part of Lynn Andrea Stein's Rethinking CS101 Project at the MIT AI Lab and the Department of
Electrical Engineering and Computer Science at the Massachusetts Institute of Technology.
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
5~2 Expressions: Doing Things With Things Chapter 5
5.1.1 Literals
The very simplest Java expression is a literal: an expression whose value is
interpreted literally, such as 25 or 32e-65 or "How about that?". Java literals
include the various kinds of numbers, characters, Strings, and booleans. For a
more complete enumeration of literal expressions and rules regarding their syntax
(i.e., how you write them), see the sidebar on Java Primitive Types, above.
Every expression has a value and a type, obtained by evaluating the expression.
The value of a literal is its prima facie value, i.e., what it appears to be. The type
of an expression is the type of its value. Integer literals are always of type int
unless an explicit type suffix (l, s, or b) is included in the literal. Non-integral
numeric literals are always of type double unless explicitly specified to be of type
float (using the f suffix).
5.1.2 Names
Names are also Java expressions. A name is only a legitimate expression once it
has been declared, i.e., within its scope.1 The value of that name is the value
currently associated with it -- i.e., stored in the shoebox if it is a shoebox name, or
labeled by it if it is a label name. The type of a name expression is always the type
associated with that name at the time of its definition.2
1
Strictly speaking, the area of text within which a name is legal is called its scope. The scope of a
variable -- a name with no special properties beyond being a name -- begins at its declaration and
extends to the end of the enclosing block. (See blocks, below.) Later, we will see three other kinds
of names: classes, fields and parameters. Class names have scope throughout a program or
package; they may be used anywhere. Field names have scope anywhere in their enclosing class,
including textually prior to their declaration. Parameter names have scope throughout their method
bodies only.
2
Note that the type of a name expression is the declared type of the name rather than the type of
the value associated with the name. That is, even where there is disagreement between the
declared type of a name and its value, the type of a name expression is always its declared type.
This example consists of a String literal expression ( "a test string") and a
request to that object to perform its toUpperCase() method. A String's
toUpperCase() method doesn't require any additional information, so the
parentheses are empty. (They can't be omitted, though!) The value of a String's
toUpperCase() method is a new String that resembles the old one, but contains
no lower case letters. So the value of this expression is the same as the value of
the literal expression "A TEST STRING".
Another example of method invocation is
Console.println( "Hello" )
This asks the object named by the name expression Console to print the line
supplied to it. It requires that a String -- the line to be printed -- be supplied inside
the parentheses. This is "necessary information" is called an argument to the
method.
invoked.
2. Evaluate any argument subexpressions.
3. Evaluate the method invocation by asking the object to perform the
method using the information provided as arguments.
4. The value of the expression is the value returned by the method
invocation. The type of the method invocation expression is the declared
return type of the method invoked.
In order for step 3 to work, the object must know how to perform the method, i.e.,
it must have instructions that can be followed in order to produce the return value
needed in step 4. We have already seen how an interface can describe an object's
commitment to provide such behavior. We will see in the next chapters how this
may be accomplished in detail.
From the perspective of the method invoker, however, the transition from step 3
to step 4 happens by magic (or by the good graces of the object whose method is
invoked). The object offers the service of providing a particular method requiring
certain arguments and returning a value of a particular type. For example, if we
look at the documentation (or code) for String, we will see that it has a
toUpperCase() method that requires no arguments and returns something of type
String. The println method of Console requires a String as an argument, and
println's return type is void. We will learn more about the methods that objects
provide the chapters on Classes and Objects and Designing with Objects.
In general, since every expression has a type, you can use the expression
wherever a value of that type would be appropriate. The exception to this rule
about reuse of expressions is that some expressions are constant -- their value is
fixed -- while other expressions are not. Some contexts require a constant
expression. In these cases, you cannot use a non-constant expression of the same
type. (For example, "to"+"get"+"her" is a constant expression, but
3
str+"ether" is (in general) not, even if str happens to have the value "tog". )
There are a few places where Java requires a constant value. These will be noted
when they arise.
The evaluation rule for a compound expression is essentially the same as the
evaluation rules for the expressions that make it up: Evaluate the subexpressions
that make up this expression, then combine the values of these subexpressions
according to the evaluation rule for this expression. For example, when we
evaluate "serendipitous".toUpperCase(), we are actually evaluating the
simpler (literal) expression "serendipitous", then evaluating the method
invocation expression involving "serendipitous"'s toUpperCase() method.
Similarly, str + "ether" evaluates the (name) expression str and the (literal)
expression "ether", and then combine these values using the rules for +
expressions, detailed below. In this case, str and "ether" are subexpressions of
str + "ether". There are two additional details: 1) Evaluating the
subexpressions may itself involve several evaluations, depending on how complex
these expressions are and 2) it may not always be clear which operation should be
performed first.
Method invocation, like other expressions, can be used to form increasingly
complex expressions. For example, we can combine two method invocations we
used above to cause the value of "A TEST STRING" to appear on the user's screen:
Console.println( "a test string".toUpperCase() )
or
Console.readln().toUpperCase()
3
The expression str+"ether" would be constant if str were declared final, though. Names
declared to be final cannot be assigned new values.
is int, because the type of 4563129 is int, and the value is 4563129 for the same
reason.
Note that we must have declared myNumber before we get to this expression; and
that this expression is legitimate if myNumber has type int, long, float, or
double. Note, also, that if myNumber were already declared, we wouldn't want to
declare it again. Every time that you declare a name, it creates a brand new
shoebox or label with that name.
Although assignments are expressions in Java, they are not generally used for the
resulting value. Instead, an assignment statement is generally used because it will
cause the shoebox or label on its left-hand side to be associated with a new value.
This effect is not a part of the value of the expression; instead, it happens "on the
side" and is called a side effect. Assignment statements are among the most
common expressions used for their side effects, but we will see several other
expressions with important side effects in the remainder of this chapter.
Style Sidebar
5.5.1 Fields
In addition to methods, objects sometimes have fields: data members that behave
as names. That is, fields are either shoeboxes or labels. Like methods, fields are
also accessed using the dot syntax, but without following parentheses. A field
access expression is essentially a name expression, though a more complex one
than the simple names described above. The value of a field access expression is,
as for a simple name, the value associated with the shoebox or label. So, for
example, Math.PI is a double shoebox, belonging to an object called Math,
containing a value approximating a real number whose most significant digits are
3.14159.
name for the Dimension and assigning it the result of the method invocation:
Dimension mySize = myWindow.getSize();
and then asking the newly named Dimension mySize for its height field.
Because field access expressions are actually name expressions, they also have
special behavior in the specific context of the target of an assignment statement.
That is, you can assign to a field access expression just as you would to a simple
name, and the field access expression behaves like the shoebox or label to which
it refers. For example, if height is an int shoebox owned by mySize, the
expression
mySize.height = mySize.height / 2
halves the value contained in the height shoebox of mySize, which might shrink
mySize vertically by half.
creates a new File object with external (outside of Java) name myData. Like all
other expressions, this one has a type -- ClassName, the kind of object created, in
this case File -- and a value -- the new object created. The new expression is
typically used inside an assignment or method invocation.
The rules of evaluation for creation expressions are similar to the rules of
evaluation for method invocation. The return value is always a new instance of
the type (or class) whose instance creation expression is invoked (in this case,
File). The return type is always the type whose instance creation is invoked.
The first operand, which precedes the keyword instanceof, can be any
expression whose value is of any object (non-primitive) type. The second
operand, which follows the keyword instanceof, must be the name of an object
type. As we shall see in the next few chapters, this name may be the name of any
class or any interface.
The instanceof operator is used to determine whether it is appropriate to treat its
first operand according to the rules of the type named by its second operand. (For
example, is it appropriate to cast the object to this type?) The value of an
instanceof expression is a boolean, true if it is appropriate to treat the object
according to this type, false otherwise. So, for example,
"a String" instanceof String
has the value true (because "a String" is a (literal) instance of the type String),
while
new Object() instanceof String
has the value false (because the new Object created by the instance creation
expression new Object() is not a String.
y. The + operator can be used to combine any two numeric types. The two things
combined with the operator are called the operands. In the expression x + y, + is
the operator and x and y are the operands. Some operators take two operands.
These are called binary operations. Other operators take only one operand; these
are the unary operations. One operator -- ?: -- takes three operands.
Java Operators
Java operators include
+ - * / | & ^ % << >> >>>
+= -= *= /= |= &= ^= %= <<= >>= >>>=
< > <= >= == != ! && || ++ --
= ?:
The operators in the first row are, respectively, addition, subtraction,
multiplication, division, bitwise or, bitwise and, bitwise negation, modulus,
left-shift, sign-extended right-shift, and zero-extended right-shift. Addition
is also used for String concatenation when at least one of its arguments is a
String. Subtraction can also be used as unary (one-argument) negation.
The operators in the second row combine their correlate in the first with an
assignment operation. Thus x += 2 is the same as x = x + 2; the
difference is that the left-hand side of the combined operator is evaluated
only once. The value of an operator assignment expression is the new value
of the left-hand side; the type is the type of the left-hand side. All
assignment expressions modify the name that is their left-hand side.
The third row above begins with six comparisons, each of which returns a
boolean. The final comparison is not-equal. These are followed by logical
negation, logical conjunction (and), and logical disjunction (or). Each of
these takes boolean arguments, one in the case of negation, two in the case
of conjunction and disjunction, and returns a boolean.
The final operators in the third row are autoincrement and autodecrement.
These can be used as either prefix or postfix operators. Both ++x and x++
modify x, leaving it incremented. However, ++x returns the incremented
value of x, while x++ returns the unincremented value. The -- operator
works similarly.
The final two operators are simple assignment (which works like the
compound assignments, above) and the ternary (three-operand) expression
conditional.
Arithmetic Expressions
Arithmetic expressions include the binary operators for addition (+),
subtraction (-), multiplication (*), division (/), and the modulus or
remainder operation (%). In addition, there are two unary arithmetic
operators, + and -.
Arithmetic operations work only with values of type int, long, float, or
double. When a (unary or binary) arithmetic expression is invoked with a
value of type short, byte, or char, Java automatically widens that operand
to int (or to a wider type if the other operand so requires). For further
details on widening, see the sidebar on Coercion and Casting.
When the operands of a binary arithmetic expression are of the same type,
the complete expression also has that type, except that no binary arithmetic
expression has type short, byte, or char. This is because operands of
these types are automatically widened.
When the operands are of different types, Java will automatically widens
one to the other.
For example, if myInt is a name of type int with value 7 (e.g., int myInt
= 7;), then
(long) myInt
4
There is one instance in which Java performs a narrowing but non-lossy coercion automatically.
This is in the case of a sufficiently small int constant assigned to a narrower integer type. This
allows literals-- which would otherwise have type int -- to be assigned to names with byte and
short type: short smallNumber = 32;
is an expression with type long and value 7. (Note that myInt still has type
int. Casting, like implicit coercion, does not actually modify the castee.)
Explicit coercion allows both widening and narrowing coercions: you can
cast an int to long, as in the example above, or to short -- a cast that may
lose information. Certain casts may be illegal and will cause (compile- or
run-time) errors or exceptions.
expression true || false is true. While this is not very interesting by itself,
these boolean operators can be used with names (of type boolean, of course) or in
complex expressions to great effect. For example, rainy || snowy might be a
reasonable way to express bad weather; it will (presumably) have the value true
exactly when it is precipitating. There is also a unary boolean negation operator:
!. The Java fragment
*, /, %
+, -
==, !=
&
&&
||
?:
or
total = total + 1
(The second uses the fact that total + 1 is an expression with type int
and value one greater than total to form an assignment expression whose
second operand is an arithmetic expression.) This last expression -- adding
to a name -- is pretty common, and so it has a convenient shorthand:
total += 1
is similar to total = total + 1 (or total += 1), but it has the value of
total before the assignment. The prefix autoincrement expression
++total
also adds one to total, but has the value of total after the assignment.
(Remember: ++var first increments, then produces a value; var++ produces
the value first.) The two (prefix and postfix) autodecrement operators work
similarly.
Chapter Summary
• Every expression has both a type and a value.
• Explicit cast expressions have the type of the cast operation and
the same value as the cast operand.
Exercises
1. In Java, every expression has a type. Assume that the following declarations
apply:
int i, j, c;
double d;
short s;
long l;
float f;
boolean b;
For each expression below, if it is syntactically legal Java, indicate its type (not its
value). If it is not syntactically valid, indicate why.
1. 6
2. 24L
3. +3.5
4. 3.5f
5. 2e-16
6. -25b
7. i
8. i+3
9. i+3.0
10. i+s
11. l+d
12. f+s
13. i / 0
14. 4 * 3.2
15. i = 0
16. i == 0
17. b = 0
18. b == 0
19. 'c'
22. "6" + 3
23. !b
24. !i
25. b || true
26. i += s
27. s += i
28. i += f
29. l = i = s
30. i = l += s
31. l++
32. (long) s
33. s
34. (short) l
35. l
2. Give examples of three expressions with side effects.
3. What is the value of each of the following expressions? Which ones produce
errors in evaluation? You may wish to consult the chart on operator precedence.
Assume that i is an already-defined name for an int and that b is a boolean.
1. 2.0 + 3.5 * 7
2. ("top " + "to " + "bottom" ).toUpperCase()
3. "the answer is " + 6 * 7
4. 4 + 6 + " is " + 10
5. i > 0 && i < 100
6. b = i < 0
7. ! (i == 0) && 100 / i
4. Give examples of each of the following:
1. An expression whose type is int and whose value is more than a
previously defined int, x.
2. An expression whose type is boolean and whose value is true when
x is between 5 and 15.
3. An expression whose type is double and whose value is half of x's,
where x is the aforementioned int.
4. An expression whose type is long and whose value is the
remainder when x is divided by 7.
5. An expression whose type is boolean and whose value is the
opposite of a previously defined boolean, b.
6. An expression whose type is boolean and whose value is true
exactly when the int x is evenly divisible by 5.
7. An expression whose type is String and whose value is read from
the user's keyboard.
Chapter Summary
• How do I tell the computer how to do something?
This chapter introduces statements, the simplest forms of complete executable
instructions. Statements are fragments of Java code that have neither value nor
type; instead, they have effects. Statements can be are combined to form rules, or
services that one object can provide to another. Statements and rules form the
backbone of the peanut-butter and jelly model of programming.
Statements can be built out of expressions. However, unlike expressions, which
have both type and value, statements are used for their effect -- to get something
done. Examples of this are asking a thing to do something or assigning a name to
keep track of a value. In addition to declarations, assignments, and method
invocation, this chapter introduces simple control flow statements. More
advanced statement types are introduced later in the book.
The chapter ends with a discussion of methods, the rules implementing behavior.
Method invocation provides the basis for virtually all inter-object interaction.
This chapter is supplemented by a reference chart on the syntax and semantics of
©1999 Lynn Andrea Stein. This chapter is excerpted from a draft of Interactive Programming In Java, a
forthcoming textbook from Morgan Kaufmann Publishers. It is an element of the course materials developed
as a part of Lynn Andrea Stein's Rethinking CS101 Project at the MIT AI Lab and the Department of
Electrical Engineering and Computer Science at the Massachusetts Institute of Technology.
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
6~2 Statements and Rules Chapter 6
java statements.
1
These expressions may find use in other, more complex statements, though.
effecting, although not every method invocation actually has a side effect.
Instance creations -- new expressions -- are also side-effecting.
So, for example, a simple assignment statement can be made by adding a
semicolon to the end of the assignment expression x = 3
x = 3;
The semicolon turns this into a statement. It no longer has a value or a type; it just
does its work.
To execute an expression-semicolon statement, simply evaluate the expression.
Of course, this expression may have complicated subexpressions that must be
evaluated according to the rules described in the previous chapter. Since the
expression is a side-effecting one, something will happen -- an effect will be
produced -- during the evaluation.
After executing a side-effecting-expression-plus-semicolon statement, execution
proceeds at the following statement.
int j;
int k;
and
Object thingOne; Object thingTwo;
respectively.
Style Sidebar
2
It is, however, legal to assign a label-name local variable the special non-value null. Assigning
null to a name means that the name doesn't refer to anything. Not assigning forces the computer
to guess. The rule is that you just can't leave the computer to guess.
declaration (making it a definition), or you can assign it before the first time that
you try to use the name's associated value.
A variant on a declaration statement is a definition. A definition is a declaration
statement with = expr between the name-of-thing and the semicolon (or
comma). This statement declares the name, but it also assigns it the value of expr.
For example:
int i = 2;
String who = "Pat";
double pi = 3.14159,
ninetyDegrees = pi / 2;
Note that the final statement here assigns the value 1.570795 to the name
ninetyDegrees. First 3.14159 is put into the shoebox named pi. Next, the
expression pi / 2 is evaluated: its value is the value inside the pi shoebox
divided by 2. Finally, this value is assigned to (stored in) the (newly created)
shoebox named ninetyDegrees.
It is legal to mix declarations and definitions in a single statement -- assigning
initial values to only some of the names -- but this can make your code hard to
read. It is usually better to use multiple statements in this case.
Executing a declaration statement creates a shoebox or label associated with the
name declared. Executing a definition is the same as declaring a name, plus
immediately afterwards executing an assignment statement. Note that this
assignment is an expression and may have subexpressions, causing a significant
amount of evaluation before execution is complete.
After executing a declaration or definition statement, execution proceeds at the
immediately following statement.
your code. Some other statements -- such as if, described below -- are often used
together with blocks.
Any statement can be used at any point inside a block. In particular, declarations
and definitions may appear anywhere in a block. This is useful as it allows you to
declare a name immediately before you need it. Doing so makes it easier to read
your code as the reader is less likely to have forgotten what you mean by that
name.
Blocks also have implications for scoping of names: a variable has scope (its
name can be used) from the point in the code where it is declared until the end of
the first enclosing block.3 So if we declare a name at the top of the block, it has
scope for the whole block, as i does in the example above. But j is not declared
until after the call to println, so the definition of i and the call to println are
outside of j's scope:
{ _
int i = 3; |
Console.println( "i_is " + i ); |
int j = i + 1; | scope of j | scope of i
j = i + 5; | |
} X X
This means, for example, that it would be illegal to use j in i's definition:
{
int i = j; // illegal use of j outside its scope!
Console.println( "i is " + i );
int j = i + 1;
j = i + 5;
}
Beware: The scope of a local variable only persists until the end of the enclosing
block. This means that a local variable must be declared at the same level as (or at
a level enclosing) each of its uses.
{
{
// A variable declared here...
String name;
}
// ...is invisible here, making this reference
name = "Pat";
// illegal!
3
Remember, not all names are variables. We will learn more about parameters and fields in
subsequent chapters. Type names have scope everywhere that they are visible.
}
// ...and so on.
The rules for executing a block statement are: execute each substatement in turn,
from the top (beginning) of the block to the bottom (end) of the block.
After a block, execution continues at the next statement.
Style Sidebar
Formatting Blocks
The open brace of a block should generally appear on its own line. If the
block is part of a compound statement (such as an if), its opening brace can
appear as the last character on a line. However, studies have found code
using this convention harder for programmers to scan than code in which
the open brace appears alone on a line.
Text within a block should always be indented (typically by two or four
characters). This makes the left-hand margin of code in a block line up. The
text -- but not the braces -- of an interior block is indented further; the
original indent is resumed when the interior block is closed, i.e., after the
closing brace.
The closing brace of a block should always begin its own line. If the
closing brace completes the statement, as in a simple block, it should
appear alone on that line.
// Some statements...
{
// Statements in a block
// all line up.
{
// Interior block statements
// are indented further.
}
// Close brace exits the block
// and restores earlier indent.
}
// ...and so on.
4
There are other kinds of statements that can appear in place of this block, but in this book we will
restrict ourselves to the cases in which the if body is a block.
block is skipped.
In either case, execution proceeds at the next statement following the if's body.
The if statement, as defined, is very useful when you want to do something or
skip it. But often you want to do one of two things. We can express this using two
if statements with inverse conditions:
if ( theLight.isOn() )
{
theRoom.isLit = true;
}
if ( ! (theLight.isOn() ) )
{
theRoom.isLit = false;
}
This is poor code in three ways. The first is that it invokes the same method --
theLight.isOn() -- twice, but the code would not work as we want if the value
returned were different in the two invocations. (Imagine that the light were off the
first time you asked and on the second time. The value of theRoom.isLit would
never get set!)
We could fix this problem by temporarily assigning this value to a boolean name,
and then testing the name twice:
boolean itIsLight = theLight.isOn();
if ( itIsLight )
{
theRoom.isLit = true;
}
if ( ! itIsLight )
{
theRoom.isLit = false;
}
But this makes a second problem with the code even more apparent. This code is
testing a boolean expression (theLight.isOn() or itIsLight, depending on
which version) in order to set another boolean expression. It would be cleaner just
to write
theRoom.isLit = theLight.isOn();
This statement is equivalent to the whole previous example (using itIsLight),
and much easier to read. For more on this stylistic point, see the sidebar on Using
Booleans.
Of course, we can write other code that's not subject to these two problems. For
example, we could use this idea to write code to compute absolute value of a
given int, x.
int absValue;
if ( x > 0 )
{
absValue = x;
}
if ( x < 0 )
{
absValue = - x;
}
if ( x == 0 )
{
absValue = 0;
}
This code has neither of the previous problems -- x doesn't change, so we can test
it repeatedly, and the value assigned is an int, not a boolean, so we can't write the
shorter assignment statement. But this code doesn't make it clear that these are
really three cases of the same test. There is a form of an if statement that allows us
to make this clearer. It uses the Java keyword else to denote a situation in which
we know that these conditions are mutually exclusive, i.e., at most one of them
can hold.
So, for example, we could rewrite our light-tester (verbosely) as:
boolean itIsLight = theLight.isOn();
if ( itIsLight )
{
theRoom.isLit = true;
}
else
{
theRoom.isLit = false;
}
This still isn't as nice as the one-line version, but it gives us the opportunity to
illustrate control flow in an if/else statement. To execute an if/else statement:
3. Else (the value of the condition statement is false, so) execute the else
body block. An else body is sometimes called an alternative.
4. Execution continues at the following statement.
Since there might be more than two mutually exclusive conditions -- as in the
absolute value code -- else is allowed to have its own condition. An else with a
condition is like an if, except that you only execute that part of the statement if all
previous conditions in this if/else statement have been false. An else with no
condition is always executed if no previous condition in this if/else statement has
been true.
if ( x > 0 )
{
absValue = x;
}
else if ( x < 0 )
{
absValue = - x;
}
else
{
absValue = 0;
}
Note that this is all one statement, not three as in the previous version. Exactly
one of the assignment statements will be executed, no matter what the value of x
at the beginning of the if statement.
Even now, this is not the most elegant absolute value code we could write; for
example, the final case is redundant and could be folded into the first case using
>= instead of >. It does, however, illustrate the syntax of cascaded ifs. We will
return to examine if statements, and other conditionals, in the chapter on
Dispatch.
Style Sidebar
Using Booleans
There are only two boolean values, true and false. There can be lots of
boolean labels, but each label is attached to either true or false; there is
nothing else. This means that testing whether a boolean is the same as
true --
(boolVal == true)
-- is redundant. You can just use boolVal, since it's either true or false.
Similarly, you don't need to use an if statement to test a boolean if you're
generating a boolean value. For example,
if (boolVal) {
return true;
} else {
return false;
}
is also redundant: just return boolVal;. The same thing applies if you're
assigning to a variable instead of returning: otherBoolVal = boolVal;
(or otherBoolVal = ! boolVal; if you want to reverse its sense).
}
This loop prints the numbers from 1 to 99. (Why doesn't it print 100?)
Another use is for a loop that keeps going essentially forever. (It will stop when
something stops the program, but not before:
while ( true )
{
myOutput.writeOutput( myInput.readInput() );
}
This loop continually passes whatever input it gets to its output. Since the value of
true doesn't change, this loop won't end until something nasty happens to it.
Writing loops like this one -- that go on essentially forever -- is much easier than
writing loops like the counting loop, above, because in the counting loop you
have to keep track of what's true each time you go around the loop. For example,
the value of i when you exit the loop above will always be one more than the last
value printed.
Here's an even more tricky one:
while ( x < 25 )
{
x = x + 3;
x = x - 2;
}
If x's value is 20 when we reach the beginning of this loop, what will its value be
when we exit? Remember that the test expression is only checked at the beginning
of each pass through the loop, not in the middle.
There is another looping construct in Java, called do/while statement or just a do
loop. It is much like the while loop, except that the loop body is always executed
once before the condition is tested:
int i = 1;
do
{
Console.println( "I'm up to " + i );
i = i + 1;
} while ( i < 100 )
As with a while loop, once the loop exits, execution proceeds at the statement
following the entire do statement.
The first step in method invocation is parameter binding. In this step, each
parameter name (title, firstName, and lastName) is treated as though it were
newly declared and it is given the value of the corresponding argument. (Recall
that parameters are the names in the method declaration, while arguments are the
values supplied in the method invocation expression.) In order for this to work,
each value must be assignable to the corresponding parameter's declared type.
After parameter binding, method invocation proceeds as though the method body
were a simple block. The block is, however, within the scope of the parameter
bindings, so that inside the block the parameter names can be used to refer to the
provided argument values. For example, in the body of the printFormLetter, title
is bound to "Prof", firstName is bound to "Pat", and lastName is bound to
"Smith".
Now the body statements are executed in turn. In this case, the first statement is
an if, so its test expression is evaluated to determine whether to execute the
consequent block or the alternative block. When the test expression
title != null
6.6.2 Return
This special statement can only be used inside method bodies. It is used to
terminate the execution of the method body. It is also what is responsible for
making a method body -- which is essentially a block statement -- return a value -
- which is a necessary property of a method invocation expression (unless the
method's return type is void).
The need for this statement arises when the sequence of instructions that you are
writing is turned into a method body. In this case, you need to say what the
method returns. This return value becomes the value produced by evaluating a
method invocation expression. This is accomplished using a return statement.
The syntax of a return statement is
return expression;
where expression can be any arbitrary Java expression. Remember: the return
statement -- a statement -- does not have a value, but the method invocation - an
expression -- does.
To execute a return statement, evaluate the expression. Then, exit the enclosing
method, providing the value of the expression as the return value of the method
invocation expression. Exiting the enclosing method means both exiting from the
block that is the method body and also exiting the scope of the
parameter/argument bindings.
After a return statement, execution proceeds at the method invocation whose
method body contained the return statement; evaluation of this expression is
complete (with its value the value supplied by the return statement) and execution
of the statement containing the method invocation continues.
Chapter Summary
• Statements combine expressions to produce useful behavior.
Exercises
1. Using Java's if statement, write instructions for determining which team returns
an out-of-bounds ball to play in a soccer game. In soccer, the team that did not
last touch the ball receives possession of the ball and returns it to play.
a. You may presume that you have a method, lastTouch(), that returns either
homeTeam or visitTeam, and that the goal of your code is to assign the correct
team value (either homeTeam or visitTeam) to the already-defined name
possessingTeam.
5
If the ball has exited via the side line, the return is by side throw. If the ball exits via the home
end line and is last touched by the home team, the visitors return the ball to play by means of a
corner kick. A ball that is pushed beyond the home end line by the visiting team is returned by the
home team via a goal kick. The situation at the visitor's end line is the opposite.
4. Which of the following are expressions, which statements, and which illegal?
For the expressions, indicate the type and value. For the statements, indicate the
effect (if known) and the execution sequence. You may assume that x is an int, b a
boolean.
a. int x = 5
b. boolean b;
c. x + 3
d. x = x + 3
e. x = x + 3;
f. x == 3
g. x == 3;
h. b = x == 3;
i. {
Console.print( "What is your name? " );
String name = Console.readln();
String cap = name.toUpperCase();
}
5. What will the value of d be after each of the following statements? Also,
indicate any other changes that may occur as a result of executing the statement.
You may assume that they are executed in the order given.
a. double d = 3.5;
b. d = d * 3;
c. if ( d < 8 )
{
Console.println( "d is pretty small" );
}
d. d = 2.0
e. while ( d < 30 )
{
d = d * 2;
}
Chapter Overview
• How do I group together related rules?
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
7~2 Building New Things: Classes and Objects Chapter 7
fields, and they are special java names that are always a part of any object made
from this recipe.
When you actually use your class (recipe) to create a new object, there may be
things that you need to do to get it started off right. These startup instructions are
called a constructor.
When you are building an object, you are bound by the interfaces it promises to
meet. If the interface promises a behavior, you have to provide a rule (method)
body for the object to use.
This chapter is supplemented by reference charts on the syntax and semantics of
Java classes, methods, and fields. It includes style sidebars on good
documentation practice.
Most of the syntax of this section is covered in the appendix Java Charts.
is. Like an interface, a class defines a Java type. However, interfaces specify only
contracts; classes also specify implementation. Class methods are full-fledged
rules, with bodies telling how to accomplish the task of that rule (not just the rule
specification, or method signature, of abstract interface methods). Classes also
talk about data -- information to be kept track of by objects -- as well as methods,
or behavior. And a special part of a class -- the constructor -- talks about how to
go about creating an object of the type specified by that class.
The two objects labeled by the names yesCheckBox and noCheckBox are
instances of the class CheckBox. That is, they are particular CheckBoxes. The
instructions for how to create -- or be -- a CheckBox, on the other hand, aren't a
CheckBox at all; the instructions are instructions, or a class. In fact, the
instructions are an object, too, though a very different kind of object and not one
as obviously useful as a CheckBox or a Timer or a Counter. The kind of object
the instructions are is called a Class.
Because the class contains the instructions for how to make a new instance and
for how to behave like an instance of that class, we sometimes say that a class is
like a factory where instances are made. Both a factory and its product are
objects, but factories and the widgets that they make are very different kinds of
objects. The factory has all of the know-how about its instances. But the factory
isn't one of its instances, just as the class CheckBox isn't a CheckBox. It's a
factory!
true.
Style Sidebar
Class Declaration
It is conventional to declare the members of a class in the following order:
• non-static fields
• constructors
• methods
This order is not necessary -- any class member can refer to any other class
member, even if it is declared later -- but it makes your code easier to read
and understand.
All non-private members of the class should be listed in the class's
documentation.
constructors -- are called the class's members.1 Each member is declared inside
the body of the class, but not inside any other structure within the class. Another
way of saying this is that each member is declared at top level within the class. So
members are all and only those things declared at top level within a class.
For example, each instance of Java's Rectangle class has a set of four
coordinates describing the rectangle's position and extent, as well as methods
including one which tells whether a particular x, y pair is inside the Rectangle.
...class Rectangle {
...
int height;
int width;
int x;
int y;
...
...inside(...)...
}
In this case, height, width, x, y, and inside are all members of the Rectangle
class.
1
Be careful not to confuse members, which are parts of the class, with instances, which are objects
made from the class. If chocolate chip cookies are instances of the cookie class (recipe), the
chocolate chips are members of the class.
We will return to each of the elements of this declaration later in this chapter.
For example, a ScoreCounter class might meet the contract specified by the
Counting interface:
interface Counting
{
abstract void increment();
abstract int getValue();
}
So might a Stopwatch, although it might have a totally different internal
representation. Both would have increment() and getValue() methods, but the
bodies of these methods might look quite different. For example, a ScoreCounter
for a basketball game might implement increment() so that it counts by 2 points
each time, while a Stopwatch might call its own increment() method even if no
one else does.
A class that implements a particular interface must declare this explicitly:
class ScoreCounter implements Counting {
....
}
Many objects have properties such as these: information called state or data that
each instance of a class needs to keep track of. This kind of information is stored
in parts of the object called fields. A field is simply a name that is a part of an
object. For the most common kind of field, each instance of a class is born with its
own copy of the field -- its own label or shoebox, depending on the type of name
the field is.
Declaring a field looks just like an ordinary name declaration or definition
(depending on whether the field is explicitly initialized). Such a declaration is a
field declaration if it takes place at top level in the class, i.e., if it is a class
member. (A local variable declared inside a method body or other block is not at
top level in the class.)
These fields are declared here, but not initialized: none of these fields is explicitly
assigned a value. Fields, unlike variables, are initialized by default. If you don't
give a field a value explicitly, it will have a default value determined by its type.
For example, int fields have a default value of 0. Contrast int local variables,
which don't have a default value and cannot be used until they are initialized. For
details on the default values for each type, see the sidebar on Default
Initialization.
Fields with type char default to the value of the character with ascii and
unicode code 0 -- '\u000'. This character is sometimes called the null
character, but should not be confused with the special Java value null, the
non-pointer.
Fields with boolean type are by default assigned the value false.
variable goes away. (A similar variable will come into existence the next time the
method is invoked, but any information stored in the variable during the previous
method invocation is lost.)
The storage locker story is actually somewhat more complex than that, and so is
the field story. It might be useful for someone else to have a key to my storage
locker, and it is possible for that person to go to Austin and change what's in the
locker. So if I share this locker with someone else, what I leave there might not be
what I find when I return. It is important to understand that this is still not the
same as the hotel room. Between my visits, the hotel cleans out the room. If I
leave something in my hotel room, it won't be there the next time I come back.
Each time, my hotel room starts out "like new". In contrast, the contents of my
storage locker might change, but that is because my locker partner might change
it, not because I get a freshly cleaned locker each time that I visit.
The locker partner story corresponds closely to something that can happen with
fields. It is possible for the value of a field to change between invocations of the
owning object's methods, essentially through the same mechanism (sharing) as the
storage locker. To minimize this (when it is not desired), fields are typically
declared private. For more on this matter, see the discussion of Public and
Private in the next chapter. We will return to the issue of shared state (e.g. when
two or more people have access to the same airport locker) in the chapter on
Synchronization.
In Java, the way to say "myself" is this. That is, this is a special name
expression that is always bound to the current object, the object inside whose code
the name this appears. That means that the way to say "my own width field...."
is this.width. (Note the period between this and width -- it is important!)
2
The column for Class or Interface Name refers only to top-level (non-inner) classes or interfaces.
The scope and lifetime of an inner class is determined by the context of its declaration.
3
static. For example:
class Widget {
static int numInstances;
...
}
In this case, individual Widgets do not have numInstances fields. There is only
one numInstances field, and it belongs to the factory, not the Widgets. To access
it, you would say Widget.numInstances. In this case, this.numInstances is
not legal code anywhere within the Widget class.
Style Sidebar
Field Documentation
In documenting a field, you need to indicate what that field represents
conceptually to the object of which it is a part. In addition, you should
answer these questions as appropriate:
• What other values are interdependent with this one? For example,
must this field's value always be updated in concert with another field,
or must its value remain somehow consistent with another field?
• Are there any "special" values of this field that carry hidden meaning?
• What methods (or constructors) modify this field? Which read this
field? What else relies on its value?
3
The choice of the keyword static, while understandable in a historic context, strikes us as an
unfortunate one as the common associations with the term don't really accord with its usage here.
In Java, static means "belonging to the class object."
7.4 Methods
In a previous chapter, we saw how method signatures describe the name,
parameters, and return type of a method. A method signature declared in an
interface ends in a semi-colon; this method specifies a contract, but doesn't say
anything about how it works. It is essentially a rule specification. This kind of
method -- a specification without an implementation -- is called abstract.
Classes specify more than just a contract. Classes also specify how their instances
work. In order for an instance to do be able to do something, its class must give
more than the rule specification for its methods. An instance needs the rule body
for its methods. Classes must supply bodies for any methods promised by the
interfaces that they implement. They may also supply additional methods with
their own signatures and bodies.
Methods can be identified by the fact that a method name is always followed by
an open parenthesis. (There may then be some arguments or parameters, on which
more below; there will always be a matching close parenthesis as well.)
doesn't matter what other names they might have had outside of the method body,
or what else those parameter names might refer to outside the method body. We'll
return to the issue of scoping later.
Recall from previous chapters that the method definition as we've described it so
far -- the return type and the parameter list -- is also called the signature of the
method. It tells you what types of arguments need to be supplied when the method
is called -- it must be possible to assign a value of the argument type to a variable
of the parameter type -- and what type of thing will be returned when the method
is invoked. It doesn't tell you much about the relationships between the method's
inputs and its outputs, though. (The method's documentation ought to do that!)
Style Sidebar
inputs (if any) to its output (if any), or what else should happen in between.
The body of a method is inside the scope of its parameters. That is, the parameter
names may be used anywhere within the method to refer to the corresponding
arguments supplied at method invocation time. The body of an instance method is
also within the scope of the special name this. Just as in fields, inside a method
the name this refers to the particular instance whose method this is. Static
methods -- methods belonging to the class -- are not within the scope of this,
though. That is, you can't use the special name expression this in a static method.
In order to return a value from a method, you use a special statement: return.
There are actually two forms of this statement: return(...); returns a value
(whatever is in the parentheses) from a method invocation. For example,
return (total + 1);
returns one more than the value of total, though it doesn't change the value of
total at all. The parentheses around the expression whose value is to be returned
are in fact optional, leading to the second form of return: return; is used to exit
from a method whose return type is void, i.e., that does not return anything.
Remember (from the chapter on Expressions) that a method invocation is an
expression whose type is the return type of the method and whose value is the
value returned by the method. You make this happen (when you're describing the
method rule) by using an explicit return statement in a method's body. In the
chapter on Statements, we saw the execution rule for a method body and how it
relates to the evaluation rule for method invocation. This process is summarized
in the sidebar on Method Invocation and Execution.
Each time that you refer to a method, you should ask yourself whose method it is.
You can invoke a method by first referring to the object, then typing a period,
then the method name, as in myScoreCounter.getValue(). Sometimes, the
answer to "whose method is it?" will be "my own", that is, the method belongs to
the object whose code is being executed. As with fields, the way to say "myself"
is with the special name expression this, so the way to say "my getValue()
method" is this.getValue(). (Note the period between this and getValue() --
it is important!)
Generally, methods belong to instances of the class in which they're defined.
Occasionally, though, it may be useful to have a method that belongs to the class
itself. This corresponds to a property of the factory (or recipe), rather than one
belonging to the widgets (or cookies) produced. For example, a method that prints
out the number of widgets produced by the factory so far would be a method
belonging to the factory, not one belonging to any particular widget. Methods that
belong to the class instead of to its instances look just like regular methods,
except that they are prefaced with the keyword static. (This name is pretty
unintuitive, though it makes some sense in its historical context. Remember: In
Java, static means "belonging to the class/factory/recipe itself, not to its
instances.") A static method can be addressed by first citing the object it belongs
to, then period, then the method name: Widget.howManyWidgets(). A static
method should not be invoked using this, though, because it doesn't belong to an
instance.
Inside the method body, the name this may be treated as any other name.
it is also possible to refer to the object whose method it is as (for example, if you
want to pass it as an argument to another method).
the arguments supplied is the one that is invoked. This matching is done using the
same type inclusion rules as the operator instanceof.
• If, at any point within the execution of the body, a return statement is
encountered, its expression (if present) is evaluated and then the entire
method body and the scope of parameter names and this are exited
upon completion of the return statement.
• If the method has a return type other than void, the return statement is
mandatory and must include an expression whose type is consistent
with the return type. A suitable return statement must be encountered
on any normal execution path through the method body. In this case,
the value of the return expression is the value returned by the method
invocation expression.
• If the return type of the method is void, the final closing brace of the
method body is treated as an implicit return; statement, i.e., a return
with no expression. This has the effect of exiting the method body and
special name scope.
7.5 Constructors
So, how do objects get created? Each class has a special member, called a
constructor, which gives the instructions needed to create a new instance of the
class. (If you don't give your class a constructor, Java automatically uses a default
constructor, which roughly speaking "just creates the instance" -- details below.
So some of the classes that you see may not appear to have constructors -- but
they all do.)
In other words, to create a Pie, bake its ingredients. Note that stuff is a
parameter, just like in a method. Constructor parameters work exactly like method
parameters, and constructors take arguments to match these parameters in the
same way that methods take parameters.
But you don't invoke a constructor in the same way that you invoke a method. In
order to invoke a method, you need to know whose method it is. In order to use a
constructor, you only need to know the name (and parameter type list) of the
constructor. You invoke a constructor with a new expression as follows:
new Pie ( myIngredients )
where myIngredients is of type Ingredients.
7.5.2 Syntax
The syntax of a constructor is similar to, but not identical to, the syntax of a
method. A constructor may begin with a visibility modifier (i.e., public,
protected, or private) or one of a handful of other modifiers. Next comes the
name of the constructor, which is always identical to the name of the class. The
name is followed by a comma-separated parameter list enclosed in parentheses.
This parameter list, like the parameter list of a method, consists of type-of-thing
name-of-thing pairs. As in a method, the constructor name plus the ordered list of
parameter types forms the constructor's footprint. It is possible for a class to have
multiple constructors as long as they have distinct footprints.
After the parameter list, a constructor has a body enclosed in braces. This body is
identical to a method body -- an arbitrary sequence of statements -- except that it
may not contain a return statement. This is because constructors are not methods
that can be called and that return values of specified types; instead, a constructor
is invoked using a new expression whose value is a new instance of the
constructor class's type. The constructor body may contain any other kind of
expression or statement, however, including declarations or definitions of local
variables.
modifiers ClassName ( type_1 name_1, ... type_n name_n )
{
// body statements go here
}
For example, the NameDropper StringTransformer class might begin as follows.
Note that the constructor argument is used to initialize the private field, the
particular name that *this* NameDropper will drop.
//etc.
Note the use of a this. expression to refer to the field of the particular
NameDropper instance being created.
Style Sidebar
Constructor Documentation
Although a constructor is not a method, documentation for a constructor is
almost identical to documentation for a method. Constructor documentation
should include:
4
There should be no such fields, declared after the constructor, because this makes your code
difficult to read and so is bad style. However, if any such declarations are made, they still executed
prior to the constructor itself.
assumed present. This default constructor takes no arguments and does nothing
beyond creating the object (and initializing the fields if they are defined in their
declarations).
If there is even one constructor, the implicit no-arg constructor is not assumed.
This means that if you define a constructor such as the one for NameDropper,
above, that takes a parameter, the class will not have a no-arg constructor (unless
you define one).
[Hazard: This can cause a problem when extending a class, if you're not careful.
See chapter on Inheritance.]
Contrast this with the following example, in which the initial value of the name
field isn't known until the particular Student instance is created.
class Student {
String name;
created. When the constructor is invoked, it can increment the appropriate field:
class Widget {
static int numInstances;
Widget(){
Widget.numInstances = Widget.numInstances + 1;
}
}
Note that the constructor is not declared static (Constructors don't properly
belong to any object) but that it refers to a static field. Note also that the static
field is referred to using the class name (Widget), not using this. We've also
filled in the static method referred to above.
Finally, note that there is no explicit return statement in a constructor. A
constructor is not a method, and it cannot be invoked directly. Instead, it is used in
a construction expression, with the keyword new: new Widget() is an expression
whose type is Widget and whose value is a brand new instance of the Widget
class, for example.
Style Sidebar
Capitalization Conventions
By convention, the first letters of all class and interface names are
capitalized. Since constructor names match their classes, constructor names
also begin with capital letters. Java file names also match the class (or
interface) declared within, so Java file names begin with a capital letter.
All other names (except constants) begin with lower case letters. In
particular, the names of Java primitive types begin with lower case letters,
as do fields, methods, variables, and parameters.
After the first letter, mixed case is used, with subsequent capital letters
indicating the beginnings of intermediate words: e.g., ClassName and
instanceName.
Summary
• A Java class is a Java type.
• Each (public, top level) class must be defined in a separate file whose
name matches the class name.
• In a class, methods typically have bodies specifying how to carry out the
method. (Otherwise, the method is abstract, and so is the class.)
• A field declares (and perhaps also defines) a name whose scope is the
class body (i.e., any methods, fields, or constructors in the class body) and
whose lifetime is the lifetime of the instance it belongs to.
• The class itself is an object. (It is an instance of the class Class.) Fields
and methods declared static belong to the class object itself and are
properly referred to using ClassName.methodName(...) or
ClassName.fieldName.
Exercises
1. Consider the following definition:
public class MeeterGreeter
{
private String greeterName;
i. toWhom
ii. this.greeterName
iii. name
iv. this.sayHello()
ii. Write an expression that invokes the changeGreeting method that you
have written.
iii. Next, modify the sayHello methods to replace the fixed string "Hello"
with the a reference to the greeting field. Whose greeting field is it?
3. Define a class
whose instances each have one method,
rememberAndReturnPrevious, that takes a String and returns the String it was
previously given. Supply the first return value through the instance creation
expression. Give an example of your code in use.
Chapter Overview
• How do I design using objects and entities?
In the preceding chapters, we have seen how interfaces specify contracts and how
classes implement them. We have used expressions and statements to create
instructions that describe the processes of performing actions, making up method
and constructor bodies. And we have used names to retain an object's state even
while none of the object's methods is executing. In this chapter, we turn to the
question of how we design systems using these various tools.
The first part of this chapter looks at one simple example to illustrate how the
fields and methods of an object can be identified and implemented. Although the
example is small, the principles described here are general and will be used in the
design of any object-oriented program. This example also provides an opportunity
to look briefly at the question of privacy, or how an object separates internal
information from information that it makes available to other objects.
The next section of this chapter turns to look at three important kinds of objects
that appear in many systems. These kinds of objects -- data repositories, resource
©1999 Lynn Andrea Stein. This chapter is excerpted from a draft of Interactive Programming In Java, a
forthcoming textbook from Morgan Kaufmann Publishers. It is an element of the course materials developed
as a part of Lynn Andrea Stein's Rethinking CS101 Project at the MIT AI Lab and the Department of
Electrical Engineering and Computer Science at the Massachusetts Institute of Technology.
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
8~2 Designing with Objects Chapter 8
libraries, and traditional objects -- each play distinctly different roles in any
system, and their designs reflect these roles. A fourth distinct kind of object --
animate objects -- is the topic of the next chapter.
The chapter concludes with a discussion of the ways in which different objects
and types are interrelated.
• To recognize common kinds of objects and the roles that they play.
-- numbers, for example, will probably be doubles or ints -- but most of the
things that are important enough to represent and complex enough that Java
doesn't have a built-in type for them will be objects in your world. This means
that you will have to define a Java class which describes what this type of object
is (more below).
For example:
A counter has a number associated with it. When it starts out, the number is 0.
You can increment the counter, and each time you do so, the number goes up
by one. At any time, the counter can also be asked to provide the current value
of its associated number.
The nouns in this paragraph are counter and number. (And you, but we'll assume
that you either refers to the user, which we don't need to implement, or to some
other component outside of the current system.) The counter will be a Java object;
we can use an int for the number since it isn't asked to do anything, just to be
there.
For example, the class BasicCounter is something that tells Java how to make
a new BasicCounter(). It doesn't appear explicitly in the English description, but
parts of the description are about it and other parts imply things about what it
must say. The phrase "When it starts out, the number is 0" talks about initial
conditions for BasicCounter objects; the class is the thing responsible for
Style Sidebar
• methods
• fields
• constructors
• class
• its interface, especially key methods and fields & how they
interact
int getValue()
{
return this.currentValue;
}
}
Some notes on this code:
• The class is a factory for making BasicCounters. Its body talks about
what each individual BasicCounter looks like, not about the factory
itself.
• Each individual BasicCounter has its own currentValue field. Each one
starts out with the value 0, but they can change independently: each
currentValue field belongs to a specific BasicCounter.
• The increment and getValue methods are methods that belong to each
BasicCounter instance. In each case, they refer to the currentValue
field of that BasicCounter instance. We note this by using the java
keyword this.
Console.println( myCounter.getValue() );
// prints 1
myCounter.increment();
myCounter.increment();
myCounter.increment();
Console.println( myCounter.getValue() );
// prints 4
Final
A name in Java may be declared with the modifier final. This means that
the value of that name, once assigned, cannot be changed. Such a name is,
in effect, constant.
The most common use of this feature is in declaring final fields. These are
object properties that represent constant values. Often, these field are static
as well as final, i.e., they belong to the type object rather than to its
instances. Making a constant static as well as final makes it easy for other
objects to refer to this value. It is appropriate for static final fields to be
declared public and to be accessed directly by other objects. Static final
fields are the only fields allowed in interfaces.
In addition to final fields, Java parameters and even local variables can be
declared final. A final parameter is one whose value may not be changed
during the execution of the method. A final variable is one whose value is
unchanged during its scope, i.e., until the end of the enclosing block.1
Java methods may also be declared final. In this case, the method cannot be
overridden in a subclass. Such methods can be inlined by the compiler, i.e.,
the compiler can make these methods execute more efficiently than other
non-final methods. A static method is implicitly final. An abstract method
may not be declared final.
Java classes declared final cannot be extended (or subclassed).
1
Final fields and parameters are unnecessary unless you plan to use inner classes. They may,
however, allow additional efficiencies for the compiler, and in any case they cannot be
detrimental.
2
Specifically, it would be legal, if longwinded, to say
((BasicCounter) myCounter).currentValue
= ((BasicCounter) myCounter).currentValue + 1;
In general, it's a good idea to define fields as private when you don't want them
to be accessed directly by other objects. You can also define private methods,
which are generally things an object uses for its internal computations but not
intended to be used from outside the object. Private things are a part of the class's
or its instances' own internal representations and machinations; they are not to be
shared.
Any member, not just a field or a method, can be private. You can even define
private constructors. Although this may seem like an odd thing to do, it actually
isn't all that strange. It means that the class object (along with any instances it
creates) maintains complete control over whether and when new instances can be
created. The class can refuse to create any instances, or it can create just one
instance and return this any time someone asks for a new one (using a special
method the class defines for this purpose, such as getInstance(), not the (private)
constructor), or it can ask for the secret password before creating an instance if it
(or its designer) wants to.
The opposite of private is public. You should declare things public when you
want them to be accessible from any part of anyone's code. You can also declare
classes and interfaces to be public, in which case they must be defined in a file
whose name is the same as the name of the class or interface, plus .java.
Here is some code for a very simple address object. Note that this code has some
aesthetic problems, which we will address shortly.
public class OversimplifiedAddress {
3
Actually, this should read "One object should never access another object's non-final fields
directly." Final fields are in effect constants; the reasons for objecting to field access do not apply
to read-only accesses to a constant.] Instead, an object should provide methods for accessing its
fields. [Teacher's note: Where getter methods are simply long-winded ways of doing field access,
a good compiler should be able to inline this code. In Java, this can be done when the getter
method is declared final.
class definition would presumably contain four pairs of getter and setter methods.
public class BetterAddress {
private String streetAddress,
city,
state,
postalCode;
....
public void setPostalCode( String code )
{
this.postalCode = code;
}
4
Note that a read only field is different from a constant (final) field. A read-only field can be
changed by its owning object, but not by anyone else. A final field's value, once set, cannot be
changed. This is enforced by the Java compiler.
arise when two or more people try to use the same things at the same time
(covered in the chapter on Synchronization); the tools that you can use to address
these issues generally rely on methods rather than fields.
One of the most common reasons for a pure data repository class is to allow
simultaneous return of multiple interrelated values. An example of this type is the
Dimension class in the java.awt package. This class exists so that its instances can
hold both (horizontal and vertical) coordinates, e.g., of a window size. This allows
them to be simultaneously returned from a method such as Window's getSize()
method. If getSize() weren't able to return a data repository type such as
Dimension, you'd first have to invoke a method that returned the Window's
horizontal dimension, then one that returned its vertical dimension. If the
Window's size changed in between these two method invocations, your two
individual dimension components would combine to produce a nonsensical value!
Pure data repository objects are actually quite rare in good object-oriented design.
This is because most objects do more than hold some state. The extensions we've
described above, including propagation of changes, virtual fields, and access
control already begin to expand the data repository idea. In the next subsection,
we look at objects that exist to provide behavior without state. In the following
subsection, we will return to objects that contain both data and more interesting
behavior.
would want to make instances. Instead, Math has only static methods and static
fields. This means that you can use its methods and data members through the
class object (Math) itself.For example, a typical method is Math.sqrt(double d),
which takes a double and returns a double that is the square root of its argument.
Without the Math class to collect it and other mathematical functions, it is hard to
imagine to whom this sqrt function could belong. Math exists so that there is a
place to collect sqrt and a number of other abstract mathematical functions.
The Math class has static methods for the trigonometric functions, logarithms and
exponentiation, various flavors of rounding, and very simple randomization. Math
also has two (static final, i.e., constant) fields: E and PI, doubles representing the
corresponding mathematical constants. See the sidebar on Math for details.
class Math
The built-in Java class Math may be the canonical resource library. It
contains two (static) fields, Math.E and Math.PI, both doubles,
corresponding to the mathematical constants e and pi, respectively.
Math also contains a host of useful mathematical functions, again all static.
Each of the following methods takes a double as an argument and returns a
double:
Math.max and Math.min each take two arguments of the same type (both
double, float, long, or int). max returns the larger of its arguments; min the
smaller.
Math.round takes a double and returns the long closest in value to its
argument.
Math.pow takes two doubles and yields the value of the first raised to the
power of the second. (Math.pow( base, expt ) = baseexpt.)
Math.random takes no arguments and returns a double equal to or larger
than 0.0 and strictly smaller than 1.0.
There are a few other Math methods not included here. In addition, there
are extra mathematical functions (including more flexible and powerful
randomization) available in the package java.math. For these additional
methods, see the Java API documentation on the Javasoft web site.
public Counter()
{
this.reset();
}
public void increment()
{
this.currentValue = this.currentValue + 1;
}
• This state is not directly accessible. Instead, it provides the basis for
method behavior.
For example, the code for the VCR might say (in part):
public class VCR
{
private Clock clock;
// etc.
}
In this way, the VCR provides access to the Clock's methods indirectly. This
reuse of behavior by inclusion is a very powerful mechanism. In this case, the
VCR might be providing access to the full set o Clock's methods. In another case,
the including class might only provide a subset of the included class's methods, or
it might provide a superset by combining those methods in different ways. The
including class and the included class can even implement a common interface
(such as TimeStorer) so that code that uses one or the other can't really tell the
difference so long as it only uses the interface's methods.
The DefaultGameFrame and GameControllable described above are similar.
When the DefaultGameFrame is asked to perform a reset (or a start or a stop
or...), it passes this request along to the GameControllable. In that case, the use of
an interface type -- GameControllable -- for the included object increases the
flexibility and usability of the including class.
Chapter Summary
• In an informal description of the program, nouns generally correspond to
objects or to fields, methods to verbs, and interfaces to adjectives.
• Traditional objects mix both data and methods. These objects provide the
kind of integrated state-dependent behavior that we expect of real world
objects.
Exercises
1. Design and implement a class called Time that keeps track of the hour and
minute together. Give it a nextMinute method that returns another Time, a minute
later. How do you access the fields of Time objects?
2. Design and implement a class that provides IntegerArithmetic functions add(
int, int ), sub( int, int ), mul( int, int ), and div( int, int ). You can give it any other
methods you think might be useful. What doe s its constructor do? Why do you
think that Java doesn't have such a class?
3. Design and implement a 2DVector class representing vectors in the plane.
Include sum, difference, and product methods.
Chapter Overview
• How do I create an object that can act by itself?
This chapter builds on the previous ones to create an object capable of acting
without an external request. Such an object has its own instruction follower, in
Java called a Thread. In addition, an object with its own instruction-follower must
specify what instructions are to be followed. This is accomplished by
implementing a certain interface -- meeting a particular contract specification --
that indicates which instructions the Thread is to execute.
The remainder of this chapter deals with examples of how Threads and animate
objects can be used to create communities of autonomously interacting entities.
©1999 Lynn Andrea Stein. This chapter is excerpted from a draft of Interactive Programming In Java, a
forthcoming textbook from Morgan Kaufmann Publishers. It is an element of the course materials developed
as a part of Lynn Andrea Stein's Rethinking CS101 Project at the MIT AI Lab and the Department of
Electrical Engineering and Computer Science at the Massachusetts Institute of Technology.
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
9~2 Animate Objects Chapter 9
executes.
contained in these objects. But the animate object is where it begins and ends.
What makes an animate object different from other (passive) objects? Recall that
on the first page of the first chapter of this book, we learned about the two
prerequisites for a computation: The instructions for the computation must be
present, and those instructions must be executed. Every method of every object is
a set of instructions -- a rule -- that can be executed. When a method is invoked,
its body is executed. (The method body is executed by the instruction-follower
that invoked the method; this is how a method invocation expression is
evaluated.)
An animate object differs from other objects because it also has its instruction
follower. It does not need to wait for another instruction-follower to invoke one of
its methods (although this may also happen). Instead, it has a way to start
execution on its own.
In Java, an instruction-follower is called a Thread. No object can act except a
Thread. A Thread is a special object that "breathes life" into other objects. It is the
thing that causes other objects to move. An animate object is simply an object that
is "born" with its own Thread. (Typically, this means that it creates its own
Thread in its constructor and starts its Thread running either in its constructor or
as soon as otherwise possible.)
Now, the first statement of the method body is executed. In the case of the method
invocation expression cap.transform( "Who's there?"), there is only one
statement in the method body. This is the return statement, which first evaluates
the expression following the return, then exits the method invocation, returning
the value of that expression. To evaluate the method invocation expression
what.toUpperCase() involves first evaluating the name expression what and
then invoking the toUpperCase() method of the object associated with the name
what.
In this book, we will make extensive use of a special kind of Thread called an
AnimatorThread. An AnimatorThread is an instruction follower that does the
same thing over and over again. It also has some other nice properties: it can be
started and stopped, suspended and resumed. These last two mean that it is
possible to ask your instruction follower to take a break for a while, then ask it
later to continue working. AnimatorThreads provide a nice abstraction for the
kinds of activities commonly conducted by the animate objects that are often
entities in our communities.
this.increment();
}
Of course, we'd also have to declare that Timer implements the Animate interface.
It isn't enough for Timer to have an act() method; we also have to specify that it
does so as a commitment to the Animate interface. Here is a complete Timer
implementation:
public class Timer implements Animate
{
private int currentValue;
public Timer()
{
this.reset();
}
public void increment()
{
this.currentValue = this.currentValue + 1;
}
1
As we shall see in the next chapter, we could significantly abbreviate this class
by writing it as
public class Timer extends Counter implements Animate
{
public void act()
{
this.increment();
}
}
9.3.2 AnimatorThread
On the other side of this contract is the instruction follower, an AnimatorThread.
Like any other kind of Java object, a new AnimatorThread is created using an
instance construction (new) expression and passing it the information required by
AnimatorThread's constructor. The simplest form of AnimatorThread's
constructor takes a single argument, an Animate whose act() method the new
AnimatorThread should call repeatedly.
For example, we can animate a Timer by passing it to AnimatorThread's
constructor expression:
Timer tick = new Timer();
AnimatorThread mover = new AnimatorThread( tick );
There is one more thing that we need to do before tick starts incrementing itself:
tell the AnimatorThread to startExecution():
mover.startExecution();
An AnimatorThread's startExecution() is a very special method. It returns
(almost) immediately. At the same time, the AnimatorThread comes to life and
begins following its own instructions. That is, before the evaluation of the method
invocation mover.startExecution(), there was only one Thread running. At the
end of the evaluation of the invocation, there are two Threads running, the one
that followed the instruction mover.startExecution() and the one named
mover, which begins following the instructions at tick's act() method.
Here is another class that could be used to monitor a Counting (such as a Counter
or a Timer):
public class CountingMonitor implements Animate
{
private Counting whoToMonitor;
private AnimatorThread mover;
public AnimateObject()
{
2
AnimatorThread's instances also have a startExecution() method that is identical to the
startExecution() method. This is for historical reasons.
class AnimatorThread
AnimatorThread is a cs101 class (specifically, cs101.lang.AnimatorThread)
that serves as a special kind of instruction-follower. An AnimatorThread's
constructor must be called with an instance of cs101.lang.Animate. The
AnimatorThread repeatedly follows the instructions in the Animate's act()
method.
An AnimatorThread is an object, so it can be referred to with an
appropriate (label) name. It also provides several useful methods:
The next two constructors incorporate the same functions as setRange and
setMinInterval:
AnimatorThread( Animate who, long sleepRange )
Finally, there are two additional constructors that incorporate both startup
and timing information:
AnimatorThread( Animate who, boolean startImmediately,
long sleepRange )
public InitAnimateObject()
{
this.mover = new AnimatorThread( this );
}
Thread methods
Threads are Java's instruction followers. In this book, we will most often
make use of AnimatorThreads. However, it is useful to understand how
Java's built-in Thread class works as well.
Like an AnimatorThread, each Thread provides a few methods for its
management.
boolean isAlive() tells you whether the target Thread is alive, i.e.,
has been started and has not completed its execution.
method might have been used. For example, the raw Thread class uses a different
convention, that of Runnable/run(). If you were to design your own type of
Thread, you could create a different convention for it to follow. However, once
these names and contracts have been selected by the designers of AnimatorThread
and Thread, they are absolute rules that cannot be violated.
Similarly, there must be some arbitrary convention as to how a Java program
begins. In a standalone application, the convention is that running a Java program
means supplying a class to the executable, and by convention a particular method
of the class is always the place that execution begins. This default execution does
not create an instance of the class, so the method must be a static one. Again by
convention, the name of this method is main, it takes as argument an array of
Strings, and it returns nothing. That is, the arbitrary but unvarying start point for
the execution of a standalone Java application is the
public static void main ( String[] args )
method of the class whose name is supplied to the executable.3
So if you want to write a program, you simply need to create a class with a
method whose signature matches the line above. The body of that main method
will be executed by the single Thread that is created at the beginning of a Java
execution. When execution of main terminates, the program ends. If you do not
want the program to end, you need to do something during the course of
executing main that causes things to keep going. Typically, this means that you
use the body of main to create one or more objects that themselves may execute.
For example, if the body of main creates an animate object (with its own
AnimatorThread), then that object will continue executing even if the body of
main is completed. This is called "spawning a new Thread".
Here is a very simple class that exists solely to create a new instance of the
AnimateTimer class:
public class Main
{
public static void main ( String[] args )
{
Counting theTimer = new AnimateTimer();
}
}
This program simply counts. The instruction follower that begins when this
3
Typically, this means the class you select before choosing run from the IDE menu or the class
whose name follows the command java on the command line.
program starts up (e.g., using java Main) executes the main() method, invoking
new AnimateTimer() and assigning the result to theTimer. This Thread is now
done executing and stops. However, the constructor for AnimateTimer has created
a new AnimatorThread and then called that AnimatorThread's startExecution()
method. This starts up the new Thread which repeatedly calls AnimateTimer's
act() method. The program as a whole will not terminate until the
AnimatorThread stops executing, which it will not do by itself. If you run this
program, you will need to forcibly terminate it from outside the program!
Since we didn't give this program any way to monitor or indicate what's going on,
running it wouldn't be very interesting. But we can use the CountingMonitor
above to improve this program:
public class Main
{
public static void main ( String[] args )
{
Counting theTimer = new AnimateTimer();
Animate theMonitor = CountingMonitor( theTimer );
}
}
Q. Can you find a more succinct way to express the body of the main method?
Q. What will be printed by this program? On what does it depend? (Hint:
fairness.)
The instruction follower executing the Main class's main method exits. However,
before it completes it executes the instructions to create and start two separate
AnimatorThreads. These AnimatorThreads continue after the execution of the
main Thread exits. Again, this program must be forcibly terminated from outside.
Q. Can you cause this program to stop by itself sometime after it has counted to
100? (This is a bit tricky.)
The two versions of the Main class above each contain just the instructions to
create an instance or two. In the cs101 libraries, we have provided a Main that
does this for you. This allows you to write applications without needing to write
public static void main( String[] ) methods yourself.
class Main
The cs101 libraries include a class, cs101.util.Main, that can be run from
the java command line to create an instance of a single class with a no-args
constructor. For example, we could implement the unmonitored Timer
example using the following command:
java cs101.util.Main AnimateTimer
This causes code much like the first Main class to execute, creating a single
instance of AnimateTimer (using its no-args constructor).
The class cs101.util.Main contains nothing but the single static method
main (taking a String[] argument). The command above tells Java to start
its initial instruction follower at this method -- the static main( String[] )
method of the class cs101.util.Main. The remainder of the information on
the command line (in this case, AnimateTester) is supplied to the main
method using its parameter.4
4
For more detail on arrays ([]), see the chapter on Dispatch.
Style Sidebar
Using main()
If you do decide to write your own main() method, you should do so in a
class separate from your other classes, generally one called Main and
containing only the single public static void main() method requiring a
String[] (i.e., an array of Strings). This method may have some complexity,
creating several objects and gluing them together, for example.
Alternately, you can create an extremely simple main method in any (or
even every) class that you write. In this case, however, the main method
should do nothing more than to create a single instance of the class within
which it is defined, using that class's no-args constructor. Of course, the
signature of each main method is the same: public static void main( String[]
args ) The main that will actually be executed is the one belonging to the
(first) class whose name is supplied to the java execution command. So, for
example, in the sidebar on class Main, we said
java cs101.util.Main AnimateTimer
causing cs101.util.Main's main method to be run.
The logic behind these restrictions on the use of main() is as follows. In the
second case -- main in many instantiable class's files -- the presence of
main allows that object to be tested independently. However, this test is
extremely straightforward and predictable. If the main method takes on any
additional complexity, it should be separated from the other (instantiable)
classes and form its own resource library, one that exists solely to run the
program in all its complexity.
the new expression -- would never complete. In the monitored counting example,
the invocation of AnimateTimer's constructor would cause the instruction
follower to execute the act() method of AnimateTimer over and over again. This
instruction follower -- the only instruction follower to be running so far -- would
never complete the repeated execution of the act() method. This means that it
would never get around to creating the CounterMonitor.
This is why AnimatorThread.startExecution() must be a very special kind of
method. The Thread, or instruction follower, that executes startExecution() must
return (almost) immediately. It is the new Thread, the one just started, that goes
off to execute the act() method. The original Thread returns from this invocation
and goes about its business just as if nothing ever happened. In personal terms,
this is the difference between doing the job yourself and assigning someone else
to do it. True, when someone else does it you have less control over how or when
the job gets done; but while someone else is working on it, you can be doing
something else.
Chapter Summary
• In Java, activity is performed by instruction followers called Threads.
• An animate object is simply one that has its very own Thread.
Exercises
1. Define a class whose instances each have an internal value that doubles
periodically. Each time that the value doubles, the instance should print
this new value to the Console.
2. Define a class that periodically reads from the Console and writes the
value back to the Console.
3. Define a main class that creates three instances of your doubler.
4. Using the timing parameters of AnimatorThread, demonstrate that not all
doublers have to run at the same rate.
Chapter Overview
• How do I simplify the program design task by reusing existing
code?
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
10~2 Inheritance Chapter 10
This chapter includes sidebars on the details of method and field lookup. It is
supplemented by reference charts on the syntax and semantics of java methods,
fields, and class declarations.
from another factory, then adding its own minor tweaks (bells and whistles) to the
widgets before claiming to have produced them.
The mechanism by which this is accomplished in Java is called inheritance, and it
applies to a relationship between two classes. There is a similar relationship
between two interfaces, described below. Inheritance is not ever a relationship
between a class and an interface (or between an interface and a class). Inheritance
really means an almost literal subsuming of one thing by another.
public AnimateObject()
{
this.mover = new AnimatorThread( this );
this.mover.startExecution();
}
{
Console.println( "The timer says "
+ this.whoToMonitor.getValue()
);
}
}
It would be really nice only to have to write the underlined information, not the
rest. In fact, we can do almost exactly that. The following definition is almost
equivalent to the Counter definition above:
public class CountingMonitor extends AnimateObject
{
private Counting whoToMonitor;
10.1.2 java.lang.Object
There is actually a single built-in type called Object, and all other object types
(directly or indirectly) extend Object. In other words, anything which is not one
of the built in types is an Object of some sort or another.
class Cat extends Animal
{
....
}
A class declaration is followed by an optional extends clause, then a pair of
braces around the body of the class definition. If the extends clause is missing
(e.g., class Widget {...}), the default clause extends Object is assumed.
Thus, every class (implicitly or explicitly, directly or indirectly) extends
Object.
The class Object provides some basic functionality that every other class
necessarily inherits. This means that you can guarantee that every Java object has,
e.g., a toString() method. See the sidebar on The class Object for details.
class getClass() returns the class object (i.e., factory) from which this
instance was created.
1
If you call the clone() method of an object that doesn't implement Cloneable, it will throw
CloneNotSupportedException. See the next chapter for more on Exceptions.
10.2 Overriding
The examples of inheritance in the previous section demonstrated that a subclass
can extend the functionality of its superclass. The subclass can also modify
superclass functionality by overriding, or redefining, methods provided by the
superclass. In fact, CountingMonitor overrode the act() method provided by
AnimateObject. This just wasn't a very interesting example because
AnimateObject's act() method didn't do anything.
Consider the following classes:
public class Super
{
public void doit()
{
Console.println( "super method" );
}
10.2.1 super.
What if we still want to be able to access Super's doit() method from the subclass?
To do this, we need a special expression much like this. The expression this
refers to the instance whose code is being executed. The expression super refers
to the superclass of the object containing the actual executing code.
public class ExpandingSub extends Super
{
public void doit()
{
super.doit();
Console.println( "expandingSub method" );
}
}
In this case, we'll get the effect of executing the superclass method followed by
the local println:
super method
expandingSub method
If we reverse the lines of the method body, we will reverse the order of the printed
lines.
The expression this always refers to the object on behalf of whom you are
executing. At the moment, we're executing some code in the class Super. But we
are doing it for an instance of OverridingSub; we just happen to be looking at
over as though it were a Super, just as we did when we labeled it with a Super-
type name. Looking at over as a Super doesn't make it one, though. So when we
call this.doit(), we go right back to the outside (OverridingSub) and start
working our way in again, looking for a doit() method. So the effect of invoking
over.doitAgain() is the same as invoking over's doit(), not the Super method.
int getValue()
{
return this.currentValue;
}
}
To implement the Resettable Counter class, we would like to be able to write the
following:
public class Counter extends BasicCounter implements
Resettable
{
public Counter()
{
this.reset();
10.3.1 this()
When a class has more than one constructor, we can express one constructor in
terms of another using the special syntax this(). For example, we might define a
Point class that either could be instantiated using specified values for the x and y
coordinates or could take on the default value (0,0). We might define the
constructors this way:
public class Point
{
private int x, y;
public Point()
{
this( 0, 0 );
// constructor would continue here....
}
10.3.2 super()
Constructors and inheritance work similarly. Making an inherited object (the
"inner object" that belongs to the superclass) is just like passing the buck to a
same-class constructor. The first line of any constructor may be an explicit
invocation of the superclass constructor, supplying whatever arguments are
necessary between the parentheses.
For example, if we wanted to extend the CountingMonitor class, above, to
determine whether the reading of its Counting had changed since the previous
reading, we could add a field (to keep track of the previous reading) and a
conditional in the act() method. But how would we deal with the constructor? The
beginning of this class might read:
public class ChangeDetectingCountingMonitor extends
CountingMonitor
{
private int previousReading;
Details:
Beware: Since Java will automatically invoke the no-args version of super()
unless you explicitly invoke a superclass constructor, either (1) the superclass
must have a no-args constructor or (2) you must explicitly invoke the superclass
constructor yourself, supplying the requisite arguments. If you create a class
without a no-args constructor, you can get into trouble extending it.
Style Sidebar
A class declaration that does not contain an explicit extends clause still
extends Object. Stating this explicitly may make it easier to read your
code.
• Does this new class present to its users the full range of behavior provided
by the existing class (inheritance) or just some of that behavior
(coupling)?
• Does this new class add behavior to the existing class (inheritance) or
2
But see, e.g., the Factory pattern [GHJV] for an approach to this problem.
• Does an instance of this new class have a different lifetimes from the
associated instance of the existing class (coupling)?
It is only when the superclass will be wholly reused, and when the subclass really
is an extension of the implementation provided by the superclass, that inheritance
should be used. Occasionally, this justifies the use of an abstract class to
encapsulate common behavior that is extended differently by different classes.
Abstract Classes
A class can have a method that is just a signature -- an abstract method.
In a class, however, the abstract method must be explicitly declared
abstract. (Recall that methods in an interface are assumed to be
abstract, even if they are not explicitly so declared.)
3
Technically, a class can be abstract even if it has no abstract methods. However, every
class with at least one abstract method must be declared abstract.
Chapter Summary
• Inheritance is a mechanism that allows one class to reuse the
implementation provided by another.
• Inheritance should be used only when instances of the subclass can also
reasonably be considered instances of the superclass.
Exercises
1. In the first interlude, we wrote "UpperCaser extends StringTransformer".
Explain.
2. Extend the Counter to count by 2.
Chapter Overview
• What happens when something goes wrong?
©1999 Lynn Andrea Stein. This chapter is excerpted from a draft of Interactive Programming In Java, a
forthcoming textbook from Morgan Kaufmann Publishers. It is an element of the course materials developed
as a part of Lynn Andrea Stein's Rethinking CS101 Project at the MIT AI Lab and the Department of
Electrical Engineering and Computer Science at the Massachusetts Institute of Technology.
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
11~2 When Things Go Wrong: Exceptions Chapter 11
InputAcceptor interfaces, since that is the only aspect of their behavior that we
rely on here.
public StringConnector( OutputAcceptor a, InputAcceptor b )
{
a.acceptOutputConnection( this );
b.acceptInputConnection( this );
}
This code is perfectly reasonable assuming that everything goes right. But what
happens if transformerA already has an outputConnection in place? It might be
that transformerA is a Broadcaster or AlternatingOutputter or some other kind of
transformer that can have many outputConnections. It might be that transformerA
is willing to throw away its existing outputConnection and replace it with the one
currently on offer. But it might also quite reasonably be that transformerA is
unwilling and unable to accept an OutputConnection if it already has one in place.
In this case, the StringConnector constructor code is in trouble.
This is precisely the sort of situation that we will deal with in this chapter.
Something has gone wrong. We can anticipate in our design that this might
happen. We want our code to respond appropriately. In other words, we want to
design our programs to be able to handle exceptional circumstances.
the power cord of the computer on which your program was running. In this case,
it is reasonable to expect that your computer program will stop executing
immediately. There's really nothing that you can do about a catastrophic failure. 1
The second kind of exceptional circumstance is at the other end of the spectrum.
This is a situation that is not the intended course of your program, but is so benign
that it is dealt with almost as a matter of course. These are the unexpected
situations that can be handled with a simple conditional or other testing
mechanisms. For example, if we are about to perform a division operator, we
might check to make sure that the divisor is not 0. In the extreme, these situations
can be difficult to distinguish from "normal" operation.
Most exceptional circumstances fall between these two extremes. That is, they
admit some intervention or even solution (unlike catastrophic failure), but
handling these circumstances requires cooperation among entities or other
addtional complexity; it isn't possible or desirable to deal with this situation
locally. These are the situations that you must take into account in your design.
When you are planning your program, you will be deciding how to partition the
problem among a community of interacting entities and designing how these
entities interact. At this point, you should also ask:
• What are all of the ways in which an entity might violate expectations?
1
At least at the time of failure. There are still things that you can do to plan for recovery from
catastrophic failure. For example, a banking system may temporarily lose the functioning of an
ATM, but it will not lose track of your bank balance entirely. It has been designed to keep this
information safe even in the face of computer crashes.
2
In this example, the "more activity" line inside the constructor is a shorthand for a more complex
picture. This "more activity" actually involves another method call, this one to the InputAcceptor's
©1999 Lynn Andrea Stein. This chapter is excerpted from a draft of Interactive Programming In Java, a
forthcoming textbook from Morgan Kaufmann Publishers. It is an element of the course materials developed
as a part of Lynn Andrea Stein's Rethinking CS101 Project at the MIT AI Lab and the Department of
Electrical Engineering and Computer Science at the Massachusetts Institute of Technology.
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
11~8 When Things Go Wrong: Exceptions Chapter 11
Throwing an Exception is different. What happens in this case looks more like the
following:
User Interface Constructor OutputAcceptor
-------------------->
------------------->
OH NO!!
When the OutputAcceptor's acceptOutputConnection method realizes that it has a
problem it generates an Exception object, as we have seen above. Then, it throws
the exception as hard as it can back the way it came. The Exception zooms back
along the call path, flying too fast to stop and execute any statements waiting for
its return. In fact, the Exception keeps going until it encounters a compatible
catch statement. If necessary, it may exit several method bodies. Or, if the catch
is in the same block as the throw, it may not exit any method bodies at all. In
other words, a throw statement sets an Exception flying, and the flying Exception
can only be caught by a matching catch statement; no other intervening statement
along the call path matters.
This is, in fact, just what we want. If the OutputAcceptor can't accept an output
connection, we don't want the rest of the Constructor to execute. For example, we
don't want it to try to convince the InputAcceptor to accept the input end of the
connection, because this connection isn't going to work out (since the
OutputAcceptor isn't cooperating) and if the InputConnector accepts this one, then
it won't later be able to accept a fully operational input connection. So when the
OutputAcceptor decides that it has a problem, we want the Exception to
propogate all the way back to the user interface code, which should decide that
connecting this particular pair of String Transformers may not be such a good
idea after all.
The code for the OutputAcceptor might look like this:
... implements OutputAcceptor
{
private OutputConnection out;
This example introduces a new statement type, throw, and a new declaration
element, throws. (Note the s on the declaration element.) The throw statement
works just as we have described; it abruptly terminates the execution of this
method and causes the Exception to propogate backwards along the return path
until a compatible catch statement is encountered. (We will see this below.)
What about the throws clause? Throwing an exception is actually part of the
contract that one object makes with another. It is as much a part of a method's
contract as its (normal) return type or the parameters it needs. So a method must
declare that it may throw an exception (and what type of exception it may throw).
This way, anyone calling the method knows to be prepared for it to throw this
exception. The throws clause is the final part of a method signature, and throws
clauses may appear in interface (abstract) method declarations as well as in
method definitions.
Throws clauses are not restricted to methods. Constructors, too, must declare any
exceptions that they throw. A constructor can explicitly throw an exception using
a throw statement. A constructor (or method) can also throw an exception by
calling something that throws an exception and then not catching it. This is what
happens with the StringConnector constructor. Here it is, reprinted from above,
with the added throws clause underlined.
public StringConnector( OutputAcceptor a, InputAcceptor b )
throws ConnectionRejectedException
{
a.acceptOutputConnection( this );
b.acceptInputConnection( this );
}
The throws clause appears after the argument list, but before the
method/constructor body. The syntax for a throws clause is
throws ExceptionType1, ExceptionType2, ... ExceptionTypeN
Every exception thrown and not caught within the body must match (at
least) one of the exception types declared thrown by the method or
constructor. If the method or constructor throws only a single exception
type, the list contains no commas.
• This try/catch statement type has two bodies: one after the keyword
try,and one after the catch parameter.
• The catch portion of the statement has a single parameter, the exception
(type and name) that is to be caught. In this case, the exception type is
ConnectionRejectedException, and the name of the exception is e.
The name is required, and it may be used inside the catch body, just like a
method parameter name can be used inside the method body. It is common
to name the exception e, though there's no particular reason for it; it's just
like loop variables are often named i.
• The catch body contains statements which are executed if and only if the
appropriate type of exception is thrown. (The "appropriate type" is the
type of the catch's parameter.) Inside the catch body, the parameter name
may be used to refer to the exception, though there isn't a whole lot you
can do with an exception other than print its message.
In this case, once the exception is caught, a message is printed to the user. This
statement might itself appear inside an animate object's act method, so that
something is continually listening to the user and trying to make connections on
the user's behalf. This message lets the user know that this particular attempt
didn't work. If we had supplied additional information along with the exception,
we might use it at this point to give the user more information (perhaps flashing
the object that refused the connection) or to try to repair the situation (asking
whether the user means to delete the existing connection, for example, and then
retrying the connection creation).
One try can actually have several catch statements. In this case, once something
is thrown inside the try body, it is compared against the catch parameter
statements in order until one that matches is found. If a match is found, only the
first matching catch body is executed; then control continues at the end of the
try/catch statement. If no match is found, the thrown object continues exiting
statement blocks until a corresponding catch is found.
Each catch clause has a single parameter (type and name) followed by a
block. A catch clause matches the thrown object exactly when the thrown
object can be named by a name of the catch clause's parameter type. Only
the first matching catch clause is executed.
• The try block is executed in order until something is thrown or the end
of the try body is reached.
• If nothing is thrown during the try body, execution continues after the
final catch clause of the try/catch statement.)
of carefully marked scripts rather rapidly, scanning down the instructions until an
appropriate catch statement is encountered. If the current script doesn't contain a
matching catch statement, it is summarily discarded and the next script is
examined in turn.
This means that a return statement always causes the current method to
complete, returning control to whomever called this method. This is true no
matter how many statement blocks the return is buried inside. A return always
exits exactly one method invocation.
In contrast, a throw exits one block at a time until a catch of the appropriate type
is found. This means that a throw may not exit any methods (if the throw occurs
directly inside an appropriate try/catch), or the throw may exit many methods
(if the exception is not caught in any of these calling methods). A throw exits
blocks until an appropriate catch is encountered.
Error is the Java class that denotes a catastrophic failure. This is an event
from which your program is not expected to be able to recover. A well-
designed robust program that is expected to have an extended lifetime (such
as a banking system or an airline reservation system) must have ways of
dealing with catastrophic failure, but most programs that you write will not
have to worry about such circumstances.
Chapter Summary
• In designing a program, you should anticipate things that can go wrong
and design in mechanisms to deal with them.
• Methods and constructors that may throw checked exception types must
declare this fact in their signatures.
Exercises
1. Describe the process of baking a cake. Include at least three exceptional
circumstances that might arise and how these should be handled.
2. Describe the normal conduct of a soccer game. Include at least three
exceptional circumstances that might arise and how these should be handled.
3. Define an Exception type called UnbelievableException. Remember to define
two constructors.
4. Using your UnbelievableException type, write an animate object that
continually asks the user for the user's age, then throws an UnbelievableException
if appropriate. Note: the presence of an unbelievable age should not cause the
program to terminate.
Chapter Overview
• How can I do different things at different times or under different
circumstances?
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
12~2 Dispatch Chapter 12
pressed, the calculator should display this digit, perhaps along with previously
pressed numbers.1 Pressing an arithmetic function key -- such as + or * -- means
that subsequent digits should treated as a new number -- the second operand of the
arithmetic operator -- rather than as additional digits on the first. Pressing =
causes the calculator to do arithmetic. And so on.
In this example, the calculator's central control loop is behaving like a middle
manager. It's not the boss, who gets to set direction. It's not the worker, who
actually does what needs to be done. The dispatcher is there to see that the boss's
directions (the button pressed) get translated into the appropriate action (the
helper procedure). The dispatcher is simply directing traffic. This kind of
behavior, in which different things happen under different circumstances, requires
conditional behavior. We have already seen a simple kind of conditional behavior
using Java's if statement. In this chapter, we explore several different means of
achieving conditional behavior in greater detail.
Throughout this chapter, we will assume that we have methods that actually
provide this behavior. For example, the calculator might have a
processDigitButton method which would behave like exercise # in Chapter 7.
Another method, processOperatorButton, would apply the appropriate
operation to combine the value currently showing on the calculator's display with
the number about to be entered. We will also use methods such as
isDigitButton to test whether a particular buttonID corresponds to a number
key. Separating the logic surrounding the use of these operations from their
implementation is an important part of good design and the topic of much of the
chapter on Encapsulation.
In this chapter, we are going to concern ourselves with what comes after the first
line of the calculator's act method:
public void act()
{
SomeType buttonID = this.gui.getButton();
....
}
The remainder of this method should contain code that calls, e.g.,
processDigitButton if buttonID corresponds to one of the buttons for digits 0
through 9, or processOperatorButton if buttonID corresponds to the button for
1
Pressing 6 right after you turn on a calculator is different from pressing 6 after pressing 1 right
after you turn on a calculator. In the first case, the calculator displays 6; in the second, it displays
16.
addition. This chapter is about deciding which of these is the correct thing to do.
In the English example above, if "it is raining out" is true -- i.e., if it is raining out
at the time that the sentence is spoken -- then you should take an umbrella with
you. That is, if the condition is true, you should do the next part of the statement.
This part of the if statement -- the part that you do if the test expression's value is
2
Excluding that sort of grey dreary drippy weather that haunts London and certain times of the
year in Maine, of course.
In Java, execution of an if statement works the same way. First, evaluate the
boolean test. If the value of the test expression is true, then execute the
consequent. If the value of the test expression is false, the consequent is not
executed. In this case, evaluating the test expression is the only thing that happens
during the execution of the if statement. Note that the value of the expression
that matters is its value at the time of its evaluation. If the test is executed at two
different times, it may well have two different values at those times.
In Java, the consequent may be any arbitrary statement (including a block). In this
book, we will always assume that the consequent is a block, i.e., a set of one or
more statements enclosed in braces.
true {
if ( booleanTes tExpression ) consequentStat ements
}
false
if execution path
Note that the same if statement may be executed repeatedly, and the value of the
boolean test expression may differ from one execution of the if statement to the
next. (For example, it may be raining today but not tomorrow, so you should take
your umbrella today but not tomorrow.) The value of the boolean test expression
is checked exactly once each time the if statement is executed, as the first step of
the statement's execution.
12.2.2 Else
The if statement as described above either executes its consequent or doesn't,
depending on the state of the boolean test expression at the time that the if
statement is executed. Often, we don't want to decide whether (or not) to do
something; instead, we want to decide which of two things to do. For example, if
it's raining, we should take an umbrella; otherwise, we should take sunglasses. We
could express this using two if statements:
if ( currentWeather.isRaining() )
{
take(umbrella);
}
if ( ! ( currentWeather.isRaining() ) )
{
take(sunglasses);
}
Recall that ! is the Java operator whose value is the boolean opposite of its single
argument. So if currentWeather.isRaining() is true, then !
(currentWeather.isRaining()) is false; if currentWeather.isRaining() is
false, then ! (currentWeather.isRaining()) is true.
3
It evaluates the expression x < 0, of course, but it "does nothing" that has any lasting effect.
These two conditional statements, one after the other, are intended to express
alternatives. But they don't, really. For example, the two statements each check
the boolean condition currentWeather.isRaining(). This is like looking out
the window twice. In fact, the answer in each of these cases might be different. If
we don't get around to executing the second if statement (i.e., looking out the
window the second time) for a little while, the weather might well have changed
and we'd find ourselves without either umbrella or sunglasses (or with both). The
weather doesn't usually change that often (except in New England), but there are
plenty of things that your program could be checking that do change that quickly.
And, since your program is a community, it is always possible that some other
member of the community changed something while your back was turned.4
Instead of two separate if statements, we have a way to say that these two actions
are actually mutually exclusive alternatives. We use a second form of the if
statement, the if/else statement, that allows us to express this kind of situation.
An if/else statement has a single boolean test condition but two statements, the
consequent and the alternative. Like the consequent, the alternative can be
almost any statement but will in this book be restricted to be a block.
true {
if ( booleanTestExpression ) consequentStatements
}
false
else {
alternativeStatements
}
4
But see chapter 20, where we discuss mechanisms to prevent the wrong things from changing
behind your back.
5
The test for isDigitButton, etc., may seem mysterious right now, and indeed we will simply
assume the existence of these boolean-returning predicates for now. An implementation is
provided in the section on Symbolic Constants, below, and discussed further in the chapter on
Encapsulation.
These ifs inside elses can get to be quite difficult to read, not to mention the
pressure that they put on the right margin of your code as each subsequent if is
further indented.6 In order to avoid making your code too complex -- and too
right-handed -- there is an alternate but entirely equivalent syntax, called the
cascaded if statement. In this statement, an else clause may take an if statement
directly, rather than inside a block. Further, the consequent block of this
embedded if statement is lined up with the consequent block of the original if
statement. So the example above would now read
if ( this.isDigitButton( buttonID ) )
{
this.processDigitButton( buttonID );
}
else if ( this.isOperatorButton( buttonID ) )
{
this.processOperatorButton( buttonID );
}
else if ( this.isEqualsButton( buttonID ) )
{
this.processEqualsButton( buttonID );
}
// and so on until...
else
{
throw new NoSuchButtonException( buttonID );
}
Note that instead of ending with many close braces in sequence, a cascaded if
statement ends with a single else clause (generally without an if and test
expression) followed by a single closing brace.
6
The final lines of such a sequence also contain an awful lot of closing braces.
true {
if ( booleanExpression 1 ) consequentStatements1
}
false
true {
else if ( booleanExpression 2 ) consequentStatements2
}
false
true {
else if ( booleanExpression n ) consequentStatementsn
}
false
else {
alternativeStatements
}
Since an else with no if and test is always executed, such an else must be the
last clause of the cascaded if.
if ( this.isDigitButton( buttonID ) )
{
this.processDigitButton( buttonID );
}
else if ( this.isOperatorButton( buttonID ) )
{
if ( this.isPlusButton( buttonID )
{
this.handlePlus();
}
else if ( this.isMinusButton( buttonID ) )
{
this.handleMinus();
}
else if ( this.isTimesButton( buttonID ) )
{
this.handleTimes();
}
else if ( this.isDivideButton( buttonID ) )
{
this.handleDivide();
}
else
{
throw new NoSuchOperatorException( buttonID );
}
}
else if ( this.isEqualsButton( buttonID ) )
{
// etc.
In this case, these further tests are a part of deciding how to respond to an
operator button, including an operator-specific exception-generating clause. Note
that the additional tests appear inside an if body, not inside an unconditional else.
Using an embedded conditional to further refine a tested condition is a reasonable
design strategy.
if ( theCounter.getValue() > 1 )
{
Console.println( "My, there sure are a lot of them!" );
}
else if ( theCounter.getValue() == 1 )
{
Console.println( "A partridge in a pear tree!" );
}
else if ( theCounter.getValue() = 0 )
{
Console.println( "Not much, is it?" );
}
else if ( theCounter.getValue() < 0 )
{
Console.println( "I'm feeling pretty negative" );
}
else
{
Console.println( "Not too likely, is it?" );
}
it is possible that the counter will be incremented in just such a way that "Not too
likely" might be printed.
If Statement Syntax
An if statement consists of the following parts:
• a (block) statement.
This may optionally be followed by an else clause. An else clause
consists of the following parts:
• a (block) statement
or
These are the ONLY guaranteed properties, other than the declared type of these
names.
In Java, a name is declared final to indicate that its value cannot change. This is
one of the properties that we want our symbolic constants to have: unchanging
value. A value declared final cannot be modified, so you need not worry that extra
visibility will allow another object to modify a constant inappropriately.
It is common, though somewhat arbitrary, to use ints for these constants. There
are some advantages to this practice, and it does simplify accounting. For
example, by defining a set of these constants in sequence one place in your code,
it is relatively easy to keep track of which values have been used or to add new
values.
public static final int ...
PLUS_BUTTON = 10,
MINUS_BUTTON = 11,
TIMES_BUTTON = 12,
...
Of course, you should never depend on the particular value represented by a
symbolic constant (such as EQUALS_BUTTON), since adding a new symbolic name
to the list might cause renumbering. The particular value associated with such a
name is not important.
final
In Java, a name may be declared with the modifier final. This means that
the value of that name, once assigned, cannot be changed. Such a name is,
in effect, constant.
The most common use of this feature is in declaring final fields. These are
object properties that represent constant values. Often, these fields are static
as well as final, i.e., they belong to the class or interface object rather than
to its instances. Static final fields are the only fields allowed in interfaces.
In addition to final fields, Java parameters and even local variables can be
declared final. A final parameter is one whose value may not be changed
during execution of the method, though its value may vary from one
invocation of the method to the next. A final variable is one whose value is
unchanged during its scope, i.e., until the end of the enclosing block.7
Java methods may also be declared final. In this case, the method cannot be
overridden in a subclass. Such methods can be inlined (i.e., made to
execute with especially little overhead) by a sufficiently intelligent
compiler.
Java classes declared final cannot be extended (or subclassed).
7
Final fields and parameters are not strictly speaking necessary unless you plan to use inner
classes. They may, however allow additional efficiencies for the compiler or clarity for the reader
of your code.
This means that it is often useful to declare these static final fields in an interface,
i.e., in the specification of the type and its interactions. In fact, static final fields
are allowed in interfaces for precisely this reason. Thus, the definition of
interfaces in chapter 4 is incomplete: interfaces can contain (only) abstract
methods and static final data members.
For example, the Calculator's interface might declare the button identifiers
described above:
public interface Calculator
{
public static final int PLUS_BUTTON = 10,
MINUS_BUTTON = 11,
TIMES_BUTTON = 12,
...
EQUALS_BUTTON = 27;
}
Now any user of the Calculator interface can rely on these symbolic constants as a
part of the Calculator contract. For example, the isOperatorButton predicate might
be implemented as8
public boolean isOperatorButton( int buttonID )
{
return ( buttonID == PLUS_BUTTON )
|| ( buttonID == MINUS_BUTTON )
|| ( buttonID == TIMES_BUTTON )
|| ( buttonID == DIVIDE_BUTTON );
}
8
Note the absence of any explicit conditional statement here. Using an if to decide which boolean
to return would be redundant when we already have boolean values provided by == and by ||. See
the Sidebar on Using Booleans in the chapter on Statements.
/**
* Symbolic constants representing calculator button IDs.
* The values 0..9 are reserved for the digit buttons,
* which do not have symbolic name equivalents.
*/
public static final int PLUS_BUTTON = 10,
MINUS_BUTTON = 11,
TIMES_BUTTON = 12,
...
EQUALS_BUTTON = 27;
and
/**
* Assumes that the digit buttons 0..9 will be represented by
* the corresponding ints. These values should not be used for
* other buttonID constants.
*/
public boolean isDigitButton( int buttonID )
{
return ( 0 <= buttonID ) && ( buttonID < 10 ) ;
}
Style Sidebar
12.3.2 Syntax
We turn now to a switch statement. A switch statement begins by evaluating the
expression whose value is to be compared against the fixed set of possibilities.
This expression is evaluated exactly once, at the beginning of the execution of the
switch statement. Then, each possibility is compared until a match is found. If a
match is found, "body" statements are executed. A switch statement may also
contain a default case that always matches. In these ways, a switch statement is
For example, we might implement the calculator's act method like this:
switch ( buttonID )
{
case Calculator.PLUS_BUTTON:
this.handlePlus();
break;
// ...
case Calculator.EQUALS_BUTTON :
this.handleEquals();
break;
}
The presence of the break statements as the last statement of each set of actions is
extremely important. They are not required in a switch statement, but without
them the behavior of the switch statement is quite different. See the Switch
Statement Sidebar for details.
A variant form, the labeled break statement, exits all enclosing blocks until
a matching label is found. A labeled break does not exit a method,
however. The labeled form of the break statement looks like this:
label:
blockStatementText
{
// body text
break label;
// more body text
} endBlockStatementText
9
The labeled block may be any statement containing a block, including a simple sequence
statement. The body text may contain any statements, including -- in the case of a labeled break
-- other blocks, so that a labeled break may exit multiple embedded blocks.
10
Here, LabelBreakException is a unique exception type referring to this particular labeled
break statement.
}
That is, the labeled break statement causes execution to continue
immediately after the end of the corresponding labeled block.
label : label :
{
loopBegin
statements
{
statements
break label;
continue label;
statements
statements
}
is equivalent to
blockStatementText
{
try
{
// body text
throw new LabelContinueException();
// more body text
}
catch ( LabelContinueException e )
{
}
} endBlockStatementText
switch ( integralExpression )
matches consequentStatements 1
case: integralConstant :
break;
doesn’t match
matches consequentStatements 2
case: integralConstantn :
break;
doesn’t match
matches consequentStatements n
case: integralConstant :
break;
doesn’t match
default:
defaultStatements
}
12.3.2.3 Variations
It is possible to write a switch statement without using breaks. In this case, when a
case matches, not only its following statements but all statements within the
switch and up to a break or the end of the switch statement will be executed. This
can be useful when the action for one case is a subset of the action for a second
case.
this.handleOperator( buttonID
);
break;
// ....
In this case statement, the same action would be taken for each of the four
operator types. The buttonID pressed is passed along to the operator handler to
allow it to figure out which operator is needed.
A switch statement cannot be used when the comparison values are variable or
drawn from a non-fixed set. That is, if the dispatch expression must be compared
against other things whose values may change, the switch statement is not
appropriate. For example, you wouldn't want to use a switch statement to compare
a number against the current ages of the employees of your company, because
these are changing values.
The switch statement is also not appropriate for expressions that may take on any
of a large range of values. ("Large" is subjective, but if you wouldn't want to write
out all of the cases, that's a good indication that you don't want a switch
statement.) For example, you wouldn't want to do a dispatch on a the title of a
returned library book, testing it against every book name in the card catalog, even
if you represented names as symbolic constants rather than as Strings.11
11
Of course, if you represented the names as Strings, you couldn't use a switch statement because
String is an object type.
12.4 Arrays
Sometimes, what we really want to do when dispatching is to translate from one
representation to another. For example, in constructing a Calculator, we might
want to move from the symbolic constants used to identify buttons above to the
actual labels appearing on those buttons. We might even want to move between
the labels on buttons and the buttons themselves. If our collection of objects is
indexed using an integral type -- either because it is naturally indexed or because
we have used ints as symbolic constants -- we can often accomplish this
conveniently using arrays.
Mailboxes
la
be
ls
Label array
12
Yes, that's right, myButtons[8], the ninth button. Array elements, like the characters in
Strings, are numbered starting from 0.
Button[] pushButtons;
creates a label, pushButtons, suitable for attaching to a Button[], and nothing
more. Note that both initials and pushButtons are label names, notshoebox names.
The names of array types are always label types, although a particular array may
itself be suitable either for holding shoebox (e.g., char) or label (e.g. Button)
types.
0 1 2 3 0 1 2 3 0 1 2 3
h-
Pus - h- h- h-
t
Bu s Pus - Pus - Pus -
t t t
ton Bu s Bu s Bu s
ton ton ton
Button[] pushButtons; pushButtons = new Button[4]; pushButtons[3] = new Button(); pushButtons[1] = pushButtons[3];
13
Pronounced "Button array" or "array of Buttons".
expression.14
The expression
pushButtons = new Button[ numButtons ]
makes the name pushButtons refer to a new array of Button-sized labels. How
many? That depends on the value of numButtons at the time that this statement is
executed.
The statement
String[] buttonLabels = new String[16];
combines all of these forms, creating a name (buttonLabels) suitable for labeling
an array of Strings (String[]), constructing a 16-String array, and then attaching
the name buttonLabels to that array. Note that the text String[] appears twice in
this definition, once as the type and once (with an integral argument between the
brackets) in the array construction expression.
14
An array construction expression can be passed any expression with integral type (byte, short,
int, long, or char) and its size and indexing will be set accordingly.
Array Syntax
Array Type
An array is a label name whose type is any Java type followed by []. The
array is an array of that type. Admissible types include shoebox (primitive)
types, label (object) types, and other array types. An array is declared like
any other Java name, but using an array type. For example, if baseType is
any Java type, then the following declaration creates a label, arrayName,
suitable for affixing on an array of baseType:
baseType[] arrayName;
Array Initialization
Note also that the value of the expression arrayName.length is not the index of
the last element of the array. It is in fact one more than the final index of the
array, because the array's indices start at 0. Attempting to access an array element
with a name smaller than 0 or greater than or equal to its length is an error. In this
case, Java will throw an ArrayOutOfBoundsException.
di di
als als
di
als
Once you construct an array, the number of elements in that array does not
change. However, this immutable value is the number of elements in the array
itself, not the number of elements associated with the name. If the name is used to
refer to a different array later, it may have a different set of legal indices. For
example:
char[] firstInitials = new char[ 10 ];
// firstInitials[3] would be legal, but firstInitials[12] would not.
firstInitials[ 5 ] = 'f';
firstInitials[ 5 ] = 'g';
// changes the value associated with a particular mailbox
Note that the for loop also includes the declaration of index, but that index only
has scope inside the for loop. It is as though index's definition plus the while loop
were enclosed in a block.
For additional detail on for statements, refer to the sidebar.
while ( testExpression )
{
body
incrementStatement;
}
}
The expression testExpression is any single boolean expression. It falls
within the scope of any declarations made in initStatement.
case Calculator.MINUS_BUTTON :
return "-";
// and so on....
This may be somewhat shorter, but not much. It does have the advantage of
making the dispatch on buttonID more explicit. But we can do still better.
statements?
If we create an array containing the button labels, in order, corresponding to the
buttonID symbolic constants, then we can use the buttonID to select the label:
String[] buttonLabels = { "0", "1", "2", "3", "4",
"5", "6", "7", "8", "9",
"+", "-", "*", "/",
// and so on...up to
"="};
In this case, the entire body of our getLabel method might say simply
return this.buttonLabels[ buttonID ];
This example is relatively simple, but in general arrays can be used whenever
there is an association from an index set (such as the buttonIDs) to other values.
The idea is that the index pulls out the correct information for that particular
value. This is a very simple form of a very powerful idea, which we shall revisit
in the chapter on Object Dispatch.
Chapter Summary
• Dispatch is the process of deciding what action needs to be taken based on
one's input. It is essentially a middle management function.
• The type of the array member names is the array's base type. The
array member names may be either shoebox names or label names,
depending on the base type.
• The type of the array is "array of base type". The array name is a
label name.
• The names of array members are written using the array name
followed by an integral index enclosed in square brackets.
Exercises
1. In the section entitled "Many Alternatives", there is an example of a counter
whose getValue() method is invoked repeatedly.
a. Describe an execution sequence in which the value printed would be "My,
there sure are a lot of them!"
b. Describe an execution sequence in which the value printed would be "A
partridge in a pear tree!"
c. Describe how the process of executing this conditional might be
intertwined with the incrementing of the counter to result in the printing of
none of the messages.
of the five different values being printed.
2. Convert the following to a for loop:
int sum = 0;
int i = 1;
while ( i < MAXIMUM )
{
sum = sum + i;
i = i + 2;
}
3. Write a method that takes an array of ints and returns the sum of these ints.
Where, e.g.,
private String sayHappily( String what )
{
return "I'm so happy that ";
}
(You may assume similar definitions for the other emotions, with appropriate
modifications.)
Define the symbolic constants HAPPY, SAD, and ANGRY, and provide a type
for emotion.
6. In the previous exercise, the switch statement contains no breaks. What
happens when we invoke transformEmotionally( SAD, "I am here." )?
7. Using an array, modify the code for transformEmotionally so that it fits in a
single line. The array definition need not fit on that line.
Chapter Overview
• How do I package up implementation details so that a user doesn't
have to worry about them?
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
13~2 Encapsulation Chapter 13
• It's a big, ugly function and you want to hide the "how it works" details
from code that might use it. Giving it a name allows the user to ignore
how it's done.
• It's a common thing to do, and you don't want to have to replicate the code
in several places. Giving it a name allows multiple users to rely on the
same (common) implementation.
1
It is not, however, essential that a method have a succinct description of how it does what it does.
How it accomplishes its task is an implementation detail.
13.2.4 Example
In the example immediately below, we will modify code based on redundancy,
i.e., the repetition rule of thumb. The result will also make our code more succinct
and easier to read. The newly created method will be succinctly summarizable
and a legitimately separable subtask.
Consider a bank account, which might have a method that allows the account's
owner to obtain balance information:
int getBalance( Signatory who ) throws InvalidAccessException
{
if ( ! who == this.owner )
{
throw new InvalidAccessException( who, this )
}
// else
return this.balance;
}
It might also have a withdraw method that allows the owner to remove amount
from the account, returning that amount as cash:
public Instrument withdraw( int amount, Signatory who ) throws
InvalidAccessException
{
if ( ! who == this.owner )
{
throw new InvalidAccessException( who, this )
}
// else
this.balance = this.balance - amount;
return new Cash( amount );
}
Much simpler, much more succinct, and in addition if we later need to modify the
access verification routine, there is only a single place -- verifyAccess() --
where changes will need to be made.
Style Sidebar
Procedural Abstraction
• Use procedural abstraction when a method call would make your code
(at least one of)
• shorter, or
• easier to understand.
• Use parameters to account for variation from one invocation to the next.
• Return a value when the target of an assignment varies; leave the actual
assignment out of the method body.
13.3.1 private
One of the most straightforward ways to protect internal structure -- such as fields
or helper methods -- is to declare them private. We have seen in the section above
how private methods can be used for procedural abstraction -- to break up a long
procedure, to capture common patterns, etc. -- without exposing these functions to
other objects. A method (or other member) declared private can only be called
from within the class.
Beware: This is not the same thing as saying that only an object can call its own
private methods. An object can call the private methods of any other instance of
the same class.
Private is extremely effective at protecting methods and other members from
being used by other objects. However, a member declared private cannot be
accessed from code within a subclass. This means that if you modify code in a
subclass that relies on a private helper method in the superclass, you will have to
13.3.2 Packages
An alternative to the absolute protection of private is the use of packages. A
package is a collection of associated classes and interfaces. You can define your
own packages. Libraries -- such as the Java source code or the cs101 distribution -
- generally define packages of their own. The association among classes and
interfaces in a package can be as loose or as tight as you wish to make it.
Sometimes the association among objects is merely by convenience: many kinds
of objects deal with the same kind of thing. Most of the cs101 packages are of this
sort. Often, it makes sense to define a set of interrelated classes and interfaces in a
single package and to provide only a few entry points into the package, i.e., a few
things that are usable from outside the package. These packages represent
associations by shared interconnectedness. Most of the interlude code is of this
sort. Java defines a large number of packages, some of each kind.
In the bank account, we might well choose to define the interface Instrument
(representing cash and checks, among other things) and classes BankAccount,
CheckingAccount, Cash, etc. in a single package, say finance.
Packages play two roles in Java. The first concerns names and nicknames.
Packages determine the proper names of Java classes and interfaces. The second
role of packages is as a visibility modifier somewhere between private and public.
Any (visible) class or interface can always be accessed by prefacing its name by
its package name, as in java.awt.Graphics or cs101.util.Console. If we declare the
package finance as described above, the interface finance.Instrument would
actually have a distinct name from the interface music.Instrument.
In some cases, you can also access the class more succinctly. If you include the
statement
import packageName.ClassName;
after the (optional) package statement in a file, you may refer to ClassName using
just that name, not the long (package-prefaced) name. So, for example, after
import cs101.util.Console;
the shorter name Console may be used to refer to the cs101.util.Console
class. Similarly,
import packageName.*;
means that any class or interface name in packageName may be referred to using
only its short name, unprefaced by packageName.
Note, however, that this naming role for packages is only one of convenience and
does not provide any sort of actual encapsulation. The use of a shorter name does
not give you access to anything additional. In particular, it does not change the
visibility of anything. Anything that can be referred to using a short name after an
import statement could have been referred to using the longer version of its name
in the absence of an import statement.
There are three exceptions to the need to use an import statement, i.e., three cases
in which the shorter name is acceptable even without an explicit import.
1. Names in the default package can always be referred to using their short
names.
2. Names in the current package (i.e., the package of which the file is a part)
can always be referred to using their short names.
import finance.*;
import music.*;
if both packages contain a type named Instrument. You could, however,
import finance.BankAccount;
import music.*;
since the first of these import statements doesn't shorten the name of the interface
finance.Instrument. If you do import finance.BankAccount and music.*,
you can still refer to the thing returned by BankAccount's withdraw method as a
finance.Instrument.
• The file containing the accessing code also contains one of the following
import statements:
• import packageName.TypeName;
• import packageName.*;
Between private and public are two other visibility levels. One uses the
keyword package. The other is the level of visibility that happens if you do not
specify any of the other visibility levels. This is sometimes called "package"
visibility, although it differs from friendly visibility in other languages and,
additionally, there is no corresponding keyword for it.
A member marked protected visible may be used by any class in the same
package. In addition, it may be referenced by any subclass. It is illegal -- and
causes a compiler error -- if something outside the package, not a subclass, tries to
reference a member marked protected visible.
A member, class, or interface not marked with a visibility modifier is visible only
within the package. It may not be accessed even by code within subclasses of the
defining class or interface, unless they are within the package.
This means that classes and interfaces may be declared without the modifier
public, in which case they can only be used as types within the package.
Members may be declared without a modifier, in which case they can be used
only within the package, or they may be declared protected, in which case they
can be used only within the package or within a subclass. A non-public class or
interface need not be declared in its own separate Java file.
Note, however that although a subclass may increase the visibility of a member, it
may not further restrict visibility. So a subclass overriding a protected method
may declare that method public, but not unmodified (package) or private.
There is no hierarchy in package names. This means that the package
java.awt.event is completely unrelated to the package java.awt; their names just
look similar.
Visibility Summary
A member, class, or interface marked public may be accessed anywhere.
This absence of the keyword public on the class definition means that the class
Cash is accessible only to things inside the finance package. The Cash
constructor is declared protected, so Cash may be created only from within this
package. But the two methods that Cash implements for its interface,
Instrument, must be public because you cannot reduce the visibility level
declared for a method and the interface's methods are declared public.2
Unfortunately, the guarantees of packaging are not absolute. There is nothing to
prevent someone else from defining a class to reside in an arbitrary package. For
example, I could declare a class Thief in package financial, allowing Thief
instances full access to the Cash constructor.
13.3.3 Inheritance
Inheritance can be used as a way of hiding behavior. Specifically, you can create
hidden behavior by extending a class and implementing the additional behavior in
the subclass. Conversely, labeling an object with a name of a superclass type has
the property that it makes certain members of that object invisible.
You cannot invoke a subclass method on an object labeled with a superclass type
that does not define that method, even though the object manifestly has the
method. You can take advantage of this in combination with the visibility
modifiers, for example creating a package-only subclass of a public class. Outside
the package, instances of this subclass will be regarded as instances of the
superclass, but because the subclass type is not available (since it is not visible
outside the package), its additional features cannot be used.
2
The methods of a public interface must be public, but an interface not declared public may have
methods without a visibility modifier.
...
rainyDayFund.withdraw( 10000 );
works fine, but not
rainyDayFund.writeCheck( "Tiffany's", 10000, diamondJim );
That is, the only methods available on an expression whose type is BankAccount
are the BankAccount methods. The fact that this is really a CheckingAccount is
not relevant.
The idea of using superclass types as ways of abstracting the distinctions between
a CheckingAccount and a MoneyMarketFund is an important one. Sometimes
subclasses provide extra (or different versions of) functionality. These distinctions
are not necessarily relevant to the user of the class, who should be able to treat all
BankAccounts uniformly.
Note, however, that the true type of an object is evident at the time of its
construction; it must be constructed using the class name in a new expression.
Also, if the type is visible, an explicit cast expression can be used to access
subclass properties.3
Finally, recall the discussion in chapter 10 on the inappropriateness of inheritance
unless you are legitimately extending behavior. Inheritance should not be used,
for example, when you need to "cancel" superclass properties.
3
For example, (CheckingAccount) rainyDayFund;
Conversely, inner classes can also be used to protect their containing objects. An
inner class lives inside another object and has privileged access to the state of this
"outer" object. For this reason, inner classes can be used to provide access to their
containing objects without revealing these outer objects in their entirety. That is,
an inner class's instance(s) can (perversely) be used to limit access to its
containing class.
Beware: Although an inner class is defined inside the text of another class, there
is no particular subtype relationship established between the inner and outer
classes. For example, an inner class normally does not extend its containing
(outer) class.
Beware: This naming convention looks like package syntax (or field access
syntax), but it is not.
The constructor for a static class is accessed using the class name, i.e.,
new OuterClassName.InnerClassName()
perhaps with arguments as with any constructor.
....
In this case, there is in effect one Check class for each CheckingAccount. This is
precisely what you'd want: each CheckingAccount has a slightly different kind of
Check, varying by who is allowed to sign it, etc.
instantiation. Anonymous classes are a nice match for the event handling
approaches of the Event Delegation chapter.
The syntax for an anonymous class declaration-and-instantiation expression is
new TypeName () { memberDeclarations }
where TypeName is any visible class or interface name and memberDeclarations
are non-static field and method declarations (but not constructors). 4 If TypeName
is a class, the anonymous class extends it; if TypeName is an interface, the
anonymous class implements it. In either case, memberDeclarations must include
any method declarations required to make an instantiable (sub-)class. The
evaluation rules for this expression create a single instance of this new -- and
strictly nameless -- class type. Like a local class, the anonymous class's code may
access any final parameters or local variables within whose scope it appears, and
may use OuterClassName.this to refer to its containing instance if its
declaration/construction expression appears within a non-static member.
Inner Classes
Static Inner Member Local Anonymous
InnerClass
, but name is
OuterClass . outerInstance . accessible
Type Name none
InnerClass InnerClass only within
containing
block.
like local
like static member
Type Name like member (public, variable name,
(public, protected, invisible
Accessibility protected, private, etc.) i.e., only
private, etc.)
within block
Class is
contained (outer) class instance of (outer) class block expression
within
4
If there is necessary instance-specific initialization of an anonymous class, this may be
accomplished with an instance initializer expression. Such an expression is a block that appears at
top level within the class and is executed at instance construction time.
Access to
static
members of yes yes yes yes
containing
class?
Access to
containing
yes, using yes, using yes, using
instance
no OuterClass . OuterClass OuterClass .
(including its this . this this
fields and
methods)?
Access to
parameters
yes, if they are
and local yes, if they are declared
no no declared
variables of final
final
containing
block?
visibility class
visibility class
static class ClassName
ClassName { only possible in
Declaration ClassName { {
instantiation (see
syntax members
members members below).
}
} }
as statement
inside a block
Where at top level in at top level in in anonymous class
(including
declared? OuterClass OuterClass instantiation expression
method,
constructor)
new
new new SuperTypeName()
new {
Instantiation OuterClass . outerInstanceExp
InnerClass
syntax InnerClass r . InnerClass
(...) members
(...) (...)
}
Chapter Summary
• An abstraction relies only on general properties, leaving implementation
details to vary.
• Inner classes are a mechanism for defining one class inside another.
Exercises
To come…
Chapter Overview
• How can I exploit method "ownership" to make objects do what I
want?
©1999 Lynn Andrea Stein. This chapter is excerpted from a draft of Interactive Programming In Java, a
forthcoming textbook from Morgan Kaufmann Publishers. It is an element of the course materials developed
as a part of Lynn Andrea Stein's Rethinking CS101 Project at the MIT AI Lab and the Department of
Electrical Engineering and Computer Science at the Massachusetts Institute of Technology.
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
14~2 Intelligent Objects and Implicit Dispatch Chapter 14
belonging to many different classes, can each be given a method of the same
name (and footprint). In this case, dispatching to the correct code is as simple as
asking the object to perform this method for you.
Fixing the name of the method but leaving the owning object to vary allows you
to do a wide range of things. You can, in effect, pass a method as an argument (by
passing its containing object), return a method from a procedure (by returning its
containing object), or store it in a name or other structure (by storing its
containing object). You can remember who called you and arrange to call that
object back; you can build complex homogeneous structures by exploiting the fact
that one object is associated with other, equally intelligent, objects that can
cooperatively solve problems that none could solve individually.
Each of these mechanisms works because every method is associated with an
object. If the method name is fixed at the time that the program is written, its
target object can be allowed to vary, allowing a runtime decision as to which
piece of code -- which instructions, which method body -- should actually be
executed.
For example, if yesBox is a Checkbox and you want to find out whether yesBox is
currently selected, you can ask yesBox to supply you with that information by
using the method invocation expression yesBox.isSelected(). There's no way
to just ask isSelected(), though: you have to know whose isSelected()
method it is.
Methods encapsulate behavior, but they do not by themselves encapsulate state.
This is the role of objects. An object typically contains both methods -- sets of
instructions -- and persistent information. For example, the Checkbox named by
yesBox has a method called isSelected(), which provides instructions for how
to determine whether yesBox is currently checked. When the expression
yesBox.isSelected() is evaluated, those instructions are executed and the
desired information is produced. But when the method is not being invoked, the
method itself doesn't have any information or action. In contrast, even in the
absence of any method invocation, the Checkbox yesBox contains state indicating
1
This code suffers from a few problems, not the least of which is that it doesn't do anything about
the possibility that o is none of the above. While we'd never write such code in a real application,
we'll skip the else error condition clause here for pedagogic succinctness.
and
public String dimensionToString( Dimension d )
{
return "Dimension: (" + d.getWidth() + "," + d.getHeight() + ")";
}
14.2.3 Variations
The new printObject still has a certain amount of redundant code. We can pushing
the Console.println out of the individual ifs, but then we'll need to remember the
String returned by each toString method. We could write
public void printObject( Object o )
{
String s = "";
if ( o instanceof String )
{
s = this.stringToString( (String) o );
}
else if ( o instanceof Point )
{
s = this.pointToString( (Point) o );
}
else if ( o instanceof Dimension )
{
s = this.dimensionToString( (Dimension) o );
}
Console.println( s );
}
In yet another optimization, we could actually transfer the coercion into the
individual toString methods, calling them on Objects rather than on specialized
types. This makes the methods somewhat less general -- what if they're called on
the wrong type of objects? -- but if we can be sure that they'll always be called
appropriately, it cleans up our dispatch code further.
public String pointToString( Object o )
{
Point p = (Point) o;
return "Point: (" + p.getX() + "," + p.getY() + ")";
}
s = this.dimensionToString( o );
}
Console.println( s );
}
This is just the old pointToString, with a few modifications. First, note that we've
eliminated the argument that pointToString needed. This is because the Point
we're converting is this, i.e. the particular object whose toString() method is
being executed. Second, we don't need a coercion. That's because if this set of
instructions is being executed, it is because this (Point) object's toString() method
has been called, i.e., we must be dealing with a Point. You simply can't call
Point's toString() method on a Dimension (or a String).
//...
calc.displayDigit( this.whichDigit );
}
}
Note that there are ten different NumberButton instances, and each instance will
need to remember which digit it represents.2 When, for example, the 0 button's
buttonPressed method is invoked, it asks its calculator to display its digit, i.e., 0.
The code for other button types is similar.
When we are done writing these button types, we will need to add code to the
calculator dispatcher (or to some other part of the system) that creates all of the
necessary instances of these classes. We might, for example, stick these instances
into an array indexed by the buttonID ints described in chapter 12. This would be
a field of our animate calculator object:
private Object[] buttonObjects = new Object[ Calculator.LAST_BUTTON_ID ];
And then, inside the constructor for that object, we need initialization code:
for ( int buttonID = 0; buttonID < 10; buttonID = buttonID + 1 )
{
this.buttonObjects[ buttonID ] = new NumberButton( buttonID
);
}
There is just one problem: this code won't compile. The array buttonObjects is an
array of Objects. But most Objects don't have a buttonPressed( CalculatorState )
method.
Why wasn't this a problem for the toString method of the object printer? Because
each Object has a toString() method, we didn't have to do anything special to
make the corresponding line of code -- the invocation of the object's toString()
method -- work. However, if we try this trick with a method that isn't possessed
by every object, we will find that our code won't compile. We can resolve this by
2
Once assigned, this digit doesn't change; hence, the field is declared final.
uses its constructor argument -- the object whose act() method it is supposed to
execute -- to determine what it is supposed to do. The method footprint -- act() --
is fixed by the Animate contract. Naming this method there allows the
programmer to write it explicitly into code. Remember, method names cannot be
deduced and runtime, though their target objects can.
There is a similar situation in Java involving the interface Runnable (with a single
method, run() ) and the class Thread. A Thread is started on a particular object,
and the Thread follows the instructions supplied by that object's run() method. By
starting them on instances of different classes of Runnable objects, Threads can
be induced to behave in very different ways. Like act(), run() exploit's Java's
target-based dispatch mechanism to create different kinds of behavior.
But Runnables and run() can be used even without starting a new Thread, simply
because they are fixed names for executable behavior that takes no arguments.3
Suppose that you want to pass a procedure around from one object to another. For
example, suppose that you want to create a secret message and later, you will give
that message to a decoder that will print out your secret message. One way to do
this is to make the secret message a Runnable object and to use the secret
message's run() method as a way for the decoder to get the message out.
public class SecretMessage implements Runnable
{
private String message;
3
Everything said here for run() could be done with another method with a different name, but that
name, too, would have to be fixed when the program is written. For no-arguments executable
code, run() and Runnable make a convenient convention. If you wish to pass arguments to this
procedure, you will need to define your own interface and your own method signature, as Java
offers no standard conventions.
}
}
Now, if we have
SecretMessage message = new SecretMessage( "Meet me at midnight." );
and
SecretDecoder decoder = new SecretDecoder();
to the Java console. The message stays safe inside the SecretMessage as the
SecretMessage is passed from method to method, stored in fields, returned from
methods, and otherwise passed around the system. Because it has a run() method,
that method can eventually be invoked to get the desired behavior from of the
object.
In fact, by the time that this object makes it to the decoder, we might have lost
track of the fact that it is a SecretMessage. Suppose that we have an object
toBeRun, and all that we know about it is that it is a Runnable. We can still ask
decoder.decode( toBeRun );
And now we might find out, for example, that someone has replaced our message
with some Fireworks:
public class Fireworks implements Runnable
{
private Color color;
14.5 Callbacks
A particular circumstance in which this "do the right thing" aspect of Java's
method dispatch is important is called callbacks. A callback is a situation in
which one object has invoked a method of another, and the second object needs to
get some information back to the first without returning from the method
invocation. There are a few prerequisites for callbacks:
1. The invoking object must pass a reference to itself into the original
invocation, or must otherwise indicate whose method is to be "called
back."
2. The invoking method and the invoked method must agree upon the name
of the callback method.
3. The invoked method must record the reference to the invoking object --
the callback target -- e.g., as a parameter to the original invocation or as a
field.
4. At the appropriate occasion, the invoked method must invoke the callback
method on the callback target. The fixed method name is used in this
expression; the reference to the callback target is a variable.
Suppose, for example, that we have an object whose purpose is to create many
separate "web spiders", simple programs that traverse the internet looking for
interesting information.4 Your original object will want to know when the spider
finds interesting information. But the spider won't want to stop executing when it
finds the first interesting piece of information. Instead, the spider should take the
address of its sponsor with it when it goes crawling through the web, and any time
it finds an interesting piece of information it should "call back" the sponsor
object, giving it that information without stopping its execution.
The actual situation for a web spider is a little bit more complicated than this
description because web spiders often don't run on the same computer as their
4
Such programs can be very useful, but you must be extremely careful in writing them. Serious
disasters have been caused by web spiders that got out of control, for example creating so many
spiders that the network filled up with spiders and couldn't sustain its regular traffic.
sponsor and so can't make direct method calls. But we can use this idea as the
framework for some code that illustrates callbacks.
public class SpiderStarter
{
private String interestingStuff = "";
This class provides three methods. The first starts up a Spider, telling the Spider
who its sponsor is. The second provides a way for the Spider to call it back (when
it finds information). The third provides a way for other objects to ask the
SpiderStarter to let it know what information it has collected.5
The definition for Spider might read
public class Spider extends AnimateObject
{
// where to record the callback target
5
Strictly speaking, this code might be subject to problems if we start up more than one Spider. We
really need to protect the interestingStuff using synchronization, as described in part 5 of this
book. These issues don't affect the main point of this chapter, but you should be aware of them if
you want to run a code example like this one.
this.sponsor.informationFound( interestingInfo );
}
This starts a spider going. The "looking for interesting stuff" part of the Spider is
missing, but we can still see how a Spider might take advantage of the callback
mechanism. Since a Spider is an AnimateObject, its act() method will be executed
over and over again. Each time, if it finds some interesting information, it will
invoke its sponsor's informationFound method with the interesting information.
But SpiderStarter's informationFound method just adds the new information to its
information store and returns, so the AnimatorThread that runs the Spider
AnimateObject is free to call its act() object again.
Consider trying to write Spider without the callback. SpiderStarter doesn't call a
method of Spider's directly, so Spider can't return a String that way. Even if
SpiderStarter did call Spider directly, mamaTarantula presumably wants the
Spiders to keep going even after they find their first piece of interesting
information. So it is very important that the individual Spiders have a way to get
information back without stopping their own execution. This is precisely the kind
of situation in which a callback is useful.
Callbacks are a very general mechanism that can be used any time one object
needs to get information to its invoker without returning the information directly.
They require agreement on the name of a method -- perhaps specified by an
interface contract -- that will be used to produce the callback. Callbacks take
advantage of the idea that Java's dispatch mechanism will call the appropriate
piece of code. Good object encapsulation ensures that the information supplied in
a callback gets to the appropriate place.
14.6 Recursion
One final example of how Java's method dispatch mechanisms work is the idea of
recursion. Recursion is the name for a technique in which the same named
method is called over and over again, doing something slightly different each
time. There are two kinds of recursion: structural recursion, which is quite
common in Java and other object-oriented programming languages, and
functional recursion, which is much more prevalent in functional programming
languages.
a.
b.
c.
Various linked lists (following code in text). a. After defining shorty. b. After
defining list. c. After assigning to list.
or even
list = new LinkedList( "First and foremost",
new LinkedList( "Sandwich filling", list ) );
Each of these LinkedList objects either has a next field that refers to another
LinkedList object, or has a next field that is unassigned, i.e., has the value null.
can make use of the fact that that next element is also an intelligent LinkedList
and will be able to convert itself to a String as well.
In writing the code to convert a particular LinkedList instance to a String, there
are two possibilities.
1. Perhaps this is the last element in the list, i.e., this LinkedList object's next
field is null. Then we can solve this problem simply: just convert the
contents of this object to a String.
2. Otherwise, this is not the last element; this object contains a non-null next
field. In this case, converting this LinkedList to a String requires
converting the contents of this object, then adding a comma, then
converting this object's next (LinkedList) to a String. But that next
LinkedList is an intelligent object, too. We can just ask it to convert itself!
It may seem like there's a bit of sleight of hand going on here. This argument may
look suspiciously like a circular definition. But it is not. Let's examine the logic
here carefully.
The first of these is the simple case in which there is no further recursion. As in
the definition, this is called the base case. This condition would apply if we asked
the LinkedList labeled shorty to print itself -- i.e., if we invoked
shorty.toString() -- which would return the String "Not Least". There is only
one element in this list, so printing its contents suffices.
The second case is called the recursive case, the case that relies on recursion to
work. It says, roughly, I know how to convert myself to a String, and my next
knows how to convert itself to a String, so I will simply combine those two
answers. Of course, the way that the next LinkedList element converts itself to a
String relies on this same code....so here it is. Imagine this definition inside the
class LinkedList, where the comment says maybe some methods....
public String toString()
{
if ( this.next == null )
{
return this.contents.toString();
}
else
{
return this.contents.toString()
+ ", " + this.next.toString();
}
}
Once again, the LinkedList has a non-null next field, so once again the recursive
case is invoked, creating "Pen Ultimate" + ", " plus the value of its next field's
toString() method.
The next field of this LinkedList is the same object referred to by the name
shorty. We've already seen how shorty converts itself to a String using the base
case -- returning "Not least" -- so now we can finish off "Pen Ultimate" + ", " +
"Not least". This is returned to list.next, completing "Sandwich filling, Pen
Ultimate, Not least". Finally, this String is returned to the LinkedList labeled
list, and that LinkedList can return its value as a String: "First and foremost,
Sandwich filling, Pen Ultimate, Not least".
6
Actually, to prevent just such situations, the computer may have the ability to detect this
circumstance—an infinite loop—and to object to it by raising an exception.
7
Actually, Java's && and || operators are guaranteed to evaluate their operands from left to write,
proceeding only until the value of the expression is known. In the case of &&, as soon as one
operand is false, no further operands need be evaluated. In the case of ||, evaluation stops as soon
as an operand is true. This means that we could rewrite contains as:
public boolean contains( Object what )
{
return ( ( this.contents == what )
|| ( ( this.next != null )
&& this.next.contains( what ) );
}
Structural recursion is an extension of "the object can handle it" to the case in
which the method invocation expression is contained within the same method that
it invokes. Because the target of the invoked method is a "simpler" object -- one
that is somehow closer to the base case -- this approach ultimately produces a
satisfactory answer.
Chapter Summary
• Objects encapsulate information necessary to make methods effective.
• When multiple classes have methods with the same name, Java chooses
the method that matches the target's (most specific) type.
• Method dispatch based on the target object can be used for other purposes
as well:
• In all recursions, there must be a base case that does not involve
recursion.
Exercises
1. Write toString() methods for an Address object and for a Date object. How
would printObject have to change if it might be asked to print an Address or a
Date as well as a String, Point, or Dimension?
2. Write clone() methods for Point and Dimension. (A clone() method should
create a new copy of its target object.) Write a dispatcher called cloneObject(
Object o ).
3. Write an animate AlarmedTimer class that counts by itself, as the Timer class
of chapter 9 does. In addition, it should have a setAlarm( int interval, Alarmable
who) method. When this method is invoked, the AlarmedTimer should callback
the Alarmable's alarmReached() method every int ticks. Here is Alarmable:
public interface Alarmable
{
public void alarmReached();
}
4. Using the LinkedList code above, add a method that returns the Object that is
the contents of the last element in a LinkedList. For example, list.getLast()
would return "Not least", as would shorty.getLast().
5. Define a recursive structure for a family tree. Each person in the tree should
have a father and a mother, which should be either another person or -- e.g., if the
information were not available -- null. Give this a method that prints all ancestors
of a given individual.
Bonus: Give this structure the ability to print only all female ancestors (using
Console.println).
Extra Bonus: Would your female-ancestor-printer print my father's mother?
Chapter Overview
• How do we design an entity to emphasize its responses to various
events?
In previous chapters, we have seen how an animate object can use its explicit
control loop as a dispatcher, calling appropriate methods depending on what input
it receives. In this chapter, we discuss a style of programming that shifts the
emphasis from the dispatcher to the various handler methods called by that
control loop. Entities designed in this way highlight their responses to a variety of
situations, now called events. An implicit -- behind-the-scenes -- control loop
dispatches to these event handler methods.
This event-driven style of programming is very commonly used in graphical user
interfaces (GUIs). In Java, AWT's paint methods are an example of this kind of
event-driven programming. This chapter closes with an exploration of a portion of
the java.awt package, including java.awt.Component and its subclasses, to
illustrate the structure of programs written in an event-driven style.
©1999 Lynn Andrea Stein. This chapter is excerpted from a draft of Interactive Programming In Java, a
forthcoming textbook from Morgan Kaufmann Publishers. It is an element of the course materials developed
as a part of Lynn Andrea Stein's Rethinking CS101 Project at the MIT AI Lab and the Department of
Electrical Engineering and Computer Science at the Massachusetts Institute of Technology.
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
15~2 Event-Driven Programming Chapter 15
appropriate thing needs dealing with, i.e., whenever the appropriate event arises.1
The result of this transformation is that your code focuses on the occasions when
something of interest happens -- instead of the times when nothing much is going
on -- and on how it should respond to these circumstances. An event is, after all,
simply something (significant) that happens. This style of programming is called
event-driven because the methods that you write -- the event handlers -- are the
instructions for how to respond to events. The dispatcher -- whether central
control loop or otherwise -- is a part of the background; the event handlers drive
the code.
1
Ensuring that those event handler methods will be called is a precondition for event-driven
programming, not a part of it. We will return to the question of precisely how this can be
accomplished later in this chapter.
How do these methods get called? In a traditional control loop architecture, this
might be accomplished using a dispatch loop. For example, we might make Alarm
an Animate and give it its own AnimatorThread. The job of the dispatch loop
would be to wait for and processes incoming (timeout and reset) signals. This
AnimateAlarm's act() method might say:
public class AnimateAlarm extends AnimateObject
{
Buzzer bzzz = new Buzzer();
Of course, we'll have to modify our definition of Alarm to say that it implements
TimeoutResettable:
{
Buzzer bzzz = new Buzzer();
Note that this is a modification of our original Alarm, not of the AnimateAlarm
class. The TimeoutResettable Alarm need not be Animate. In fact, if it is truly
event-driven, it will not be.
This TimeoutResettable Alarm definition works as long as some mechanism --
which we will not worry about just yet -- takes responsibility for dispatching
handleTimeout() and handleReset() calls as appropriate. That dispatcher
mechanism can rely on the fact that our Alarm is a TimeoutResettable, i.e., that it
provides implementations for these methods. The dispatcher that invokes
handleTimeout() and handleReset() need not know anything about the Alarm
other than that it is a TimeoutResettable.
The details of this dispatcher are rather unrealistic. For one thing, it is extremely
specific to the type of event, and extremely general to its event handler
dispatchees. More importantly, in event-driven programming it is quite common
not to actually see the dispatcher.
But dispatchers in real event-driven programs play the same role that this piece of
code does in many ways. For example, the dispatcher doesn't know much about
the object that will actually be handling the events, beyond the fact that it
implements the specified event-handling contract. This dispatcher can invoke
handleTimeout() and handleReset() methods for any TimeoutResettable,
provided that the appropriate TimeoutResettable is provided at construction time.
Different dispatchers might dispatch to different Alarms. In fact, timeout and reset
are sufficiently general events that other types of objects might rely on them.
Another object that might be an event-driven user of timeouts and resets -- and be
controlled by the TimeoutResetDispatcher -- is an image animation. An image
animation is a series of images, displayed one after the other, that give the
impression of motion. In this case, we use the timeout event to cause the next
image to be displayed, while reset restores the image sequence to the beginning.
ImageAnimation simply provides implementations of these methods without
worrying about how or when they will be invoked.
public class ImageAnimation implements TimeoutResettable
{
private Image[] frames;
private int currentFrameIndex = 0;
// To be continued...
The image array frames will hold the sequence of images to be displayed during
the animation. When the ImageAnimation is asked to paint (or display) itself, it
will draw the Image labeled by this.frames[this.currentFrameIndex]. By
changing this.currentFrameIndex, we can change what is currently displayed.
When we do change this.currentFrameIndex, we can make that change
apparent by invoking the ImageAnimation's repaint() method, which causes the
ImageAnimation to display the image associated with
this.frames[this.currentFrameIndex].
We omit the setup code that loads the Images into frames and handles other
construction details.
The next segment of code is the timeout event handler, the helper method that is
called when a timeout occurs. What should the ImageAnimation do when a
timeout is received? Note that the question is not how to determine whether a
timeout has occurred, but what to do when it has. This is the fundamental premise
behind event-driven programming: the event handler method will be called when
appropriate. The event handler simply provides the instructions for what to do
when the event happens. When a timeout occurs, it is time to advance to the next
frame of the animation:
public void handleTimeout()
{
if (this.currentFrameIndex < (this.frames.length - 1))
{
this.currentFrameIndex = this.currentFrameIndex + 1;
this.repaint();
}
}
This code checks to see whether there are any frames left. If the animation is
already at the end of the sequence, the execution skips the if clause and -- since
there is no else clause -- does nothing. Otherwise -- if there's a next frame -- the
execution increments the current frame counter, setting up the next frame to be
drawn. Then, it calls this.repaint(), the method that causes the
ImageAnimation to be redrawn. Recall that the ImageAnimation paints itself
using the image that is associated with this.frames[this.currentFrameIndex].
What about a reset? What should the ImageAnimation do when it receives the
signal to reset? Handling a reset event is much like handling a timeout, but even
simpler. The ImageAnimation simply returns to the first image in the sequence:
public void handleReset()
{
this.currentFrameIndex = 0;
this.repaint();
}
No matter what, we reset the current frame index to 0, then repaint the image
animation with the new frame. Note also that the next timeout will cause the
frame to begin advancing again.
The code to actually repaint the image, which we have not shown here, makes
this.frames[this.currentFrameIndex] appear. As a result,
handleTimeout() works by changing the index to the next frame (until the end
of the animation is reached); handleReset() restarts the image animation by
restoring the index to the beginning index of this.frames once more.
Both Alarm and ImageAnimation are objects written in event-driven style. That
is, they implement a contract that says "If you invoke my event handler method
whenever the appropriate event arises, I will take care of responding to that
event." Alternately, we think of the contract as saying "When the event in
question happens, just let me know." When building both Alarm and
ImageAnimation, the question to ask is, "What should I do when the specified
event happens?"
The role of the event queue is to serve as a drop-off place for events that need to
be handled, sort-of like a To Do list. When an object produces behavior that
constitutes an event, it reports that event to the event queue, which holds on to the
event. The report of the event may be as simple as an indication that something
happened ("Timeout!") or as complex as a complete description of the state of the
world at the time that the event happened (e.g., the complete Wall Street Journal
report on the stock market crash). What is important is that the event queue stores
(remembers) this event report.
In addition to receiving event reports, the event queue also has an active
instruction-follower that removes an event (typically the oldest one) from the
queue and notifies any interested event handler methods. This is the queue-
checker/dispatcher. An event queue also needs some way to figure out who to
notify when an event has happened. In the cases that we explore in this chapter,
there is always a single event queue per hander object, so it is always that object
to which events are reported. In the next chapter, we will discuss a system that
allows finer-grained control.
Consider the TimeoutResettable event handlers described above. A timer might
generate the timeout events and deposit them into the queue. It would then return
to its own business, keeping time and paying no more attention to the event
queue. A separate instruction follower, the event dispatcher, would discover the
timeout event in the queue and invoke the handleTimeout() method of the relevant
party. The structure of this "queue cleaner" would be very similar to the
TimeoutResetDispatcher we saw above.
2
A Runnable's run() method is an exception to this, because the Thread that executes run() has
nothing to go back to doing. When run() completes, the execution of that Thread stops.
3
In the next chapter, we will see that some event queues also provide an event listener registry
service. This is not necessary in the event systems of this chapter, where there is a single event
queue per handler object, but provides yet another layer of flexibility.
keyboard is often a useful adjunct. Graphical user interfaces became the standard
interface for personal computers in the 1980s, though they were invented much
earlier.
15.4.1 java.awt
Java provides a few different ways of making graphical user interfaces. In this
section, we will take a look at the package java.awt. This package contains three
major kinds of classes that are useful for making GUIs. The first of these is
java.awt.Component and its subclasses. These are things that appear on your
screen, like windows and buttons. The immediately following subsection explores
this component hierarchy. The second major GUI class is the class
java.awt.Graphics, which is involved in special kinds of drawing. We will
return to java.awt.Graphics at the end of this chapter. The final group of
classes are the event classes: the java.awt.Event class together with the classes
in the java.awt.Event package. We'll come back and look at AWT Events in the
event delegation chapter. Here we'll deal only with one (pseudo-)event, painting.
In the remainder of this section, we are going to focus on Components and, in a
bit, Graphics.
The event that we will be concerned with here is painting. That is, this is the event
that occurs when a window or other user interface object becomes visible, is
resized, or for other reasons needs to be redrawn. This event happens to a
Component. In order to handle this event you need to know what the current state
of the drawing is, including both its coordinate system and what if anything is
currently visible. That information is held by a Graphics. So when the event
4
This is a screen shot from Claris Home Page 3.0.
15.4.2 Components
A component is a thing that can appear on your screen, like a window or a button.
The parent of all component classes is java.awt.Component. The Component
class embodies a screen presence. You can't have a vanilla Component, though;
you can only have an instance of one of its subclasses. (In fact,
java.awt.Component is an abstract class. See the sidebar in Chapter 7 for further
detail on abstract classes.)
Although you can't instantiate Component directly, Component has several useful
subclasses. One group of these is the set of stand-alone widgets that let you
interact with your screen in stereotyped ways. There are many GUI widgets built
in to java.awt. These include Checkbox, Choice, List, Button, Label, and
Scrollbar. In addition, there are several Menu variants that don't extend
Component directly, but also provide useful widgets. Each of these widgets is
pretty well able to handle its GUI behavior -- showing up, disappearing, allowing
selections to be made, etc. In the event delegation chapter, we will see how to use
these GUI components to allow the user to communicate with your application;
for example, to have something smart happen when a selection is made. (This
involves customizing these widgets' event handlers.)
Another set of components are called Containers. These Components extend
java.awt.Container (which itself is an abstract class extending
java.awt.Component.) Containers are components that can have other components
inside them. For example, a java.awt.Window (which is a kind of component) can
have a java.awt.Scrollbar.
In this chapter, we will confine ourselves to one simple component behavior:
painting itself. To do this, we will use a generic Component, called Canvas, that
you can instantiate. The java.awt.Canvas class doesn't do anything special, but
you can either use it as a generic component or extend it to get specialized
behavior. We will make a Canvas that paints itself with a special picture.
15.4.3 Graphics
A java.awt.Graphics (sometimes called a "graphics context") is a special kind of
object that knows how to make pictures appear. A Graphics uses a coordinate
system to keep track of locations within it. The origin of this coordinate system --
the point (0,0) -- is in the upper left-hand corner. Moving right from this point
involves increasing the first (x) coordinate, so (100, 0) is 100 pixels to the right of
the origin, along the top edge of the Graphics.5 Moving down increases the
second (y) coordinate, so (0,50) is 50 pixels below the top of the Graphics, along
its left-hand side. (100,50) is a point that is not on either the top or left edge; it is
100 pixels to the right and 50 pixels down.
Each Graphics has methods such as drawLine, fillOval, and setColor that
allow you to create pictures. For example, if you had a Graphics named g,
g.fillOval(100,100,10,10) would make it display a 10-pixel by 10-pixel
circle with its upper left-hand corner at position 100, 100. If you called
g.setColor(Color.red) first, the circle would be red. A complete list of the
methods of a java.awt.Graphics, together with a brief description of each, can be
found in the java.awt.Graphics reference.
A Graphics is not the kind of object that you are likely to create or have hanging
around. You will probably never run into the Graphics associated with GUI
widgets or containers. However, each time that your Canvas needs to redisplay
itself, it will be handed a Graphics context with which to do that redisplaying. So
there will be times when your code will be given a Graphics to use.
5
A pixel, short for picture element, is the smallest visible unit on your computer's screen. A
higher resolution display is one that has more pixels in the same amount of space, i.e., one with
smaller pixels. Java Graphics are delineated in pixels.
your component's paint method is called, it should ask whatever Graphics object
is supplied to it to drawRect( int x, int y, int width, int height).6
For example, if paint were called with a Graphics named g, the instructions might
read
g.fillRect(0,0,20,20);
to draw a square in the upper left-hand corner of the Component. The whole
method would read
public void paint( Graphics g )
{
g.fillRect(0,0,20,20);
}
A Component's paint method is an event handler. This means that the
Component's paint method is the set of instructions describing the Component's
response to a request to redisplay itself. It triggers whenever Java finds that
something has happened that requires the component to redisplay itself.
6
drawRect takes four arguments: the upper left hand coordinates and the size coordinates. All
measurements are in pixels -- tiny boxes that make up your screen -- and the origin -- the point
(0,0) -- is in the upper left-hand corner of the component. These are called "screen coordinates".
Graphics objects have lots of other drawing methods, too. See the java.awt.Graphics
documentation for a comprehensive listing.
contract -- how the component decides to paint itself, for example -- is something
that the rest of the system doesn't have to worry about.
A corollary benefit, then, is that different kinds of components can handle the
same event in very different ways. We saw this early in this chapter where the
same pair of events -- timeout and reset -- were used to run both an alarm and an
image animation. In these two objects, the timeout event meant very different
things. The alarm handled a timeout by turning on its buzzer; the image animation
switched to the next image each time a timeout occurred.
The GUI painting system that we have described uses this polymorphism to great
advantage. When a component like a Canvas is asked to paint itself in a Graphics,
it may draw a simple picture using the Graphics supplied. When a widget like a
Button is asked to paint itself, it creates labeled region of the screen appropriate
for clicking into. A Checkbox may paint itself as a square, with or without an X in
it depending on whether the Checkbox is checked. A container such as a Window
not only paints itself, it also asks each of the components contained inside it to
paint themselves. The Window doesn't need to know anything about how these
components appear; it simply asks them to paint themselves in the way that they
know best.
Chapter Summary
• By hiding the central control loop, we shift emphasis from explicit
dispatch to event handler methods.
• Event driven programming separates things that happen from how they're
handled.
Scrollbar.
Exercises
1. Define a TimeoutResettable that simply prints to the Console whenever an
event happens. The message printed should differ depending on which
event occurs. Implement it in a purely event driven style, i.e., assuming
that something else will manage the event dispatching.
2. Describe a scenario in which an event occurs to the object in the previous
exercise. Explain the sequence of action.
Chapter Overview
• How do I separate an entity's core behavior (model) from its on-
screen appearance (view)?
Permission is granted to copy and distribute this material for educational purposes only, provided that the
following credit line is included: "©1999 Lynn Andrea Stein." In addition, if multiple copies are made,
notification of this use must be sent to ipij@ai.mit.edu or ipij@mkp.com.
16~2 Event Delegation and java.awt Chapter 16
system, each Component handles its own events. In this chapter, we will look at a
more complicated two-layer model which further separates the event producer
from the event consumer. This mechanism, which relies on an explicit listener
registration protocol, is at the heart of the event handling system in Java's AWT
versions 1.1 and later.
The problem of GUI design is illustrative of larger design issues. The event-
delegation approach described in this chapter arises from our desire to separate
what happens in the GUI (such as clicking a button) from the behavior that this
causes (such as playing a song). To make this work, we connect GUI objects
(such as Buttons) to application objects (such as SongPlayers) indirectly, through
special EventListener objects. The EventListener records the appropriate
connection between GUI events and application behavior, keeping these details
out of both GUI and application components. This allows significant flexibility: a
single application behavior may be invoked by many different GUI events; one
GUI event may give rise to many application behaviors; or the relationship
between GUI events and application behavior may be remapped by a running
program, for example.
This kind of indirect coupling through a Listener object is a useful technique in a
wide range of applications.
1
The event delegation mechanism described in this chapter is used in Java's AWT version 1.1 and
later and also in the Java Swing toolkit. In Java's AWT version 1.0, all event handling was done
using a system closer to that of chapter 15.
The first of these lines makes it possible for the user to type in the TextField. The
second highlights all of the text in the TextField, so that what the user types will
replace the text displayed there.
Now suppose that the user types her name into the TextField box, replacing the
highlighted text previously displayed. If the user ends her name by typing the
return key, this causes an action event to be registered on the TextField. In other
words, something has happened and we are ready to invoke the appropriate event
handler.
Now, we are ready to print our greeting. For example, we might say
Console.println( "Hello, " + reference_to_nameText.getText() );
Each TextField has a getText() method that returns the String displayed in the
TextField at the time of the getText() invocation. So, if we execute code along
these lines, the text
Hello, Galadriel
should appear on the Java Console. There are, of course, a few issues:
1. Where does this code appear? That is, who is handling the event, and in
what method?
2. How does that event handler access the TextField called nameText (in
order to ask it to getText())?
This is where Java's event delegation system comes in.
cs101.awt.DefaultFrame
A cs101.awt.DefaultFrame is a cs101 utility provided to make it easy to put
up a window containing a single Component. The DefaultFrame takes care
of sizing, activating the window's close box, causing the window to appear
on the screen, etc.
If c is a Java component, it can be made to appear on the screen using
new cs101.awt.DefaultFrame( c ).init();
// Create a Frame around the component.
This class actually keeps track of which TextField it wants to associate itself with.
We can create a particular FieldHandler associated with nameText using the
construction expression
new FieldHandler( nameText )
Now, when this FieldHandler's actionPerformed method is invoked -- when the
action happens -- the FieldHandler will use nameText's getText() method to print
a greeting to Galadriel.
Of course, we might want to hang on to that FieldHandler once we've created
it....It will come in handy in another few paragraphs.
The answer is that Java needs to be notified that the FieldHandler is interested in
this TextField's action events. To return to our earlier analogy, the FieldHandler
needs to subscribe to the TextField's action event clipping service.
Now, when Galadriel finishes typing, an action event will not only be generated
but also forwarded to nameHandler to handle.
16.2.4 Recap
The code that creates this situation is distributed over the paragraphs above. Here
is the entire setup code. It might, for example, appear in a main method or in the
constructor of an entity that provided the name-greeting behavior described at the
beginning of this section.
// Set up the TextField.
TextField nameField = new TextField( "Type your name here" );
nameField.setEditable( true ); // Allows user typing.
nameField.selectAll(); // Highlights current text.
2
or simply nameText.addActionListener( new FieldHandler( nameText ) );
Some kinds of events, like ActionEvents, are notable mostly for happening. For
example, when a Button is clicked, an ActionEvent is generated. If you know
what Button was clicked to generate the ActionEvent, you really know
everything worth knowing about the ActionEvent. (If you don't know what
Button was clicked, you can find out by asking the ActionEvent; see below.) An
ActionEvent is also generated when the return key is typed in a TextField (as we
have seen), indicating that the text is complete. In this case, you need to know
both which TextField and, perhaps, what text was typed. But once you know what
TextField generated the ActionEvent, you can ask the TextField for its text. So
the internal structure of an ActionEvent is not likely to be of much interest.
Different kinds of events have methods that provide access to the different kinds
of information that you'd want if you were dealing with a mouse click or a
window close. These event methods are summarized in the AWT Events segment
of the appendix AWT Quick Reference. For example, a MouseEvent has a few
methods that are especially worth noting. If the MouseEvent is labeled mickey,
then
Every AWTEvent also has a getSource() method. This method returns the Object
to whom the event happened. For example, we could have replaced the
actionPerformed method of our FieldHandler class with the definition
public void actionPerformed( ActionEvent ae ) {
TextField theField = (TextField) ae.getSource();
Console.println( "Hello, " + theField.getText() );
}
This text uses the TextField that is the source of the action event, rather than the
TextField that is handed to the FieldHandler constructor, as the target of the
getText() method.3
Some AWTEvents, such as MouseEvent, are ComponentEvents. Every
ComponentEvent also has a getComponent() method that returns the same thing
as its getSource() method, but typed as a Component.
A variety of useful event types and their methods are documented in the AWT
Events segment of the AWT Quick Reference appendix.
3
In this case, we could simply eliminate the constructor, making the FieldHandler definition look
like this:
public class FieldHandler implements ActionListener {
public void actionPerformed( ActionEvent ae ) {
TextField theField = (TextField) ae.getSource();
Console.println( "Hello, " + theField.getText() );
}
}
The ActionListener defined above will do the trick quite nicely for our
TextField. The ActionListener interface only had a single method to
implement. Other listener interfaces are more complex, though. For example, the
MouseListener interface defines five methods:
public interface MouseListener extends EventListener {
public void mouseClicked( MouseEvent mickey );
public void mouseEntered( MouseEvent mickey );
public void mouseExited( MouseEvent mickey );
public void mousePressed( MouseEvent mickey );
public void mouseReleased( MouseEvent mickey );
}
If you want to be able to respond to mouse clicks, you will need to implement a
class that has an appropriate mouseClicked method. But the
MouseMotionListener interface specifies a contract with five distinct methods. If
clicks are the only kind of MouseEvent that you want to respond to, it would be
rather annoying to have to implement each of the other four methods just to be
able to write the one (mouseClicked) that we need. Our class definition might
say
public class MouseHandler implements MouseListener {
public void mouseClicked( MouseEvent mickey ) {
// Interesting code goes here...
}
public void mouseEntered( MouseEvent mickey ) {}
public void mouseExited( MouseEvent mickey ) {}
public void mousePressed( MouseEvent mickey ) {}
public void mouseReleased( MouseEvent mickey ) {}
}
Not very concise or beautiful, but necessary if we are to implement the interface
directly. After all, an interface is a contract and implementing the interface means
fulfilling the whole contract, not just a part of it.
4
Except PaintEvent, which uses the mechanism described in the previous chapter rather than the
listener registration system described here.
We might, for example, make our own class -- our own specialized TextField --
that is born with its own FieldHandler:
public class HandledTextField extends TextField {
public HandledTextField() {
ActionListener nameHandler = new FieldHandler( nameText );
nameText.addActionListener( nameHandler );
}
}
Now each HandledTextField is born with its own FieldHandler. This is similar to
AnimateObject's creating its own AnimatorThread, rather than expecting someone
else to create the AnimatorThread on its behalf.
Using inner classes,5 we can make this innovation do even more work for us.
Inner classes are a relatively advanced feature of Java, and they add only to the
aesthetics of this program, not to its functionality. They do provide a little bit
more protection for code from unanticipated use, a feature that we can exploit.
5
See chapter 12 for details.
Chapter Summary
• EventListeners are interfaces promising particular sets of event handler
methods. There are Listeners for groups of related AWT event types, such
as mouse motion events, in the package java.awt.event.
Exercises
1. Define a class that implements java.awt.event.MouseListener and
extends the mouseClicked(MouseEvent) method by printing the
coordinates of the point on which the mouse had clicked. You may also want
to make use of the class java.awt.event.MouseAdapter. (Bonus: also print
the components of the previous mouse click.)
2. Now define a class that extends java.awt.Canvas and sends its mouse events
to your MouseListener.
4. Define a class that extends java.awt.Canvas and looks like a (black and
white) Japanese flag, i.e., it has a circle at (100,100). Make the circle change
color when the mouse is over your Canvas. (Hint: mouse enter, mouse leave.)
About Applets
An applet is a piece of Java code that can be run under certain network
browsers (and appletviewer, a Java program). Applets are embedded in
html and invoked by viewing the page (or running appletviewer on the
page). Every applet extends java.applet.Applet, which in turn extends
java.awt.Panel. When an applet is invoked, an instance is created (i.e.,
its constructor is called). No arguments are supplied to the constructor;
instead, there is html syntax for providing parameters to applets. At applet
creation time, three methods are called in sequence:
1. the applet's constructor
Other than this information, applets are largely outside the scope of this
course.
• AWT Components
• Component
• Canvas
• Basic Widgets
• ItemSelectable Widgets
• Text Widgets
• Container
• Graphics
• AWT Events
AWT Components
An awt component is a visible gui entity. The root of the component
hierarchy is the class java.awt.Component.
The class java.awt.Component is abstract. Its methods include:
• Choice, a popup menu, which contains a set of items. One of these items
may be seleted.
Component
This abstract class is the root of the visible AWT classes. All of the other
classes extend it and inherit its methods. However, few subclasses rely on
the full generality of Component and most of these methods are unused in
most of Component's subclasses. If you want to exploit the behavior of
Component, it is common to extend Canvas, the generic instantiable
Component.
java.awt.Component
• abstract
• extends Object
° public void repaint( long time, int x, int y, int width, int height );
ComponentListener l );
• Override these to specify a different size from the default for your
Component
Canvas
java.awt.Canvas
A Canvas is an instantiable Component. It has no additional behavior
beyond that inherited from Component. It is often extended and
customized, particularly by overriding its paint() method or supplying
specialized event listeners.
• extends Component
• public Canvas();
• Canonical usage:
• common to addMouse(Motion)Listener
The major methods of each widget type are listed in separate sidebars, below.
Basic Widgets
java.awt.Label
• constructors
° public Label();
• Canonical usage:
• constructors
° public Button();
• Canonical usage:
• implements ItemSelectable
• constructor
° public Choice();
• item management:
• Canonical usage:
• implements ItemSelectable
• constructor
° public Checkbox();
• Canonical usage:
• Not a Component!
• constructor
° public CheckboxGroup();
Text Widgets
These three widget types provide varying kinds of text display and editing.
TextField is by far the simplest, especially as it relies on ActionEvents
triggered only when editing is "complete", e.g., when the user hits return.
TextEvents allow finer-grained access to the user's editing.
java.awt.TextComponent
Parent class for TextArea, TextField; less commonly used directly.
• extends TextComponent
• constructors
° public TextField();
• Size in columns, i.e., how wide can this line of text be. Note also
interacts with component size.
• If echoChar is set, text typed into the TextField will appear as echoChar.
This is useful if the information typed is secret, e.g., a password.
• Canonical usage:
• extends TextComponent
• constructors
° public TextArea();
° public TextArea ( String text, int rows, int columns, int scrollbars
);
• Size in columns and rows, i.e., how wide and high can this block of text
appear. Note also interacts with component size.
Container
This abstract class is the root of the parent (container) AWT classes. All of
the other container classes extend it and inherit its methods. Only classes
extending Container can be a parent to another Component.
Container has four important subclasses:
Java.awt.Container
• abstract
• protected Container();
• extends Window
• implements MenuContainer
• constructors
° public Frame();
• Make the Frame as small as it can be while still holding all of its
contained Components
• What to do when you're done with the Frame and its contained
Components:
• Canonical usage:
java.awt.Panel
• extends Container
• constructors
° public Panel();
• Canonical usage:
• Implements Serializable
• Constructors:
° Public Dimension();
java.awt.Point
• Implements Serializable
• Constructors:
° Public Point();
° public int x;
° public int y;
• extends java.awt.geom.Rectangle2D
• implements Serializable
• constructors:
° public Rectangle();
° public int x;
° public int y;
• Geometric predicates:
• Geometric computations:
Graphics
A Graphics is the "screen" object on which all primitive drawing takes
place. Graphics support a huge number of methods. You will almost always
use the Graphics passed into a paint method when it is invoked by Java.
java.awt.Graphics
• abstract
• extends Object
• constructor
° protected Graphics();
° public abstract void clearRect( int x, int y, int width, int height );
° public void draw3DRect( int x, int y, int width, int height, boolean
raised );
° public abstract void drawArc( int x, int y, int width, int height, int
startAngle, int arcAngle );
° public abstract boolean drawLine( int startX, int startY, int endX,
int endY );
° public abstract void drawOval( int x, int y, int width, int height );
° public void fill3DRect( int x, int y, int width, int height, boolean
raised );
° public abstract void fillArc( int x, int y, int width, int height, int
startAngle, int arcAngle );
° public abstract void fillOval( int x, int y, int width, int height );
° public abstract void fillRect( int x, int y, int width, int height );
• Get rid of a Graphics you no longer need (only if you've created it!)
° public abstract boolean drawImage( Image image, int dx1, int dy1,
int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, ImageObserver
observer );
° public abstract boolean drawImage( Image image, int dx1, int dy1,
int dx2, int dy2, int sx1, int sy1, int sx2, int sy2, Color
background, ImageObserver observer );
AWT Events
There are many different kinds of events in the package java.awt.event. Each
is a subclass of java.awt.event.AWTEvent. It is unlikely that you would ever
need to create an awt event. Instead, you are likely to write Listeners that
handle these Events.
java.awt.AWTEvent
The most important method of the class java.awt.AWTEvent is
Listener
Event Class Adapter Class Listener/Adapter methods
Interface
ActionEvent ActionListener -- actionPerformed( ActionEvent e );
mouseClicked( MouseEvent e );
mouseEntered( MouseEvent e );
mouseExited( MouseEvent e );
MouseListener MouseAdapter
mousePressed( MouseEvent e );
MouseEvent
mouseReleased( MouseEvent e );
mouseDragged( MouseEvent e );
MouseMotionListener MouseMotionAdapter
mouseMoved( MouseEvent e );
windowActivated( WindowEvent e );
Window gains focus, etc.
windowClosed( WindowEvent e );
successfully closed Window
windowClosing( WindowEvent e );
WindowEvent WindowListener WindowAdapter user requestedWindow close
windowDeactivated( WindowEvent e );
Window loses focus, etc.
windowDeiconified( WindowEvent e );
windowIconified( WindowEvent e );
windowOpened( WindowEvent e );
componentHidden( ComponentEvent e );
componentMoved( ComponentEvent e );
ComponentEvent ComponentListener ComponentAdapter
componentResized( ComponentEvent e );
componentShown( ComponentEvent e );
componentAdded( ContainerEvent e)
ContainerEvent ContainerListener ContainerAdapter
componentRemoved( ContainerEvent e)
focusLost( FocusEvent e );
keyPressed( KeyEvent e );
KeyEvent KeyListener KeyAdapter keyReleased( KeyEvent e );
keyTyped( KeyEvent e );
Contents
• Program File
• Interface Declaration
• Class Declaration
• Field Declaration
• Method Declaration
• Expressions
• Statements
Program File
A program file (.java) contains:
Optional: 0 or 1 occurences.
Interface Declaration7
Optional: 0 or 1.
Exactly 1.
This name is a Java type that can be used
interface anywhere it is visible. If the class is defined in a
InterfaceName
package other than the default, its proper name
is packageName.InterfaceName, though this
may be abbreviated using import statements.
Optional: 0 or 1.
If present, this interface extends the specification
extends provided by OtherInterfaceName. Specifically,
OtherInterfaceName all methods and (static final) fields
specified by InterfaceName are required for
any instance implementing interface
InterfaceName.
Class Declaration7
Optional: 0 or 1.
Optional: 0 or 1.
final
If final, no subclasses of this class can be
defined, i.e., no other class can extend this one.
Optional: 0 or 1.
Exactly 1.
This name is a Java type that can be used
class ClassName anywhere it is visible. If the class is defined in a
package other than the default, its proper name is
packageName.ClassName, though this may be
abbreviated using import statements.
Optional: 0 or 1.
If present, this class "inherits" implementation
from OtherClassName. Specifically, all non-
extends OtherClassName
private fields and methods defined for
OtherClassName are available to/through class
3
ClassName and its instances, unless they are
explicitly overridden.
Field Declaration4
Optional: 0 or 1.
Optional: 0 or 1.
Optional: 0 or 1.
final
If final, the field must be initialized at its creation
time, and its value cannot later be changed.
Exactly 1.
TypeName
May be a class or interface name, one of the eight
Java primitive types, or an array type.
fieldName Exactly 1.
Optional: 0 or 1.
; Exactly 1.
Method Declaration4
Optional: 0 or 1.
Optional: 0 or 1.
Optional: 0 or 1.
Optional: 0 or 1.
Optional: 0 or 1.
Exactly 1.
methodName Exactly 1.
Expression
Every Java expression has both a type and a value. A Java expression is made up
of exactly one of the following.
prefixOperator expression
expression
postfixOperator These are operator expressions. They
include unary operations such as - and ++,
expression1 infixOperator binary operations such as *, >, and =, and the
expression2
ternary conditional operator.
booleanExpression ?
expression1 : expression2
Statement
Java statements are executable code with neither type nor value. A Java statement
is made up of exactly one of the following (though its pieces sometimes contain
other statements).
block.
A conditional statement is executed
by first evaluating
booleanExpression. If its value is
true, statement is executed. If the
value of booleanExpression is
false, statement is not executed.
In either case, execution continues
immediately after this statement.
2. If booleanExpression. is false,
execution skips statement and
continues at anotherStatement.
integralValues is a constant
expression of the same integral type.
Any number of case
integralValue : and statement
lines, and at most one default:, can
be intermixed in any order in the
switch body.
2. If a throw statement is
encountered during the execution of
tryBlock and the type of the thrown
object matches thetype of one of the
catch clauses, execution
exitstryBlock and continues in the
catchBlock of the first catch
clause whose type matches the type
of the thrown object. During
execution of catchBlock, name is
bound to the thrown object. After
completing this catchBlock,
execution continues at
finallyBlock (if present).
3. If execution of tryBlock is
interrupted for any other reason -- a
thrown object that doesn't match a
catch clause, a break or continue
statement -- execution exitstryBlock
and continues at finallyBlock (if
present). If execution of
finallyBlock completes normally
(or if finallyBlock is absent,
execution continues abnormally, i.e.,
as though finallyBlock had thrown
the object or raised the break or
continue. That is, execution
continues exiting blocks until the
throw, break, or continue is
resolved.
applet
A Java program capable of running embedded in a web browser or other
specialized applet environment. Contrast application.
application
A Java program capable of running "standalone". Contrast applet.
animacy
A Java Thread that enables concurrent execution, e.g., of a self-animating
object. See the chapter on Self-Animating Objects.
animate object
See self-animating object.
array
A structure for holding many Things of the same type.
argument
A value supplied to a method when it is invoked. During the execution of the
method body, this value is named by the matching method parameter.
arithmetic operator
An operator that computes one of the arithmetic functions. See the chapter on
Expressions.
assignment
The association of a name with a value. See the chapter on Things, Types, and
Names.
B
backslash
\ Used in character escapes.
binary operator
An operator that takes two operands. See the chapter on Expressions.
binding
See name binding.
bit
A single binary digit.
bitwise operator
An operator that computes a bit-by-bit function such as bitwise complement.
See the chapter on Expressions.
block
A segment of code that begins with a { and ends with the matching }. See the
section on Blocks in the chapter on Statements.
body
The body of a method, class, or interface, i.e., either a method body, a class
body, or an interface body.
boolean
C
call
See invocation.
call path
The sequence of method invocation (instructions followed by a Thread) that
led up to the currently executing method body. Unless execution exits
abruptly, each of these invocations will return, one at a time, in this order,
along the reverse of the call path, i.e., the return path.
carriage return
One of two line-ending characters. (The other is line feed.) So named after an
archaic device called a typewriter in whose early models the carriage (i.e.,
paper-bearing part) literally needed to be returned to the other side of the
typewriter at the end of each line.
case-sensitive
Distinguishing between upper and lower case letters.
cast expression
An expression involving a type and an operand whose value is the same as its
operand but whose type is the type supplied. Contrast coercion.
catastrophic failure
An exceptional circumstance so incapacitating that your program cannot hope
to prevent or deal with it. At this point, the only hope is in recovery.
catch statement
A particular kind of Java statement, typically used with exceptions, that
receives a thrown object. See the chapter on Exceptions.
character
A single letter, digit, piece of punctuation, or piece of whitespace. In Java,
represented by the primitive type char, using unicode notation, and occupying
sixteen bits, and by the object type Char. See the sidebar on Java Primitive
Types in the chapter on Things, Types, and Names.
character escape
A special sequence indicating a character other than by typing it directly.
Especially useful for non-printing characters, such as carriage return.
class
A (user-definable) type from which new objects can be made. See the chapter
on Classes and Objects.
class body
The portion of a class definition containing the class's members. The portion
of a class definition enclosed by { }. See the chapter on Classes and Objects
and the Java Chart on Classes.
class object
The object representing the class itself, i.e., the factory. Itself an instance of
class java.lang.Class.
code
An excerpt from a program.
coercion
Treating an object of one type as though it were of another type. Contrast cast.
See the chapter on Expressions.
comment
Text embedded in a program in such a way that the Java compiler ignores it.
Intended to make it easier for people to read and understand the code.
comparator
compiler
The utility that transforms your Java code into something that can be run on a
Java virtual machine.
compount assignment
A shorthand assignment operator (or expression) that also involves an
arithmetic or logical operation.
concatenation
The gluing together of two strings.
condition
In a conditional statement, the boolean expression whose value governs
whether the consequent or the alternative is executed.
conditional
A compound statement whose execution depends on the evaluation of a
boolean expression. Consists of a condition, a consequent, and an optional
alternative.
conjunction
D
data
Values, as opposed to executable code. Things that might be associated with
names such as variables, parameters, or fields. See also state.
data repository
A kind of object whose primary purpose is to store data. See the chapter on
Designing with Objects.
debug
To attempt to eliminate bugs from your program.
declaration
A statement associating a name with a type. Once the name has been declared,
it can be used to refer to Things of the associated type. See the chapter on
Things, Types, and Names.
default value
The value associated with a name that has been declared but not assigned a(n
initial) value. See the sidebar on Default Initialization in the chapter on
Things, Types, and Names.
default visibility
Also called package visibility. The visibility level of an unmodified interface,
class, method, field, or constructor. Visible to only within the package.
definition
A statement that both declares and initializes a name. See the chapter on
Things, Types, and Names.
design
The process of figuring out what your program should do and how it should
accomplish it.
disjunction
dot
See period.
double precision floating point
A representation for rational numbers (and an approximation for real
numbers) that uses 64 bits of storage. In Java, implemented by the primitive
type double. See floating point.
down cast
A cast from superclass to subclass. May be invalid; should be guarded.
E
embedded
The property of being in an environment (or system) and interacting with it.
entity
A member of the community. A conceptual unit consisting of an object or set
of objects that is (implicitly or explicitly) persistent and that interacts with
other entities.
environment
Where an entity is embedded. What the entity interacts with.
error checking
Code (often a conditional statement) designed to catch illegal values or other
potential problems, and to correct them, before or as they arise. A way to
avoid bugs in your program. An important part of design.
evaluate
To compute the value of an expression.
event
1. Something that happens.
2. A special kind of object used in event-driven programming to record the
occurrence of a particular event (in the conventional sense). See the chapters
on Event-Driven Programming and Event Delegation.
event-driven programming
A style of programming in which an implicit (often, system-provided) control
loop activates event handler methods when a relevant event occurs. See the
chapters on Event-Driven Programming and Event Delegation.
event handler
In event-driven programming, a method that is called when a relevant event
occurs. See the chapters on Event-Driven Programming and Event Delegation.
exit condition
The condition under which the repeated execution of a loop stops. Formally
called the termination condition for the loop.
exception
A special kind of Java object used to indicate an exceptional circumstance.
Typically used in conjunction with throw and catch statements. See the
chapter on Exceptions.
execute
To follow the instructions corresponding to a statement or program.
expression
A piece of Java code with a type and a value, capable of being evaluated.
Contrast statement. See the chapter on Expressions.
extend
To reuse the implementation supplied by a superclass through inheritance.
F
factory
A class, metaphorically, for its instances.
feature
1. A deliberately designed and generally beneficial aspect of a program.
2. post hoc. A bug, when discovered by a user after it's too late to fix it.
field
A data member of a class, i.e., a name associated with each instance of a class
(if not static) or with the class object itself (if static). See the chapter on
Classes and Objects.
field access
An expression requiring an object and a field name. Its type is the declared
type of the field and whose value is the value currently associated with that
field.
floating point
A representation for rational numbers (and an approximation for real
G
getter, getter method
A method that exists solely to provide read access to a field. Formally called a
selector.
global variable
A term with no meaning in Java.
graphical user interface
A user interface that makes use of windows, icons, mouse, etc., and is
typically implemented in an event-driven style. Sometimes abbreviated GUI.
guard expression
A test that prevents execution of a potentially dangerous statement.
GUI
An acronym for graphical user interface.
H
hyphen
- Used as the unary and binary subtraction operator and to indicate negative
numbers.
I
identifier
The formal term for a name.
idiot proofing
A not very tactful name for error checking, especially as concerns interaction
with the user.
if, if/else
instructions
Code, generally statements, explaining how to do something. Followed step
by step by an instruction follower.
integer type
interface
1. The common region of contact between two or more entities.
2. (Java) A formal statement of method signatures and constants defining a
type and constraining the behavior of objects implementing that interface.
invocation
To call a method, i.e., execute its body, passing arguments to be associated
with the method's parameters.
J
Java console
A place in every Java environment from which standard input is read and to
which standard output is written. I/O to the Java console is provided by
cs101.util.Console, java.lang.System.in, and java.lang.System.out.
jelly
An exceedingly sticky concoction made from the juice of a fruit, often a
grape, ideally purple. See also peanut butter.
K
keyword
A word with special meaning in Java. All Java keywords are reserved, i.e.,
cannot be used as Java names.
L
label name
A name capable of referring to something of an object type, i.e., anything not
of a primitive type. See the chapter on Things, Types, and Names.
left-hand side
In an assignment, the expression representing the shoebox or label to which
the value is assigned.
literal
A Java expression to be read literally, i.e., at face value. Only the primitive
types plus strings have corresponding literal expressions. See the sidebar on
Java Primitive Types in the chapter on Things, Types, and Names.
local
Another term for a variable. Short for local variable.
local variable
The formal term for a variable.
logical operator
An operator that computes an arithmetic function such as conjunction or
disjunction. See the chapter on Expressions.
loop
A construct by which a sequence of statements is executed repeatedly,
typically until some exit condition is met.
M
member
A constructor, field, or method of a class. Alternately, a (static) field or
(abstract) method of an interface. Also member (inner) classes or interfaces.
See the chapter on Classes and Objects.
method
An executable class member. Consists of a signature plus a body (unless
abstract). When a method is invoked on an argument list, the body is
executed with each of the method's parameter names bound to its
corresponding argument.
method body
The portion of a method that contains executable statements. When a method
is invoked (on a list of arguments), its body is executed within the scope of the
parameter bindings, i.e., with the parameter names bound to the corresponding
arguments.
method footprint
The name plus the ordered list of parameter types of a method. An object may
have at most one method with any particular footprint. Contrast method
signature. See the chapter on Interfaces.
method invocation
See invocation.
method overriding
When a subclass redefines a method or field that would otherwise be inherited
from its superclass.
method overloading
When one object has two or more methods with the same name (but different
footprints), typically performing different functions.
method signature
The specification of a method's name, ordered list of parameter types, return
type, and exceptions, possibly including modifiers. Contrast method footprint.
See the chapter on Interfaces.
modifier
mutator
The formal name for a setter method.
N
name
A Java expression that refers to a particular object or value. Examples include
variables, parameters, fields, class names, and interface names. Every name
has an associated type (fixed when the name is declared). Within its scope, the
name is generally bound to a value (of the appropriate type). See the chapter
on Things, Types, and Names.
name binding
The association of a name with a value, typically through assignment or
through parameter binding during method invocation. The details of this
association depend on whether the name is a shoebox name or a label name,
i.e., of primitive or object type.
no-args
Taking no arguments or, more properly, having no parameters.
null
null character
The character with unicode number 0. Not to be confused with the non-value
null.
O
object
object type
In Java, any type other than one of the eight primitive types. All object types
are named by label names.
operand
One sub-expression of an operator expression. See the chapter on Expressions.
operator
The part of an operator expression that determines the particular relationship
of the operands to the expression's value. See the chapter on Expressions.
operator expression
An expression involving an operator (e.g., +) and one or more operands.
Typically, the value of the expression is a particular function of the operands,
with the operator specifying what function. See the chapter on Expressions.
overriding
See method overriding.
overloading
See method overloading.
P
package
1. A named group of Java interface and class definitions.
2. The default visibility level of an unmodified interface, class, method, field,
or constructor. Visible only within the package(1).
paper
An archaic but amazingly persistent storage medium made of wood pulp.
Reported continually over the last half-century to be destined for imminent
obsolesence with the incipient advent of the paperless office. Sometimes used
with a typewriter.
parameter
A name whose scope is a single invocation of the method to which it belongs.
Declared in the method signature. When the method is invoked on a list of
arguments, each parameter is bound to the corresponding argument prior to
(and with scope over) the execution of the method body.
parameter binding
The form of name binding that occurs when a method is invoked on a list of
arguments. Each of the method's parameters is bound to the corresponding
argument, i.e., the first parameter to the first argument, etc.
peanut butter
A gooey brown paste made by grinding up a certain legume, often consumed
with jelly between two slices of very bland white bread.
period
. Sometimes also called "dot". Used in method invocation and field access
expressions, package naming, and as a decimal point.
persistent
Existing even when not currently the subject of the coder's or computer's
attention.
pointer
A term with no meaning in Java.
postfix
Coming after.
prefix
Prior to.
primitive type
In Java, one of byte, short, int, long, float, double, char, or boolean.
All primitive types are named by shoebox names. See the sidebar on Java
Primitive Types in the chapter on Things, Types, and Names.
private
follows.
v. To compose a program. See also incremental program design, debug.
programmer
A person who develops (designs, writes, debugs, modifies) a program.
programming language
A language in which one writes a program. For the purposes of this book,
Java.
protected
Q
R
read, read access
Interacting with a name by obtaining its associated value, or with an object by
reading the value(s) of one or more of its fields.
recipe
The instructions for how to do something. A class is a recipe for the behavior
of its instances. A constructor is the recipe for how to make an instance of its
class.
recovery
Also recovering from error. What a program ought to do after something has
gone wrong; patch things up as well as possible and move on. If things are
disasterous enough (e.g., after a catastrophic failure), this can be a significant
task. It is facillitated by design that anticipates the need for eventual recovery.
reference type
The formal term for the types named by a label name.
reserved word
A word that cannot be used as an identifier in Java, typically because it is a
keyword.
resource library
A class that exists to hold methods that don't logically belong to any particular
object, or other (typically system-wide) resources. Typically not an
instantiable class. See the chapter on Designing with Objects.
return
A statement whose execution causes normal termination of the execution of a
method body. If the return statement contains an expression, its type must
match the return type of the method. In this case, the expression is evaluated
prior to exiting the method body and the value of this expression is the return
value of the method invocation.
return path
See call path.
return type
The type of the value returned by a method invocation. The first item in a
method declaration.
return value
The value returned by a method invocation.
rule
A proto-method. Consists of a specification and a body. See the chapter on
Statements and Rules.
rule body
The set of statements detailing how a rule is to be accomplished. A proto-
method body. See the chapter on Statements and Rules.
rule specification
The information needed and provided by a rule. A proto-signature. See the
chapter on Statements and Rules.
S
scope
The expanse of code within which a name has meaning, i.e., is a valid
expression. See the note on Scoping in the chapter on Expressions. Not quite.
selector
The formal name for a getter method.
self-animating object
An object or entity with its own animacy, i.e., one that runs concurrently and
persistently. See the chapter on Self-Animating Objects.
side effect
A change to something that occurs as a consequence of evaluating an
expression. For example, an assignment.
signature
See method signature.
slash
/ Used to delineate comments and as the division operator.
software
Another term for computer program.
standard input
The stream which reads from the Java console. Bound to
java.lang.System.in.
standard output
The stream which writes to the Java console. Bound to
java.lang.System.out.
state
What is true of a program or entity at a specific time. Especially the current
set of associations of values with names.
statement
A piece of executable Java code. Has neither type nor value. Contrast
expression. See the chapter on Statements and Rules.
static
A modifier indicating a member of a class (rather than of its instances).
stream
A persistent Java object which permits the reading or writing of multiple
sequential values. Represents a connection to another (potentially non-Java)
entity. Used for input or output.
string
subclass
A class that inherits from another, i.e., extends that other. Contrast
superclass. See the chapter on Inheritance.
superclass
A class that is inherited from by another, i.e., the other extends the
superclass. Contrast subclass. See the chapter on Inheritance.
T
target
In a method invocation expression, the object whose method it is.
termination condition
The formal name for an exit condition.
test
1. A crucial part of program development in which program behavior is
exercised in an attempt to find failures, or bugs.
2. In a conditional statement, another name for the boolean expression known
as the condition.
Thing
The nouns of Java, including Things of primitive type and objects. See the
chapter on Things, Types, and Names.
this
A Java (label) name that is bound to the current instance. Because it refers to
an instance, static members are outside of its scope .
throw statement
A particular kind of Java statement, typically used with exceptions, that
causes an object to be thrown and thereby circumvents the typical return
trajectory. See the chapter on Exceptions.
throws clause
The part of a method signature which specifies any exceptions thrown by that
method. See the chapter on Exceptions.
top-down design
An approach to design that starts with the highest level, most abstract, or
largest things in your system and proceeds by decomposing them.
top level
Immediately inside the containing structure. Top level within a class means
inside the class body but not inside any other structure.
trinary operator
An operator that takes three operands. See the chapter on Expressions.
type
A partial specification of the Thing. In Java, a type is either a primitive type or
an object type. See the chapter on Things, Types, and Names.
type-of-thing name-of-thing rule
The rule that says: to declare a name, first state its type, then state its name.
typewriter
An archaic device vaguely resembling a keyboard attached directly to a
printer with no intervening memory. Requires paper.
U
unary operator
An operator that takes one operand. See the chapter on Expressions.
unbound
The state of a label name when it is not associated with an object, i.e., has no
object referent. In this case, the label name is associated with the non-value
null.
unicode
The representation used by Java for characters.
up cast
A cast or coercion from subclass to superclass. Always valid.
user
1. A human being, with respect to a computer program.
2. A piece of code, with respect to another piece of code, especially an
interface. Contrast implementor.
user interface
The portion of a program with which a (human) user interacts. See also
graphical user interface.
V
value
Either a primitive value or an object.
value type
The formal term for the types named by a shoebox name.
variable
A Java name that has scope only from its declaration to the end of the
enclosing block. Variables are formally called local variables; sometimes, this
is abbreviated to locals.
virtual field
A piece of state within an object that is not stored directly as a field, but is
instead calculated using the values of other fields of the object. Must be
accessed using a getter method as there is no field to read directly.
virtual machine
The utility that actually runs your (compiled) Java program.
visibility
Whether a class, field, method,field, or constructor can be used by a particular
piece of code. Visibility levels include private, protected, default (or package),
and public.
void
The return type of a method whose invocation does not return anything.
Contrast null.
W
white bread
A substance resembling styrofoam, but with less taste and texture. Generally
available in uniform white squares with pale brown edges, called crusts, that
must be removed before serving to small children. Useful mostly to keep the
peanut butter and jelly from getting on your fingers.
write, write access
Interacting with a name by changing its associated value, or with an object by
changing the value of one or more of its fields.
X
Y
Z