Sei sulla pagina 1di 176

Lecture Notes

ON

OBJECT ORIENTED
PROGRAMMMING

B. Tech (CSE/IT)
Semester-Vth
CODE: PCC-CS-503
SUBJECT NAME: OBJECT ORIENTED PROGRAMMING
NO OF CREDITS: 3

MODULE-1: ABSTRACT DATA TYPES


Decomposition & Abstraction, Abstraction Mechanisms – parameterization, specificatio n,
Kind of Abstractions – Procedural, Data, Type hierarchies, Iteration. ADT implementation –
Concrete state space, concrete invariant, abstraction function. Implementing operations,
illustrated by the Text example

MODULE-2: FEATURES OF OBJECT-ORIENTED PROGRAMMING


Encapsulation, object identity, polymorphism – Inheritance in OO design. Implementing OO
language features.- Classes, Objects and variables, Type Checking, Procedures - Commands
as methods and as objects, Exceptions, Polymorphic procedures, Templates, Memory
management

MODULE-3: DESIGN PATTERNS


Introduction and classification. Creational Pattern – Abstract Factory Pattern, Factory Method,
Singleton, Structural Pattern – Bridge, Flyweight, Behavioural Pattern - The iterator pattern,
Observer pattern, Model-view-controller pattern

MODULE-4: GENERIC TYPES AND COLLECTIONS


Simple Generics, Generics and Subtyping, Wildcards, Generic Methods, Set Interface, List
Interface, Queue Interface, Deque Interface, Map Interface, Object Ordering, SortedSet
Interface, SortedMap Interface

MODULE-5: GUIS. GRAPHICAL PROGRAMMING WITH SCALA AND SWING


Swing components, Laying out components in a container, Panels, Look & Feel, Event listener,
concurrency in swing.

MODULE-6: THE SOFTWARE DEVELOPMENT PROCESS


Requirement specification and analysis, Data Model, Design, Implementation, Testing.

REFERENCES
1. Barbara Liskov, Program Development in Java, Addison-Wesley, 2001
Table of Contents
MODULE 1: .................................................................................................................................. 6
ABSTRACT DATA TYPES ................................................................................................................. 6
1.1 Decomposition and Abstraction ............................................................................................... 6
1.2 ABSTRACTION .................................................................................................................... 7
1.2.1 Abstraction by Parameterization...................................................................................................................................8
1.2.2 Abstraction by Specification .........................................................................................................................................9
1.2.3 Kinds of Abstractions..................................................................................................................................................10
1.3 Invariants ............................................................................................................................. 10
1.3.1 Immutability ................................................................................................................................................................10
1.3.2 Immutable Wrappers Around Mutable Data Types..................................................................................................14
1.3.3 How to Establish Invariants ........................................................................................................................................14
1.4 Abstract Data Types (ADTs)-Implementation .......................................................................... 15
1.4.1 The Stack ADT...........................................................................................................................................................16
1.4.2 The Queue ADT.........................................................................................................................................................20
Module-2 ................................................................................................................................... 26
2.1 INTRODUCTION..................................................................................................................... 26
2.1.1 Abstraction...................................................................................................................................................................26
2.1.2 Encapsulation ..............................................................................................................................................................27
2.1.3 Polymorphism .............................................................................................................................................................27
2.1.4 Inheritance ...................................................................................................................................................................27
2.1.5 Association ..................................................................................................................................................................28
2.1.6 Aggregation .................................................................................................................................................................28
2.1.7 Composition ................................................................................................................................................................28
2.2 Characteristics of OOP ........................................................................................................... 29
2.2.1 Abstraction...................................................................................................................................................................29
2.2.2 Interfaces......................................................................................................................................................................30
2.2.3 Encapsulation ..............................................................................................................................................................31
2.2.4 Inheritance ...................................................................................................................................................................32
2.2.5 Polymorphism .............................................................................................................................................................33
2.3 Object-Oriented Programming Basics With Java...................................................................... 35
2.3.1 Encapsulation and data hiding ....................................................................................................................................36
2.3.2 The interaction of objects using polymorphism .........................................................................................................37
2.4 Object-Oriented Programming By Example ............................................................................. 39
2.5 A Start-to-Finish Example ...................................................................................................... 60
2.6 TEMPLATES ..................................................................................................................... 64
2.6.1 Function templates ......................................................................................................................................................64
2.6.2 Class Templates ..........................................................................................................................................................67
2.6.3 Template Specialization ..............................................................................................................................................68
2.6.4 Non-type parameters for templates.............................................................................................................................70
2.6.5 Templates and multiple-file projects...........................................................................................................................71
2.7 Memory Management..................................................................................................... 71
2.8 Object Oriented Memory Management ............................................................................ 74
Module-3 ................................................................................................................................... 75
Design Patterns .......................................................................................................................... 75
3. 1 Introduction to Design Patterns ............................................................................................ 75
3.2 Classification of Design Patterns............................................................................................. 75
3.2.1 Creational Patterns.......................................................................................................................................................75
3.2.2 Structural Patterns........................................................................................................................................................84
3.2.3 Behavioral Patterns......................................................................................................................................................90
MODULE 4: ................................................................................................................................ 99
GENERIC TYPES AND COLLECTIONS............................................................................................ 100
4.1 Introduction........................................................................................................................ 100
4.2 Generics ............................................................................................................................. 101
4.2.1 Generics versus Templates .......................................................................................................................................102
4.3 Generics and Subtyping ....................................................................................................... 103
4.4 Wildcards with extends ....................................................................................................... 104
4.5 Wildcards with super........................................................................................................... 105
4.5.1 Wildcards versus Type Parameters..........................................................................................................................106
4.5.2 Wildcard Capture ................................................................................................................................................108
4.5.3 Restrictions on Wildcards .........................................................................................................................................109
4.6 Generic Methods and Varargs .............................................................................................. 111
4.7 Set Interface ....................................................................................................................... 112
4.7.1 Set Interface Basic Operations ..................................................................................................................................113
4.7.2 Set Interface Bulk Operations ...................................................................................................................................115
4.8 The List Interface ................................................................................................................ 116
4.8.1 Collection Operations................................................................................................................................................117
4.8.2 Positional Access and Search Operations.................................................................................................................117
4.8.3 Iterators......................................................................................................................................................................119
4.8.4. Range-View Operation ............................................................................................................................................120
4.9 Queue Interface .................................................................................................................. 123
4.10 The Deque Interface .......................................................................................................... 125
4.10.1 Insert ........................................................................................................................................................................125
4.10.2 Remove ...................................................................................................................................................................125
4.10.3 Retrieve....................................................................................................................................................................125
4.11 The Map Interface............................................................................................................. 126
4.11.1 Map Interface Basic Operations .............................................................................................................................127
4.11.2 Map Interface Bulk Operations ..............................................................................................................................128
4.11.3 Collection Views.....................................................................................................................................................129
4.12 Object Ordering ................................................................................................................ 130
4.12.1 Writing Your Own Comparable Types.................................................................................................................131
4.13 The SortedSet Interface ..................................................................................................... 133
4.13.1 Set Operations .........................................................................................................................................................134
4.13.2 Standard Constructors.............................................................................................................................................134
4.13.3 Range-view Operations ..........................................................................................................................................134
4.13.4 Endpoint Operations ...............................................................................................................................................135
4.13.5 Comparator Accessor.............................................................................................................................................136
4.14 The SortedMap Interface ................................................................................................... 136
4.14.1 Map Operations.......................................................................................................................................................136
4.14.2 Standard Constructors.............................................................................................................................................137
MODULE 5: ............................................................................................................................ 138
GRAPHICAL PROGRAMMING WITH SCALA AND SWING................................................. 138
5.1 Basics-Creating a Window................................................................................................... 139
5.2 Responding to an event........................................................................................................ 140
5.3 The scala.swing package...................................................................................................... 141
5.3.1 Classes SwingApplication and SimpleSwingApplication ......................................................................................142
5.4 Laying out components ....................................................................................................... 142
5.4.1 A strongly typed, concise container interface...........................................................................................................142
5.4.2 Extending Layout Container.....................................................................................................................................143
5.5 Reacting to events ............................................................................................................... 144
5.5.1 Java Swing Listeners versus scala.swing Reactions ................................................................................................145
5.5.2 Publishers and Reactors............................................................................................................................................146
5.5.3 Actions.......................................................................................................................................................................146
5.6 List Views and Tables ......................................................................................................... 147
5.6.1 Class ListView ..........................................................................................................................................................147
5.6.2 Renderers...................................................................................................................................................................148
5.6.3 Class Table ................................................................................................................................................................150
5.7 CustomPainting .................................................................................................................. 150
5.8 Panels................................................................................................................................. 151
5.9 Concurrency in Java Swing.................................................................................................. 155
5.9.1 Initial Threads............................................................................................................................................................156
5.9.2 Event Dispatch Thread..............................................................................................................................................156
5.9.3 Worker Threads and SwingWorker.........................................................................................................................157
MODULE 6: ............................................................................................................................ 159
THE SOFTWARE DEVELOPMENT PROCESS ....................................................................... 159
6.1 The Software Life Cycle ...................................................................................................... 159
6.2 Requirement Analysis ......................................................................................................... 160
6.3 Requirement Specification ................................................................................................... 162
6.3.1 Data Models ..............................................................................................................................................................162
6.3.1.1 Subsets .................................................................................................................. 162
6.3.1.2 Relations................................................................................................................ 163
6.3.2 Requirements Specification ......................................................................................................................................166
6.4 Designs............................................................................................................................... 168

MODULE 1:

ABSTRACT DATA TYPES

1.1 Decomposition and Abstraction

The basic paradigm for tackling any large problem is clear-we must “divide and rule”. Our goal
in decomposing a program is to create modules that interact with one another in simple, well-
defined ways. If we achieve this goal, different people will be able to work on different modules
independently, without needing much communication among themselves, and yet the modules
will work together. In addition, during program modification and maintenance, it will be
possible to modify some of the modules without affecting all of the others.
When we decompose a problem, we factor it into separable subproblems in such a way that

 Each subproblem is at the same level of detail.


 Each subproblem can be solved independently.
 The solutions to the subproblems can be combined to solve the original problem.
Sorting using merge sort is an elegant example of problem solving by decomposition. It breaks
the problem of sorting a list of arbitrary size into the two simpler problems of sorting a list of
size two and merging two sorted lists of arbitrary size.
From Babbage’s day onward, people have recognized the utility of such things as macros and
subroutines as decomposition devices for programmers. It is important to recognize, however,
that decomposition is not a panacea and when used improperly, it can have a harmful effect.
Furthermore, large or poorly understood problems are difficult to decompose properly. The
most common problem is creating individual components that solve the stated subproblems but
do not combine to solve the original problem. This is one of the reasons why system integratio n
is often difficult.
For example, imagine creating a play by assembling a group of writers, giving each a list of
characters and a general plot outline, and asking each of them to write a single character’s lines.
The authors might accomplish their individual tasks admirably, but it is highly unlikely that
their combined efforts will be an admirable play. It would probably lack any sort of coherence
or sense.
Abstraction is a way to do decomposition productively by changing the level of detail to be
considered. When we abstract from a problem, we agree to ignore certain details in an effort to
convert the original problem to simpler one.
The paradigm of abstracting and then decomposing is typical of the program design process;
decomposition is used to be break the software into components that can be combined to solve
the original problem; abstractions assist in making a good choice of components. We alternate
between the two processes until we have reduced the original problem to a set of problems we
already know how to solve.

1.2 ABSTRACTION
The process of abstraction can be seen as an application of a many-to-one mapping. It allows
us to forget information and consequently to treat things that are different as if they were the
same. We can simplify our analysis by separating attributes that are relevant from those that
are not. It is crucial to remember, however, that relevance often depends upon context.
For example, A more specifically computer- oriented example that is useful in many programs
is the concept of a “file”. Files abstract from raw storage and provide long-term, online storage
of named entities. Operating Systems differ in their realizations of files; for example, the
structure of the filenames differs from system to system, as does the way in which the files are
stored on secondary storage devices.
In recent years, programmers have become dissatisfied with the level of abstraction generally
achieved in high level language programs. Consider, for example, the program fragments in
Figure 1.1:
//search upwards
found=false;
for(int i=0; i<a.length; i++)
if(a[i]==e)
{
z=i;
found=true;
}
//search downwards

found=false;
for(int i=<a.length; i>=0; i++)
if(a[i]==e)
{
z=i;
found=true;
}

Figure 1.1:

At the level of abstraction defined by the programming language, these fragments are clearly
different: if there is an occurrence of e in a, one fragment finds the index of the first occurrence
and the other, the index of the last. If e does not occur in a, one sets i to a.length and the other
to -1.

A preferable alternative is to design into the language mechanisms that allow programmers to
construct their own abstractions as they need them. One common mechanism is the use of
procedures. By separating procedure definition and invocation, a programming langua ge
makes two important methods of abstraction possible: abstraction by parameterization and
abstraction by specification.

 Abstraction by parameterization abstracts from the identity of the data by replacing them
with parameters. It generalizes modules so that they can be used in more situations.
 Abstraction by specification abstracts from the implementation details (how the module is
implemented) to the behaviour users can depend on (what the module does). It isolates
modules from one another’s implementations; we require only that a module’s
implementation supports the behaviour being relied on.
1.2.1 Abstraction by Parameterization
Abstraction by parameterization, through the introduction of parameters, allows us to represent
a potentially infinite set of different computations with a single program text that is an
abstraction of all of them. For example,
X*x + y*y
describes a computation that adds the square of the value stored in the variable x to the square
of the value stored in the variable y.
On the other hand, the lambda expression
λ x,y : int(x*x+y*y)
describes the set of computations that square the value stored in some integer variable, which
we shall temporarily refer to as x, and add it to the square of the value stored in another integer
variable, which we shall temporarily call y. In such a lambda expression, we refer to x and y
as the formal parameters and x*x+y*y as the body of the expression. We invoke a computatio n
by binding the formal parameters to arguments and then evaluating the body.
For example,
λ x,y : int(x*x+y*y)(w,z) is identical in meaning to w*w+z*z
Programmers often use abstraction by parameterization without even noticing that they are
doing so. For example, suppose we ned a procedure that sorts an array of integers a. At some
time in the future, we shall probably have to sort some other array, perhaps even somewhere
else in the same program. It is highly unlikely, however, that every array we need to sort will
be named a; we therefore invoke abstraction by parameterization to generalize the procedure
and thus make it more useful.
Abstraction by parameterization is an important means of achieving generality in programs. It
is an extremely powerful mechanism. Not only does it allow us to describe a large (even
infinite) number of computations relatively simply, but it is easily and efficiently realizab le in
programming languages.
1.2.2 Abstraction by Specification
Abstraction by specification allows us to abstract from the computation described by the body
of a procedure to the end that procedure was designed to accomplish. We do this by associating
with each procedure a specification of its intended effect and then considering the meaning of
a procedure call to be based on this specification rather than on the procedure’s body.
We are making use of abstraction by specification whenever we associate with a procedure a
comment that is sufficiently informative to allow others to use that procedure without looking
at its body. A good way to write such comments is to use pairs of assertions.
The requires assertion (or precondition) of a procedure specifies something that is assumed to
be true on entry to the procedure.
The effects assertion (or postcondition) of a procedure specifies something that is assumed to
be true at the completion of any invocation of the procedure for which the precondition was
satisfied.
Consider, for example, the sqrt procedure in Figure 1.2. Because a specification is provided,
we can ignore the body of the procedure and take the meaning of the procedure call y=sqrt(x)
to be “If x is greater than zero when the procedure is invoked then after the execution of the
procedure, y is an approximation to the square root of x.” Notice that the requires and efforts
assertions permit us to say nothing about the value of y if x is not greater than zero. This is
important, since a user might otherwise quite reasonably assume that sqrt(0) returned a
meaningful answer.
In using a specification to reason about the meaning of a procedure call, we follow two distinct
rules:
1. After the execution of the procedure, we can assume that the post condition holds
provided the precondition held when the call was made.
2. We can assume only those properties that can be inferred from the post condition.
The two rules mirror the two benefits of abstraction by specification.
The first asserts that users of the procedure need not bother looking at the body of the procedure
in order to use it. They are thus spared the effort of first understanding the details of the
computations described by the body and then abstracting from these details to discover that the
procedure really does compute an approximation to the square root of its argument.
The second rule makes it clear that we are indeed abstracting from the procedure body, that is,
omitting some irrelevant information.
1.2.3 Kinds of Abstractions
Abstraction by parameterization and by specification are powerful methods for program
construction. They enable us to define three different kinds of abstractions : procedural
abstraction, data abstraction, and iteration abstraction. In addition, each procedural, data, and
iteration abstraction will incorporate both methods within it.
Procedural abstraction is a powerful tool. It allows us to extend the virtual machine defined by
a programming language by adding a new operation. This kind of extension is most useful
when we are dealing with problems that are conveniently decomposable into independent
functional units.
Data abstraction consists of a set of objects and a set of operations characterizing the behaviour
of the objects. It is often more fruitful to think of adding new kinds of data objects to the virtua l
machine. The behaviour of the data objects is expressed most naturally in terms of a set of
operations that are meaningful for those objects. This set includes operations to create objects,
to obtain information from them, and possibly to modify them.
Iteration abstraction is used to avoid having to say more than is relevant about the flow of
control in a loop. A typical iteration abstraction might allow us to iterate over all the elements
of any set without constraining the order in which the elements are to be processed.
Type Hierarchy allows us to abstract from individual data types to families of related types.
All members of the family have operations in common; these common operations are defined
in the supertype, the type that is the ancestor of all the others, which are its subtypes. Type
families abstract from the details that distinguish members of a family from one another to their
commonalities. They allow programmers to ignore the difference most of the time.

1.3 Invariants
The property of a good abstract data type is that it preserves its own invariants. An invariant
is a property of a program that is always true, for every possible runtime state of the program.
Immutability is one crucial invariant: once created, an immutable object should always
represent the same value, for its entire lifetime. Saying that the ADT preserves its own
invariants means that the ADT is responsible for ensuring that its own invariants hold. It
doesn’t depend on good behavior from its clients.

When an ADT preserves its own invariants, reasoning about the code becomes much easier. If
you can count on the fact that Strings never change, you can rule out that possibility when
you’re debugging code that uses Strings – or when you’re trying to establish an invariant for
another ADT that uses Strings. Contrast that with a string type that guarantees that it will be
immutable only if its clients promise not to change it. Then you’d have to check all the places
in the code where the string might be used.

1.3.1 Immutability
Here’s a specific example:

/**
* This immutable data type represents a tweet from Twitter.
*/
public class Tweet {

public String author;


public String text;
public Date timestamp;

/**
* Make a Tweet.
* @param author Twitter user who wrote the tweet.
* @param text text of the tweet
* @param timestamp date/time when the tweet was sent
*/
public Tweet(String author, String text, Date timestamp) {
this.author = author;
this.text = text;
this.timestamp = timestamp;
}
}
How do we guarantee that these Tweet objects are immutable – that, once a tweet is created,
its author, message, and date can never be changed?

The first threat to immutability comes from the fact that clients can — in fact must — directly
access its fields. So nothing’s stopping us from writing code like this:
Tweet t = new Tweet("justinbieber",
"Thanks to all those beliebers out there inspiring me everyday",
new Date());
t.author = "rbmllr";

This is a trivial example of representation exposure, meaning that code outside the class can
modify the representation directly. Rep exposure like this threatens not only invariants, but also
representation independence. We can’t change the implementation of Tweet without affecting
all the clients who are directly accessing those fields.

Fortunately, Java gives us language mechanisms to deal with this kind of rep exposure:
public class Tweet {

private final String author;


private final String text;
private final Date timestamp;

public Tweet(String author, String text, Date timestamp) {


this.author = author;
this.text = text;
this.timestamp = timestamp;
}

/** @return Twitter user who wrote the tweet */


public String getAuthor() {
return author;
}

/** @return text of the tweet */


public String getText() {
return text;
}

/** @return date/time when the tweet was sent */


public Date getTimestamp() {
return timestamp;
}

}
The private and public keywords indicate which fields and methods are accessible only
within the class and which can be accessed from outside the class. The final keyword also
helps by guaranteeing that the fields of this immutable type won’t be reassigned after the object
is constructed.

But that’s not the end of the story: the rep is still exposed! Consider this perfectly reasonable
client code that uses Tweet:
/** @return a tweet that retweets t, one hour later*/
public static Tweet retweetLater(Tweet t) {
Date d = t.getTimestamp();
d.setHours(d.getHours()+1);
return new Tweet("rbmllr", t.getText(), d);
}
retweetLater takes a tweet and should return another tweet with the
same message (called a
retweet) but sent an hour later. The retweetLater method might be part of a system that
automatically echoes funny things that Twitter celebrities say.

What’s the problem here? The getTimestamp call returns a reference to the same date object
referenced by tweet t. t.timestamp and d are aliases to the same mutable object. So when that
date object is mutated by d.setHours(), this affects the date in t as well, as shown in the
snapshot diagram.

Tweet’s immutability invariant has been broken. The problem is that Tweet leaked out a
reference to a mutable object that its immutability depended on. We exposed the rep, in such a
way that Tweet can no longer guarantee that its objects are immutable. Perfectly reasonable
client code created a subtle bug.

We can patch this kind of rep exposure by using defensive copying: making a copy of a mutable
object to avoid leaking out references to the rep. Here’s the code:

public Date getTimestamp() {


return new Date(Date.getTime());
}

Mutable types often have a copy constructor that allows you to make a new instance that
duplicates the value of an existing instance. In this case, Date’s copy constructor uses the
timestamp value, measured in seconds since January 1, 1970. As another example,
StringBuilder’s copy constructor takes a String. Another way to copy a mutable object is
clone(), which is supported by some types but not all. There are unfortunate problems with
the way clone() works in Java. For more, see Josh Bloch, Effective Java, item 10.

So we’ve done some defensive copying in the return value of getTimestamp. But we’re not
done yet! There’s still rep exposure. Consider this (again perfectly reasonable) client code:

/** @return a list of 24 inspiring tweets, one per hour today */


public static List<Tweet> tweetEveryHourToday () {
List<Tweet> list = new ArrayList<Tweet>();
Date date = new Date();
for (int i=0; i < 24; i++) {
date.setHours(i);
list.add(new Tweet("rbmllr", "keep it up! you can do it", date));
}
return list;
}
This code intends to advance a single Date object through the 24 hours of a day, creating a
tweet for every hour. But notice that the constructor of Tweet saves the reference that was
passed in, so all 24 Tweet objects end up with the same time, as shown in this snapshot diagram.

Again, the immutability of Tweet has been violated. We can fix this problem too by using
judicious defensive copying, this time in the constructor:
public Tweet(String author, String text, Date timestamp) {
this.author = author;
this.text = text;
this.timestamp = new Date(timestamp.getTime());
}
In general, you should carefully inspect the argument types and return types of all your ADT
operations. If any of the types are mutable, make sure your implementation doesn’t return direct
references to its representation. Doing that creates rep exposure.

You may object that this seems wasteful. Why make all these copies of dates? Why can’t we
just solve this problem by a carefully written specification, like this?
/**
* Make a Tweet.
* @param author Twitter user who wrote the tweet.
* @param text text of the tweet
* @param timestamp date/time when the tweet was sent. Caller must never
* mutate this Date object again!
*/
public Tweet(String author, String text, Date timestamp) {
This approach is sometimes taken when there isn’t any other reasonable alternative – for
example, when the mutable object is too large to copy efficiently. But the cost in your ability
to reason about the program, and your ability to avoid bugs, is enormous. In the absence of
compelling arguments to the contrary, it’s almost always worth it for an abstract data type to
guarantee its own invariants, and preventing rep exposure is essential to that.

1.3.2 Immutable Wrappers Around Mutable Data Types


The Java collections classes offer an interesting compromise: immutable wrappers.

Collections.unmodifiableList() takes a (mutable) List and wraps it with an object that


looks like a List, but whose mutators are disabled – set(), add(), remove() throw exceptions. So
you can construct a list using mutators, then seal it up in an unmodifiable wrapper (and throw
away your reference to the original mutable list), and get an immutable list.

The downside here is that you get immutability at runtime, but not at compile time. Java won’t
warn you at compile time if you try to sort() this unmodifiable list. You’ll just get an exception
at runtime. But that’s still better than nothing, so using unmodifiable lists, maps, and sets can
be a very good way to reduce the risk of bugs.

1.3.3 How to Establish Invariants


An invariant is a property that is true for the entire program – which in the case of an invaria nt
about an object, reduces to the entire lifetime of the object.

To make an invariant hold, we need to:

 make the invariant true in the initial state of the object; and
 ensure that all changes to the object keep the invariant true.

Translating this in terms of the types of ADT operations, this means:

 creators and producers must establish the invariant for new object instances; and
 mutators and observers must preserve the invariant.
The risk of rep exposure makes the situation more complicated. If the rep is exposed, then the
object might be changed anywhere in the program, not just in the ADT’s operations, and we
can’t guarantee that the invariant still holds after those arbitrary changes. So the full rule for
proving invariants is:

Structural induction. If an invariant of an abstract data type is

1. established by creators and producers;


2. preserved by mutators, and observers; and
3. no representation exposure occurs,

then the invariant is true of all instances of the abstract data type.

1.4 Abstract Data Types (ADTs)-Implementation


An abstract data type (ADT) is a mathematically specified entity that defines a set of its
instances.
An ADT specifies:
 What data is stored
 Interface - Operations on the data
o Manipulation
o access
 a set of axioms (preconditions and post conditions) that define what the operations do
(not how)
It serves as specifications of requirements for the building blocks of an algorithm.
It encapsulates the data structure and the algorithms that works on the data structure.
It separate issues of correctness and efficiency.

ADT Example - Order Book of a Stock Trading System - Dynamic Set

Axioms that define the methods


 isIn(New(), e) - false
 isIn(Add(B, e), e) - true
 isIn(Add(B, f), e) - isIn(B, e) if e≠f
 isIn(Remove(B, e), e) - false
 isIn(Remove(B, f), e) = isIn(B, e), if e≠f
1.4.1 The Stack ADT
Container of abstract objects that are inserted and removed according to the last-in-first-out
(LIFO) principle
Objects can be inserted at any time, but only the most-recently inserted object can be
removed
 Inserting an object - push
 removing an object - pop

The Stack ADT – Specification


 Data Object
 Operations
 new():ADT - creates a new stack
 push(S:ADT, o:Object):ADT – Inserts object o onto the top of stack S
 pop(S:ADT):ADT - removes the top object of stack S; if the stack is empty an
error occurs.
 Auxiliary operations
 top(S:ADT):Object - returns the top object of stack S without removing it; if the stack
is empty, an error occurs
 size(S:ADT):integer returns the number of objects in stack S
 isEmpty(S:ADT):boolean - indicates if stack S is empty
 Axioms
 pop(push(S, o)) = S
 top(push(S, o)) = o
Stack ADT specification in JAVA? Interfaces
 Interface - collection of method declarations with no data and no bodies
 write down the method names and the parameters (essentially the types)
 when a class implements an interface, it must implement all the methods declared in
the interface.
 Separating interface and implementation is a useful programming technique.
 java.util has a built- in stack data structure class - we will define our own interface and not
use the built-in class
public interface Stack<E> {
// accessor methods
public int size(); // returns the number of objects in the stack
public boolean isEmpty(); // returns true if the stack is empty, false otherwise
public E top(); thr// returns the top object of the stack
// manipulation/update methods
public void push(E o); // inserts the object at the top of the stack
public E pop(); throw // removes and returns the object at the top of the stack
}
Stack ADT specification in JAVA? Exceptions
 unexpected events that occur during the execution of a program
 useful for handling errors
 when an error (or an exceptional event is encountered, the Java code throws an
exception
 Upon throwing an exception, the control flow exits from the current method and goes to
the parent calling method
 responsibility of handling the error can be delegated to the parent method
StudentNotFoundException {
...
if (s is not part of CSL201)
throw new StudentNotFoundException(s+"is not a student in CSL201");
...
}

public void UpdateClassScore() {


...
try {
CSL201.UpdateStudentScore(s);
}
catch (StudentNotFoundException e) {
system.out.println(e);
}
...
}

 try - guarded fragment of code that might thrown an exception when executed.
 catch - block of code to which control jumps on catching an exception. The block contains
code to analyze the exception and apply an appropriate solution.
 If an exception is never caught in a method, it will propagate upwards along the sequence
of method calls until the user sees it.
 java.util has a built- in stack data structure class - we will define our own interface and
not use the built-in class

public interface Stack<E> {


// accessor methods
public int size(); // returns the number of objects in the stack
public boolean isEmpty(); // returns true if the stack is empty, false otherwise
public E top() throws StackEmptyException; // returns the top object of the stack
// manipulation/update methods
public void push(E o); // inserts the object at the top of the stack
public E pop() throws StackEmptyException; // removes and returns the object at the top of
the stack
}

Array-Based Stack Implementation in Java


 Simple way to implement a stack
specify the maximum size N of our stack
 Add elements left to right
 variable t keeps track of the top element of the array stack S

 array indices start at 0, so we initialize t to -1



public class ArrayStack<E> implements Stack<E> {
/** Default array capacity. */
public static final int CAPACITY=1000; // default array capacity

/** Generic array used for storage of stack elements. */


private E[] data; // generic array used for storage

/** Index of the top element of the stack in the array. */


private int t = -1; // index of the top element in stack

/** Constructs an empty stack using the default array capacity. */


public ArrayStack() { this(CAPACITY); } // constructs stack with default capacity
/**
* Constructs and empty stack with the given array capacity.
* @param capacity length of the underlying array
*/
public ArrayStack(int capacity) { // constructs stack with given capacity
data = (E[]) new Object[capacity];
}
/**
* Returns the number of elements in the stack.
* @return number of elements in the stack
*/
public int size() { return (t + 1); }
/**
* Tests whether the stack is empty.
* @return true if the stack is empty, false otherwise
*/
public boolean isEmpty() { return (t == -1); }
/**
* Inserts an element at the top of the stack.
* @param e the element to be inserted
* @throws StackFullException if the array storing the elements is full
*/
public void push(E e) throws StackFullException {
if (size() == data.length) throw new StackFullException("Stack is full");
data[++t] = e; // increment t before storing new item
}
/**
* Returns, but does not remove, the element at the top of the stack.
* @return top element in the stack (or null if empty)
* @throws StackEmptyException if the array storing the stack is empty
*/
public E top() {
if (isEmpty()) throw new StackEmptyException ("Stack is empty”);
return data[t];
}

/**
* Removes and returns the top element from the stack.
* @return element removed
* @throws StackEmptyException if the array storing the stack is empty
*/
public E pop() {
if (isEmpty()) throw new StackEmptyException ("Stack is empty”);
E answer = data[t];
data[t] = null; // dereference to help garbage collection
t--;
return answer;
}

Array-Based Stack Performance and Limitations


 Performance
 implementation is simple and efficient
methods performed in O(1)
 Space complexity - O(N)
 Limitations
 maximum capacity of the array has to be specified a priori
too large - wastage of space
too small - insufficient for a given application
 StackFullException is specific to this implementation
 StackEmptyException is required by the interface

Linked List-Based Stack Implementation in Java


public class LinkedStack<E> implements Stack<E> {

/** The primary storage for elements of the stack */


private SinglyLinkedList<E> list = new SinglyLinkedList<>(); // an empty list

/** Constructs an initially empty stack. */


public LinkedStack() { } // new stack relies on the initially empty list

/**
* Returns the number of elements in the stack.
* @return number of elements in the stack
*/
public int size() { return list.size(); }
/**
* Tests whether the stack is empty.
* @return true if the stack is empty, false otherwise
*/
public boolean isEmpty() { return list.isEmpty(); }

/**
* Inserts an element at the top of the stack.
* @param element the element to be inserted
*/
public void push(E element) { list.addFirst(element); }
/**
* Returns, but does not remove, the element at the top of the stack.
* @return top element in the stack
* @throws StackEmptyException if the list storing the stack is empty
*/
public E top() {
if (isEmpty()) throw new StackEmptyException ("Stack is empty”);
return list.first();
}

/**
* Removes and returns the top element from the stack.
* @return element removed
* @throws StackEmptyException if the list storing the stack is empty
*/
public E pop() {
if (isEmpty()) throw new StackEmptyException ("Stack is empty”);
return list.removeFirst();
}

Applications of Stacks
 Direct applications
 Page-visited history in a Web browser
 Undo sequence in a text editor
 Chain of method calls in the Java Virtual Machine
 Indirect applications
 Auxiliary data structure for algorithms
 Component of other data structures

1.4.2 The Queue ADT


 The insertion and removal routines of the Queue ADT follow the first-in- first-out (FIFO)
principle.
 Elements may be inserted at any time, but only the element which has been in the queue
the longest may be removed.
 Inserting an object (at the rear) – enqueue
 Removing an object ( from the front) – dequeuer
The Queue ADT - Specification
 Data Object
 The queue supports the following fundamental methods:
 New():ADT – creates an empty queue
 Enqueue(Q:ADT, o:element):ADT – inserts the object o at the rear of the queue
 Dequeue(Q:ADT):element – removes the object from the front of the queue; an error
occurs if the queue is empty
 First(Q:ADT):element – returns, but does not remove the object at the front of the
queue; an error occurs if the queue is empty.
 Auxiliary methods
 Size(Q:QDT):integer – returns the number of elements in the queue
 isEmpty(Q:ADT):boolean – indicates whether the queue is empty
 Axioms
 Front(Enqueue(New(), v)) = v
 Dequeue(Enqueue(New(), v)) = New()
 Front(Enqueue(Enqueue(Q,w),v)) = Front(Enqueue(Q,w))
 Dequeue(Enqueue(Enqueue(Q,w), v)) = Enqueue(Dequeue(Enqueue(Q,w),v)

Queue ADT specification in JAVA


public interface Queue<E> {
// accessor methods
int size(); // returns the number of elements in the queue
boolean isEmpty(); // returns true if the queue is empty, false otherwise
//manipulation methods
void enqueue(E e); // adds the element to the rear of the queue
E first() throws QueueEmptyException; // returns the first element of the queue
E dequeue() throws QueueEmptyException; // removes and returns the first element of the
queue
}

Array-based Queue
 Use an array of size N in a circular fashion
 Two variables keep track of the front and size
f index of the front element
sz number of stored elements
 When the queue has fewer than N elements, array location r = (f + sz) mod N is the first
empty slot past the rear of the queue

Queue Operations
 We use the modulo operator (remainder of division)

 Operation enqueue throws an exception if the array is full – Implementation specific


exception

 Note that operation dequeue throws QueueEmptyException if the queue is empty


Array-based Implementation

public class ArrayQueue<E> implements Queue<E> {


// instance variables
public static final int CAPACITY = 1000; // default array capacity
private E[] data; // generic array used for storage
private int f = 0; // index of the front element
private int sz = 0; // current number of elements

// constructors
public ArrayQueue() {this(CAPACITY);} // constructs queue with default capacity

public ArrayQueue(int capacity) { // constructs queue with given capacity


data = (E[]) new Object[capacity]; // safe cast; compiler may give warning
}

// methods
/**
* Returns the number of elements in the queue.
* @return number of elements in the queue
*/
public int size() { return sz; }

public boolean isEmpty() { return (sz == 0); } // Tests whether the queue is empty.

/**
* Inserts an element at the rear of the queue.!
* This method runs in O(1) time.!
* @param e new element to be inserted!
* @throws QueueFullException if the array storing the elements is full!
*/
public void enqueue(E e) throws QueueFullException {
if (sz == data.length) throw new QueueFullException ("Queue is full");
int avail = (f + sz) % data.length; // use modular arithmetic
data[avail] = e;
sz++;
}

/**
* Returns, but does not remove, the first element of the queue.!
* @return the first element of the queue!
* @throws QueueEmptyException if the array storing the elements is empty!
*/
public E first() throws QueueEmptyException {
if (isEmpty()) throws new QueueEmptyException() ;
return data[f];
}

/**
* Removes and returns the first element of the queue.
* @return element removed
* @throws QueueEmptyException if the queue is empty)
*/
public E dequeue() throws QueueEmptyException {
if (isEmpty()) throw new QueueEmptyException ();
E answer = data[f];
data[f] = null; // dereference to help garbage collection
f = (f + 1) % data.length;
sz--;
return answer;
}

Applications of Queues
 Direct applications
 Waiting lists, bureaucracy
 Access to shared resources (e.g., printer)
 Multiprogramming
 Indirect applications
 Auxiliary data structure for algorithms
 Component of other data structures

Application: Round Robin Schedulers


We can implement a round robin scheduler using a queue Q by repeatedly performing the
following steps:
1. e = Q.dequeue()
2. Service element e
3. Q.enqueue(e)
Double-Ended Queue – Deque

 Deque supports insertion and deletion from the front and back of the queue
 The deque ADT supports the following fundamental methods
 InsertFirst(Q:ADT, e:element):ADT – inserts e at the beginning of the deque
 InsertLast(Q:ADT, e:element):ADT – inserts e at the end of the deque
 RemoveFirst(Q:ADT):ADT – removes the first element
 RemoveLast(Q:ADT):ADT – removes the last element
 First(Q:ADT): element – returns the first element
 Last(Q:ADT):element – returns the last element

Deque implementations
Module-2

2.1 INTRODUCTION
Java is a class-based object-oriented programming (OOP) language that is built around the
concept of objects. OOP concepts (OOP) intend to improve code readability and reusability by
defining how to structure a Java program efficiently. The main principles of object-oriented
programming are:

1. Abstraction
2. Encapsulation
3. Inheritance
4. Polymorphism
5. Association
6. Aggregation
7. Composition

Java comes with specific code structures for each OOP principle. For example, the extends
keyword for inheritance or getter and setter methods for encapsulation.

OOP concepts allow us to create specific interactions between Java objects. They make it
possible to reuse code without creating security risks or making a Java program less readable.

Here are the four main principles in more detail.

2.1.1 Abstraction
Abstraction aims to hide complexity from the users and show them only the relevant
information. For example, if you want to drive a car, you don’t need to know about its interna l
workings. The same is true of Java classes. You can hide internal implementation details by
using abstract classes or interfaces. On the abstract level, you only need to define the method
signatures (name and parameter list) and let each class implement them in their own way.

Abstraction in Java:

 Hides the underlying complexity of data


 Helps avoid repetitive code
 Presents only the signature of internal functionality
 Gives flexibility to programmers to change the implementation of the abstract
behaviour
 Partial abstraction (0-100%) can be achieved with abstract classes
 Total abstraction (100%) can be achieved with interfaces
2.1.2 Encapsulation
Encapsulation allows us to protect the data stored in a class from system-wide access. As its
name suggests, it safeguards the internal contents of a class like a real-life capsule. You can
implement encapsulation in Java by keeping the fields (class variables) private and providing
public getter and setter methods to each of them. Java Beans are examples of fully encapsulated
classes.

Encapsulation in Java:

 Restricts direct access to data members (fields) of a class.


 Fields are set to private
 Each field has a getter and setter method
 Getter methods return the field
 Setter methods let us change the value of the field

2.1.3 Polymorphism
Polymorphism refers to the ability to perform a certain action in different ways. In Java,
polymorphism can take two forms: method overloading and method overriding. Method
overloading happens when various methods with the same name are present in a class. When
they are called they are differentiated by the number, order, and types of their parameters.
Method overriding occurs when the child class overrides a method of its parent.

Polymorphism in Java:

 The same method name is used several times.


 Different methods of the same name can be called from the object.
 All Java objects can be considered polymorphic (at the minimum, they are of their own
type and instances of the Object class).
 Example of static polymorphism in Java is method overloading.
 Example of dynamic polymorphism in Java is method overriding.

2.1.4 Inheritance
Inheritance makes it possible to create a child class that inherits the fields and methods of the
parent class. The child class can override the values and methods of the parent class, however
it’s not necessary. It can also add new data and functionality to its parent. Parent classes are
also called super classes or base classes, while child classes are known as subclasses or derived
classes as well. Java uses the extends keyword to implement the principle of inheritance in
code.

Inheritance in Java:

 A class (child class) can extend another class (parent class) by inheriting its features.
 Implements the DRY (Don’t Repeat Yourself) programming principle.
 Improves code reusability.
 Multilevel inheritance is allowed in Java (a child class can have its own child class as
well).
 Multiple inheritances are not allowed in Java (a class can’t extend more than one
class).

2.1.5 Association
Besides the four main principles of OOP, Java also works with three further concepts
(association, aggregation, composition) you can make use of when designing your programs.
Aggregation is a special form of association, while composition is a special form of
aggregation.

Association simply means the act of establishing a relationship between two unrelated classes.
For example, when you declare two fields of different types (e.g. Car and Bicycle) within the
same class and make them interact with each other, you have performed association.

Association in Java:

 Two separate classes are associated through their objects.


 The two classes are unrelated, each can exist without the other one.
 Can be a one-to-one, one-to-many, many-to-one, or many-to-many relationship.

2.1.6 Aggregation
Aggregation is a narrower kind of association. It occurs when there’s a one-way (HAS-A)
relationship between the two classes you associate through their objects. For example, every
Passenger has a Car but a Car doesn’t necessarily have a Passenger. When you declare the
Passenger class, you can create a field of the Car type that shows which car the passenger
belongs to. Then, when you instantiate a new Passenger object, you can access the data stored
in the related Car as well.

Aggregation in Java:

 One-directional association.
 Represents a HAS-A relationship between two classes.
 Only one class is dependent on the other.

2.1.7 Composition
Compositionis a stricter form of aggregation. It occurs when the two classes you associate are
mutually dependent on each other and can’t exist without each other. For example, take a Car
and an Engine class. A Car cannot run without an Engine, while an Engine also can’t functio n
without being built into a Car. This kind of relationship between objects is also called a PART-
OF relationship.

Composition in Java:

 A restricted form of aggregation


 Represents a PART-OF relationship between two classes
 Both classes are dependent on each other
 If one class ceases to exist, the other can’t survive alone
2.2 Characteristics of OOP
2.2.1 Abstraction
With abstraction, you can hide the internal workings of an object and only show the features
the user needs to know about. Java provides two ways to implement abstraction: abstract
classes and interfaces. With abstract classes, you can achieve partial abstraction, while
interfaces make total (100%) abstraction possible.

Abstract classes
An abstract class is a superclass (parent class) that cannot be instantiated. You need to
instantiate one of its child classes if you want to create a new object. Abstract classes can have
both abstract and concrete methods. Abstract methods contain only the method signature, while
concrete methods declare a method body as well. Abstract classes are defined with the
abstract keyword.

In the example below, you can see an abstract class called Animal with two abstract and one
concrete method.

abstract class Animal {


// abstract methods
abstract void move();
abstract void eat();

// concrete method
void label() {
System.out.println("Animal's data:");
}
}
Extend the Animal abstract class with two child classes: Bird and Fish. Both of them set up their
own functionality for the move() and eat() abstract methods.
class Bird extends Animal {

void move() {
System.out.println("Moves by flying.");
}
void eat() {
System.out.println("Eats birdfood.");
}
}

class Fish extends Animal {


void move() {
System.out.println("Moves by swimming.");
}
void eat() {
System.out.println("Eats seafood.");
}
}

Now, test it with the TestBird and TestFish classes. Both call the one concrete ( label()) and the
two abstract ( move() and eat()) methods.
class TestBird {
public static void main(String[] args) {
Animal myBird = new Bird();

myBird.label();
myBird.move();
myBird.eat();
}
}

class TestFish {
public static void main(String[] args) {
Animal myFish = new Fish();

myFish.label();
myFish.move();
myFish.eat();
}
}

In the console, the concrete method has been called from the Animal abstract class, while the two
abstract methods have been called from Bird() and Fish(), respectively.
[Console output of TestBird]
Animal's data:
Moves by flying.
Eats birdfood.

[Console output of TestFish]


Animal's data:
Moves by swimming.
Eats seafood.

2.2.2 Interfaces
An interface is a 100% abstract class. It can have only static, final, and public fields and
abstract methods. It’s frequently referred to as a blueprint of a class as well. Java interfaces
allow us to implement multiple inheritance in our code, as a class can implement any number
of interfaces. Classes can access an interface using the implements keyword.

In the example, define two interfaces, Animal and Bird. Animal has two abstract methods,
while Bird has two static fields and an abstract method.
interface Animal {
public void eat();
public void sound();
}

interface Bird {
int numberOfLegs = 2;
String outerCovering = "feather";

public void fly();


}
The class Eagle implements both interfaces. It defines its own functionality for the three abstract
methods. The eat() and sound() methods come from the Animal class, while fly() comes from
Bird.

class Eagle implements Animal, Bird {


public void eat() {
System.out.println("Eats reptiles and amphibians.");
}
public void sound() {
System.out.println("Has a high-pitched whistling sound.");
}
public void fly() {
System.out.println("Flies up to 10,000 feet.");
}
}

In the TestEagle test class, instantiate a new Eagle object (called myEagle) and print out all
the fields and methods to the console.

As static fields don’t belong to a specific object but to a whole class, you need to access them
from the Bird interface instead of the myEagle object.

class TestEagle {
public static void main(String[] args) {
Eagle myEagle = new Eagle();

myEagle.eat();
myEagle.sound();
myEagle.fly();

System.out.println("Number of legs: " + Bird.numberOfLegs);


System.out.println("Outer covering: " +
Bird.outerCovering);
}
}

The Java console returns all the information you wanted to access:
[Console output of TestEagle]
Eats reptiles and amphibians.
Has a high-pitched whistling sound.
Flies up to 10,000 feet.
Number of legs: 2
Outer covering: feather

2.2.3 Encapsulation
With encapsulation, you can protect the fields of a class. To do so, declare the fields as private
and providing access to them with getter and setter methods.

The Animal class below is fully encapsulated. It has three private fields and each of them has
its own set of getter and setter methods.
class Animal {
private String name;
private double averageWeight;
private int numberOfLegs;

// Getter methods
public String getName() {
return name;
}
public double getAverageWeight() {
return averageWeight;
}
public int getNumberOfLegs() {
return numberOfLegs;
}

// Setter methods
public void setName(String name) {
this.name = name;
}
public void setAverageWeight(double averageWeight) {
this.averageWeight = averageWeight;
}
public void setNumberOfLegs(int numberOfLegs) {
this.numberOfLegs = numberOfLegs;
}
}

The TestAnimal class first sets a value for each field with the setter methods, then prints out the
values using the getter methods.
public class TestAnimal {
public static void main(String[] args) {
Animal myAnimal = new Animal();

myAnimal.setName("Eagle");
myAnimal.setAverageWeight(1.5);
myAnimal.setNumberOfLegs(2);

System.out.println("Name: " + myAnimal.getName());


System.out.println("Average weight: " +
myAnimal.getAverageWeight() + "kg");
System.out.println("Number of legs: " +
myAnimal.getNumberOfLegs());
}
}

As you can see below, the Java console returns properly all the values you set with the setter methods:
[Console output of TestAnimal]
Name: Eagle
Average weight: 1.5kg
Number of legs: 2

2.2.4 Inheritance
Inheritance allows us to extend a class with child classes that inherit the fields and methods of
the parent class. It’s an excellent way to achieve code reusability. In Java, we need to use the
extends keyword to create a child class.
In the example, the Eagle class extends the Bird parent class. It inherits all of its fields and
methods, plus defines two extra fields that belong only to Eagle.
class Bird {
public String reproduction = "egg";
public String outerCovering = "feather";

public void flyUp() {


System.out.println("Flying up...");
}
public void flyDown() {
System.out.println("Flying down...");
}
}

class Eagle extends Bird {


public String name = "eagle";
public int lifespan = 15;
}

The TestEagle class instantiates a new Eagle object and prints out all the information (both the
inherited fields and methods and the two extra fields defined in the Eagle class).
class TestEagle {
public static void main(String[] args) {
Eagle myEagle = new Eagle();

System.out.println("Name: " + myEagle.name);


System.out.println("Reproduction: " +
myEagle.reproduction);
System.out.println("Outer covering: " +
myEagle.outerCovering);
System.out.println("Lifespan: " + myEagle.lifespan);

myEagle.flyUp();
myEagle.flyDown();
}
}

You can see the console output below:


[Console output of TestEagle]
Reproduction: another egg
Outer covering: feather
Lifespan: 15
Flying up...
Flying down...

2.2.5 Polymorphism
Polymorphism makes it possible to use the same entity in different forms. In Java, this means
that you can declare several methods with the same name until they are different in certain
characteristics. Java provides us with two ways to implement polymorphism: method
overloading and method overriding.
Static polymorphism
Method overloading means that you can have several methods with the same name within a
class. However, the number, names, or types of their parameters need to be different.

For example, the Bird() class below has three fly() methods. The first one doesn’t have
any parameters, the second one has one parameter (height), and the third one has two
parameters (name and height).
class Bird {
public void fly() {
System.out.println("The bird is flying.");
}
public void fly(int height) {
System.out.println("The bird is flying " + height + " feet
high.");
}
public void fly(String name, int height) {
System.out.println("The " + name + " is flying " + height +
" feet high.");
}
}

The test class instantiates a new Bird object and calls the fly() method three times. Firstly, without
parameters, secondly, with one integer parameter for height, and thirdly, with two parameters for
name and height.

class TestBird {
public static void main(String[] args) {
Bird myBird = new Bird();

myBird.fly();
myBird.fly(10000);
myBird.fly("eagle", 10000);
}
}

In the console, we can see that Java could have differentiated the three polymorphic fly() methods:
[Console output of TestBird]
The bird is flying.
The bird is flying 10000 feet high.
The eagle is flying 10000 feet high.

Dynamic polymorphism
By using the method overriding feature of Java, you can override the methods of a parent
class from its child class.

The Bird class extends the Animal class in the example below. Both have an eat() method.
By default, Bird inherits its parent’s eat() method. However, as it also defines its own
eat() method, Java will override the original method and call eat() from the child class.

class Animal {
public void eat() {
System.out.println("This animal eats insects.");
}
}

class Bird extends Animal {

public void eat() {


System.out.println("This bird eats seeds.");
}

The TestBird class first instantiates a new Animal object and calls its eat() method. Then,
it also creates a Bird object and calls the polymorphic eat() method again.

class TestBird {
public static void main(String[] args) {
Animal myAnimal = new Animal();
myAnimal.eat();

Bird myBird = new Bird();


myBird.eat();
}
}

The console returns the values of the relevant methods properly. Therefore Java could have
differentiated the two eat() methods indeed.

[Console output of TestBird]


This animal eats insects.
This bird eats seeds.

2.3 Object-Oriented Programming Basics With Java


In his keynote address to the 11th World Computer Congress in 1989, renowned computer
scientist Donald Knuth said that one of the most important lessons he had learned from his
years of experience is that software is hard to write!

Computer scientists have struggled for decades to design new languages and techniques for
writing software. Unfortunately, experience has shown that writing large systems is virtua lly
impossible. Small programs seem to be no problem, but scaling to large systems with large
programming teams can result in $100M projects that never work and are thrown out. The only
solution seems to lie in writing small software units that communicate via well-defined
interfaces and protocols like computer chips. The units must be small enough that one
developer can understand them entirely and, perhaps most importantly, the units must be
protected from interference by other units so that programmers can code the units in isolation.

The object-oriented paradigm fits these guidelines as designers represent complete concepts
or real world entities as objects with approved interfaces for use by other objects. Like the
outer membrane of a biological cell, the interface hides the internal implementation of the
object, thus, isolating the code from interference by other objects. For many tasks, object
oriented programming has proven to be a very successful paradigm. Interestingly, the first
object-oriented language (called Simula, which had even more features than C++) was
designed in the 1960's, but object-oriented programming has only come into fashion in the
1990's.

Points to Consider:
 Object-oriented programming takes advantage of our perception of world.
 An object is an encapsulated completely-specified data aggregate containing attributes
and behaviour.
 Data hiding protects the implementation from interference by other objects and defines
approved interface.
 An object-oriented program is a growing and shrinking collection of objects that
interact via messages.
 You can send the same message to similar objects--the target decides how to impleme nt
or respond to a message at run-time.
 Objects with same characteristics are called instances of a class.
 Classes are organized into a tree or hierarchy.
 Two objects are similar if they have the same ancestor somewhere in the class
hierarchy.
 You can define new objects as they differ from existing objects
 Benefits of object-oriented programming include:
 reduced cognitive load (have less to think about and more natural paradigm)
 isolation of programmers (better team programming)
 less propagation of errors
 more adaptable/flexible programs
 faster development due to reuse of code
You are used to observing the world around you through the eyes of a huntergatherer: mainly
animals acting upon other animals and objects. There must have been tremendous selection
pressure for brains that were adept at reasoning about entities, their attributes, their behavior,
and the relationships among them. Is that object edible, ready to eat me, or going to mate with
me? When writing software, one can easily argue that you are at your best when designing and
implementing software in a manner that parallels the way your brain perceives the real world.

2.3.1 Encapsulation and data hiding


The first and most important design principle we can derive from our perception of the real
world is called encapsulation. When you look at an animal, you consider it to be a complete
entity--all of its behavior and attributes arise strictly from that animal. It is an independent,
completely-specified, and self-sufficient actor in the world. You do not have to look behind a
big rock looking for another bit of functionality or another creature that is remotely controlling
the animal.

Closely associated with encapsulation is the idea of data hiding. Most animals have hidden
attributes or functionality that are inaccessible or are only indirectly accessible by other
animals. The inner construction and mechanism of the human body is not usually available to
you when conversing with other humans. You can only interact with human beings through the
approved voice-recognition interface. Bookkeeping routines such as those controlled by the
autonomic nervous system like breathing may not be invoked by other humans. Without
bypassing the approved interface, you cannot directly measure attributes such as internal body
temperature and so on.
One can conclude that we perceive objects in the world as encapsulated (selfcontained) entities
with approved interfaces that hide some implementation behavior and attributes. From a design
perspective, this is great because it limits what you have to think about at once and makes it
much easier for multiple programmers to collaborate on a program. You can think about and
design each object independently as well as force other programmers to interact with your
objects only in a prescribed manner; that is, using only the approved interface. You do not have
to worry about other programmers playing around with the inner workings of your object and
at the same time you can isolate other programmers from your internal changes.

Encapsulation and data hiding are allowed to varying degrees in non-object oriented langua ges
and programmers have used these principles for decades. For example, in C, you can group
related variables and functions in a single file, making some invisible to functions in other files
by labeling them as static. Conversely, object-oriented languages support these design
principles. In Java, for example, you will use an actual language construct called a class
definition to group variables and functions. You can use access modifiers like private and
public to indicate which class members are visible to functions in other objects.

2.3.2 The interaction of objects using polymorphism


Encapsulation and data hiding are used to define objects and their interfaces, but what about
the mechanism by which objects interact? In the real world, animals are self-contained and,
therefore, do not physically share brains. Animals must communicate by sending signals.
Animals send signals, depending on the species, by generating sound waves such as a voice,
images such as a smile, and chemicals such as pheromones. There is no way for an animal to
communicate with another by directly manipulating the internal organs or brain of another
because those components are hidden within the other animal. Consequently, our brain is
hardwired to communicate by sending signals.

If we view the world as a collection of objects that send and receive messages, what
programming principle can we derive? At first you may suspect that a signal or message is just
a function call. Rather than manipulate the internals of an object, you might call a function that
corresponded to the signal you wanted to send.

Unfortunately, function calls are poor analogs to real world messaging for two main reasons.
First, function calls do things backwards. You pass objects to functions whereas you send
messages to an object. You have to pass objects to functions for them to operate on because
they are not associated with a particular object. Second, function calls are unique in that the
function's name uniquely identifies what code to run whereas messages are more generic. The
receiver of a message determines how to implement it. For example, you can tell a man and a
woman both to shave and yet they respond to the exact same message by doing radically
different things.

The truly powerful idea behind message sending lies in its flexibility--you do not even need to
know what sex the human is to tell them to shave. All you need to know is that the receiver of
the message is human. The notion that similar, but different, objects can respond to the same
message is technically called polymorphism (literally "multiple- forms"). Polymorphism is
often called latebinding because the receiver object binds the message to an appropriate
implementation function (method in Java terminology) at run-time when the message is sent
rather than at compile-time as functions are.
Polymorphism's flexibility is derived from not having to change the code that sends a message
when you define new objects. Imagine a manager that signals employees when to go home at
night. A so-called micro-manager must know all sorts of details about each employee (such as
where they live) to get them home at night. A manager that delegates responsibility well will
simply tell each employee to go home and let them take care of the details. Surely, adding a
new kind of employee such as a "summer intern" should not force changes in the manager
code. Alas, a micro-manager would in fact have to change to handle the details of the new
employee type, which is exactly what happens in the functio n-centric, non-object-oriented
programming model.

Without polymorphism, encapsulation's value is severely diminished because you cannot


effectively delegate, that is, you cannot leave all the details within a self-contained object. You
would need to know details of an object to interact with it rather than just the approved
communication interface.

The relationship between objects and inheritance


Humans rely on their ability to detect similarities between objects to survive new situations. If
an animal has many of the characteristics of a snake, it is best to leave it alone for fear of a
venomous bite. In fact, some animals take advantage of similarity detectors in other anima ls
by mimicking more dangerous creatures; some kingsnakes have colored bands like the deadly
coral snake, although in a different order. Similarly, we learn most easily when shown new
topics in terms of how they relate or differ from our current knowledge base. Our affinity for
detecting and using similarity supports two important ideas in the object-oriented design
model: polymorphism requires a definition of similarity to be meaningful and we can design
new objects as they differ from existing objects.

Behavior versus identity


Sending the same message to two different objects only makes sense when the objects are
similar by some measure. For example, it makes sense to tell a bird and an airplane to fly
because they share behavior. On the other hand, telling a dog to sit makes sense, but telling a
cat to sit makes no sense; well, it is a waste of time anyway. Telling a human to shave makes
sense, but telling an airplane to shave does not. One way to define similarity is to simply say
that the objects implement or respond to the same message or set of messages; that is to say,
they share at least a partial interface (common subset of public methods). This is the approach
of early object-oriented languages such as SmallTalk.

Another similarity measure corresponds to the relationship between the objects themselves
rather than just their interfaces. If two objects are the same kind of object then it makes sense
that they also share a partial interface. For example, males and females are kinds of humans
and, hence, share a common interface (things that all humans can do like shave, sleep, sit and
so on). Java uses object relationships to support polymorphism.

The inheritance relationship


What metaphor does Java use to specify object relationships? Java uses inheritance. That is, if
man and woman objects are kinds of humans then they are said to inherit from human and
therefore share the human interface. Consequently, we are able to treat them generically as
humans and do not have to worry about their actual type. If we had a list of humans, we could
walk down the list telling everyone to shave without concern for the objects' concrete type
(either man or woman). Late-binding ensures that the appropriate method is invoked in
response to the shave message depending on the concrete type at runtime.
All objects have exactly one parent, inheriting all of the data and method members from the
parent. The inheritance relationships among objects thus form a tree, with a single predefined
root called Object representing the generic object.

Defining by difference
The second important feature of an object-oriented design model inspired by similar ity
detection is "defining by difference." If I want to define a new object in my system, an efficie nt
way to do so is to describe how it differs from an existing object. Since man and woman objects
are very similar, the human object presumably contains most of the behavior. The man object,
for example, only has to describe what distinguishes a man from the abstract notion of a human,
such as buying lots of electronic toys.

Background
Over the past 40 years, four main computation paradigms have developed:
• Imperative (procedural); C, Pascal, Fortran
• Functional; Lisp, ML
• Logic (declarative); Prolog
• Object-oriented; Simula67, SmallTalk, CLOS, Objective-C, Eiffel, C++, Java
There are also application-specific languages like SQL, SETL (set language), AWK/PERL,
XML (structured data description), ANTLR (grammars), PostScript, and so on. The idea is
that you should use the "right tool for the right job."

Object-oriented programming is not a "silver bullet" that should be used in every situation, but
many modern programming problems are readily mapped to object oriented languages.

2.4 Object-Oriented Programming By Example


This section is a starting point for learning object-oriented programming. It parallels the
process you would follow to become a car designer: first you learn to drive a car, then you look
under the hood, next you learn to modify and repair engines, finally you design new cars.
Specifically, in this section, you will:
1. learn how to use a trivial object
2. study the object definition
3. learn to modify an object definition
4. learn how to define new objects

"Learning to drive"

We all know how to add two numbers, for example "3+4=7". In a programming language such
as C, you might have the values in variables like this:
int a, b, c;
a=3;
b=4;
c=a+b; // c is 7
Now imagine that you plan on doing some graphics work and your basic unit will be a two-
dimensional point not a single number. Languages do not have built in definitions of point, so
you have to represent points as objects (similar to structs in C and records in Pascal). The plus
operator "+" also is not defined for point so you must use a method call to add points together.
In the following code fragment, type Point holds x and y coordinates and knows how to add
another point to itself yielding another Point. Just like in C or other procedural languages, you
have to define the type of your variables. Here, the variables are objects of type Point. To create
a new point, use the new operator and specify the coordinates in parentheses like a functio n
call.

Point a, b, c;
a = new Point(2,4);
b = new Point(3,5);
To add two points together, use the plus() method:

c = a.plus(b); // c is 5,9
In object-oriented terminology, you would interpret this literally as, "send the message plus to
point a with an argument of b." In other words, tell a to add b to itself, yielding a new point.
The field access operator, "dot", is used for methods just like you use it in C or Pascal to access
struct or record fields.

A comment about object-oriented method call syntax


The object-oriented syntax
target.method(arguments);
may look a bit odd (backwards) since you are used to calling functions not invoking methods
on objects; functions are called with both operands as arguments:
c = plus(a,b);
On the contrary, object-oriented syntax is closer to mathematics and English syntax where the
"operator" appears in the middle of the statement. For example, you say "Robot, move left 3
units":
robot.moveLeft(3); /* Java style */

not "Move left Robot, 3":

moveLeft(robot,3); /* C style */

The "dot" field access operator is consistent with field access. For example, in most procedural
languages like C, you say p.x to get field x from struct or record p. In object-oriented langua ges,
you group methods as well as data into objects--the field access operator is consistently used
for both. The Point type is called a class because it represents a template for a class of objects.
In the example above, a, b, and c are all point objects and, hence, they are instances of class
Point. You can think of a class as blueprints for a house whereas instances are actual houses
made from those blueprints. Instances exist only at run-time and an object-oriented program is
just a bunch of object instances sending messages to each other. Also, when you hear someone
talking about the methods of an object, they are, strictly speaking, referring to the methods
defined in the object's class definition.

"Looking under the hood"

Imagine that you wanted to duplicate the simple point addition example above, but in a
procedural programming language. You almost certainly would make a data aggregate such as
the following C struct:
struct Point {
int x, y;
};
Then, to define two points, you would do:
struct Point a = {2,4};
struct Point b = {3,5};
struct Point c;
Adding two points together would mean that you need an add_points function that returned the
point sum like this:

c = plus_points(a,b);

A Comment on C syntax
Variable definitions in C are like Java and of the form:
type name;
or
type name = init-value;
For example,
int x = 0;
defines an integer called x and initializes it to 0.
C data aggregates are called structs and have the form:
struct Name {
data-field(s);
};
Defining a variable of struct type allocates memory space to hold something that big;
specifically, sizeof(Name), in C terminology. You can initialize these struct variables using a
curly-brace-enclosed list of values. For example,
struct Point a = {1,2};
makes memory space to hold a point of size sizeof(Point) (normally two 32- bit words) and
initializes its x and y values to 1 and 2, respectively.

To access the fields within a struct, use the "dot" operator. For example, if you printed out the
value of a.x after you defined variable a as above, you would see the value 1.

Functions in C are defined as follows:


return-type name(arguments)
{
statement(s);
}
For example, here is a function that adds two integers and returns the result:
int add_ints(int i, int j)
{
return i+j;
}
Functions with no return type (that is, procedures) use type void:
void foo() {...}
You could define plus_points as follows.
struct Point plus_points(struct Point a, struct Point b)
{
struct Point result = {a.x+b.x, a.y+b.y};
return result;
}
At this point, you have the data defined for a point and a function to operate on it. How does
someone reading your code know that function plus_points is used to manipulate points? That
person would have to look at the name, arguments, and statements to decide the functio n's
relevance to the Point struct. Invert the question. Where is all the code that manipulates a point?
The bad news, of course, is that you have to search the entire program!

Object-oriented programming offers a better solution. If humans naturally see the elements in
the real world as self-contained objects, why not program the same way? If you move the
functions that manipulate a data aggregate physically into the definition of that aggregate, you
have encapsulated all functionality and state into single program entity called a class. Anybody
that wants to examine everything to do with a point, for example, just has to look in a single
place. Here is a starting version of class Point in Java:
class Point {
int x,y;
Point plus(Point p) {
return new Point(x+p.x,y+p.y);
}
}

It contains both the state and behavior (data and functionality) of Point. The syntax is very
similar to the C version, except for the method hanging around inside. Notice that the name of
function plus_points becomes method plus; since plus is inside Point, the "_points" is
redundant. The difference between a function and a method will be explored in more detail in
the section on polymorphism. For now, it is sufficient to say that C functions or Pascal
procedures become methods inside class definitions.

How are objects initialized? In other words, how do the arguments of "new Point(1,2)" get
stored in the x and y fields? In an object-oriented language, you can define a constructor method
that sets the class data members according to the constructor arguments (arriving from the new
expression). The constructor takes the form of another method with the same name as the class
definition and no return type. For example, here is an augmented version of Point containing a
constructor.

class Point {
int x,y;
Point(int i, int j) {
x=i;
y=j;
}
Point plus(Point p) {
return new Point(x+p.x,y+p.y);
}
}
The constructor is called by the Java virtual machine once memory space has been allocated
for a Point instance during the new operation.
What would happen if you defined the constructor with arguments named the same thing as
the instance variables:
Point(int x, int y) {
x=x;
y=y;
}
Ooops. Reference to variables x and y are now ambiguous because you could mean the
parameter or the instance variable. Java resolves x to the closest definition, in this case the
parameter. You must use a scope override to make this constructor work properly:

Point(int x, int y) {
this.x=x; // set this object's x to parameter x
this.y=y;
}
This class definition is now adequate to initialize and add points as in the following fragment.
Point a=new Point(2,4);
Point b=new Point(3,5);
Point c=a.plus(b); // c is 5,9
Look at the method invocation:
c=a.plus(b);
What is this really doing and what is the difference between it and
c=d.plus(b);
for some other point, d? You are supposed to think of a.plus(b)as telling a to add b to itself and
return the result. The difference between the two method invocations is the target object, but
how does method plus know about this target; that is, how does plus know which object to add
b to--there is only one argument to the method? Remember that computers cannot "tell" objects
to do things, computers only know (in hardware) how to call subroutines and pass arguments.
It turns out that the two plus invocations are actually implemented as
follows by the computer:
c=plus(a,b);
c=plus(d,b);
where the method targets a and d are moved to arguments, thus, converting them from message
sends to function calls. Because you may have many objects with a method called plus such as
Point3D, the compiler will actually convert your method named plus to a unique function name,
perhaps Point_plus. The method could be converted to a function something like:

Point Point_plus(Point this, Point p) {


return new Point(this.x+p.x,this.y+p.y);
}

The "this" argument will be the target object referenced in the method invocation expression
as the translation above implies.
So, for the moment, you can imagine that the compiler converts the class definition, variable
definitions, and method invocations into something very much like what you would do in C or
other procedural languages. The main difference at this point is that the class definitio n
encapsulates the data of and the functions for a point into a single unit whereas C leaves the
data and functions as disconnected program elements.

Encapsulation promotes team programming efforts because each programmer can work on an
object without worrying that someone else will interfere with the functionality of that object.
A related concept enhances the sense of isolation. Data hiding allows you to specify which
parts of your object are visible to the outside world. The set of visible methods provides the
"user interface" for your class, letting everyone know how you want them to interact with that
object. Sometimes helper methods and usually your data members are hidden from others. For
example, you can modify class Point to specify visibility with a few Java keywords:
class Point {
protected int x,y;
public Point(int i, int j) {
x=i;
y=j;
}
public Point plus(Point p) {
return new Point(x+p.x,y+p.y);
}
}
The data is protected from manipulation, but the constructor and plus() method must be
publicly visible for other objects to construct and add points.

"Modifying the engine"


You know how to use objects and what class definitions look like. The next step is to augment
a class definition with additional functionality. To illustrate this, consider how you would print
out a point. To print strings to standard output in Java, you call method System.out.println().
You would like to say:
System.out.println(p);

for some point p. Java has a convention that objects are automatically converted to strings via
the toString() method, therefore, the above statement is interpreted as:
System.out.println(p.toString());

Naturally, the functionality for converting a Point to a string will be in class Point:

class Point {
Point(int i, int j) {
x=i;
y=j;
}
Point plus(Point p) {
return new Point(x+p.x,y+p.y);
}
String toString() {
return "("+x+","+y+")";
}
}

"Designing new cars"


Once you have decided upon a set of objects and their state/behavior in your design, defining
the Java classes is relatively straightforward.
Composition
Consider defining a Rectangle object composed of an upper left corner and a lower right corner.
The data portion of Rectangle is pretty simple:
class Rectangle {
Point ul;
Point lr;
}
What should the constructor for Rectangle look like? Well, how do you want to construct them?
Construction should look something like:

Rectangle r = new Rectangle(10,10, 20,40); // size 10x30


or
Point p = new Point(10,10);
Point q = new Point(20,40);
Rectangle r = new Rectangle(p,q); // size 10x30

The first Rectangle construction statement uses a constructor that takes the four coordinates
for the upper left and lower right coordinates:

public Rectangle(int ulx, int uly, int lrx, int lry) {


ul = new Point(ulx,uly);
lr = new Point(lrx,lry);
}
The second constructor takes two points:
public Rectangle(Point ul, Point lr) {
this.ul = ul;
this.lr = lr;
}

If your design calls for a method to return the width and height of a Rectangle, you could define
method, say getDimensions(), as follows.
class Rectangle {
protected Point ul;
protected Point lr;
/** Return width and height */
public Dimension getDimensions() {
return new Dimension(lr.x- ul.x, lr.y-ul.y);
}
}

Because Java does not allow multiple return values, you must encapsulate the width and height
into an object, called Dimension as returned by getDimensions() in the example:
class Dimension {
public int width, height;
public void Dimension(int w, int h) {
width=w;
height=h;
}
}
The complete Rectangle class looks like:

class Rectangle {
protected Point ul;
protected Point lr;
/** Return width and height */
public Dimension getDimensions() {
return new Dimension(lr.x- ul.x, lr.y-ul.y);
}
public Rectangle(int ulx, int uly, int lrx, int lry) {
ul = new Point(ulx,uly);
lr = new Point(lrx,lry);
}
public Rectangle(Point ul, Point lr) {
this.ul = ul;
this.lr = lr;
}
}
You might notice that we have two constructors, which naturally must have the same name--
that of the surrounding class! As long as the arguments different in type, order, and/or number,
you can reuse constructor and regular method names. When you reuse a method name, you are
overloading a method. At compile time, the compiler matches up the arguments to decide
which method to execute just as if the constructors were named differently.

Inheritance
A Rectangle object contains two Point objects. A Rectangle is not a kind of Point nor is a Point
a kind of Rectangle; their relationship is one of containment. There is another kind of object
relationship called inheritance in which one object is a specialization of another. You say that
a class extends or derives from another class and may add data or behavior. The original class
is called the superclass and the class derived from it is called the subclass.

The first important concept behind inheritance is that the subclass can behave just like any
instance of the superclass. For example, if you do not add any data members nor extend the
behavior of Rectangle, you have simply defined another name for a Rectangle:

class AnotherNameForRectangle extends Rectangle {


}

You can refer to AnotherNameForRectangle as a Rectangle:


AnotherNameForRectangle ar = ...;
Rectangle r = ar; // OK: ar is a kind of Rectangle

If instance ar is a kind of rectangle, you can ask for its dimensions:


Dimension d = ar.getDimensions();

The definition of class AnotherNameForRectangle is empty, so how can you ask for its
dimensions? The subclass inherits all data and behavior of Rectangle, therefore, object ar can
masquerade as a plain Rectangle. Just as you are born with nothing, but inherit money and
characteristics from your parents,
AnotherNameForRectangle inherits data and behavior from its parent: Rectangle. One way to
look at inheritance of data and behavior is to consider it a language construct for a "live" cut-
n-paste. As you change the definition of a class, all subclasses automatically change as well.

The second important concept behind inheritance is that you may define new objects as they
differ from existing objects, which promotes code reuse. For example, your design may call
for a FilledRectangle object. Clearly, a filled rectangle is a kind of rectangle, which allows yo u
to define the new class as it differs from Rectangle. You may extend Rectangle by adding data:
class FilledRectangle extends Rectangle {
protected String fillColor = "black"; // default color
}

Unfortunately, FilledRectangle does not, by default, know how to initialize itself as a kind of
Rectangle. You must specify a constructor, but how does a subclass initialize the contributio ns
from its superclass? A subclass constructor can invoke a superclass constructor:
class FilledRectangle extends Rectangle {
protected String fillColor = "black"; // default color
public FilledRectangle(Point ul, Point lr, String c) {
super(ul,lr); // calls constructor: Rectangle(ul,lr)
fillColor = c; // initialize instance vars of this
}
}

When you construct a FilledRectangle, space is allocated for an object the size of a Rectangle
plus a String contributed by subclass FilledRectangle. Then Java initializes the Rectangle
portion of the object followed by the FilledRectangle portion. The new operation for
FilledRectangle takes three arguments, two of which are used to initialize the Rectangle
component:
Point p = new Point(10,10);
Point q = new Point(20,40);
FilledRectangle fr = new FilledRectangle(p,q,"red");

You may add behavior to subclasses as well. For example, to allow other objects to set and
get the fill color, add a so-called "getter/setter" pair:
class FilledRectangle extends Rectangle {
protected String fillColor = "black"; // default color
public FilledRectangle(Point ul, Point lr, String c) {
super(ul,lr); // calls constructor: Rectangle(ul,lr)
fillColor = c; // initialize instance vars of this
}

public void setFillColor(String c) {


fillColor = c;
}
public String getFillColor() {
return fillColor;
}
}
Another object may then access the added functionality:
fr.setFillColor("blue");
String c = fr.getFillColor();
Recall that, since FilledRectangle is a Rectangle, you may refer to it as such:
Rectangle r = fr;
But, even though r and fr physically point at the same object in memory, you cannot treat the
Rectangle as a FilledRectangle:
fr.setFillColor("blue");// no problem
Rectangle r = fr; // r and fr point to same object
r.setFillColor("blue"); // ILLEGAL!!!
Reference r has a restricted perspective of the filled rectangle.

Summary
• An object-oriented program is a collection of objects
• Objects with same properties and behavior are instances of the same class
• Objects have two components: state and behaviour (variables and methods)
• An object may contain other objects
• Instance variables in every object, class variables are like global variables shared by all
instances of a class.

A running object-oriented program is a collection of objects that interact by sending messages


to each other like actors in a theater production. An object is an "actor" or self-conta ined
software unit that often corresponds to a real world entity and, therefore, running an object-
oriented program is like performing a simulation of the real world.

Many of the objects in a running program will have the same characteristics and conform to
the same rules of behavior. Such objects are considered to be instances of the same class. For
example, there may be many instances of the class Car in existence at any point in a running
traffic simulation. An object-oriented program is a collection of class definitions, each one
wrapping up all the data and functionality associated with a single concept or entity specified
in the program design.

Consider trying to outline the definition of a car for a computer or person unfamiliar with a car.
You would note that a car has certain properties like color and number of doors:
• color
• numberOfDoors

A car is mainly composed of an engine, a body, and four wheels:


• theEngine
• theBody
• theWheels[4]

A car has certain behavior; it can start, stop, turn left, turn right, and so on:
• start
• stop
• turnLeft
• turnRight

By adding a bit of punctuation, you can turn this outline into a Java class definition:
class Car {
// properties
String color;
int numberOfDoors;

// contained objects (Engine,Body,Wheel defined elsewhere)


Engine theEngine;
Body theBody;
Wheel[] theWheels;
// behavior
void start() {...}
void stop() {...}
void turnLeft() {...}
void turnRight(){...}
}

This class definition is analogous to the blueprint for a car versus the car instances themselves.

The data fields are called instance variables and the functions embedded within a class are
called methods to distinguish them from functions or procedures in nonobject- oriented
languages. Both the data fields and methods are called members of the class. A class definitio n
explicitly relates data and the methods that operate on it.

Instance variables and methods


Every instance of a class has a copy of the instance variables. For example, every instance of a
Car has a color, number of doors, an engine, a body, and a set of four wheels. If you have 10
cars in your program, you will have 10 strings, 10 integers, 10 engines, etc... Methods that
operate on these instance variables are instance methods. Method start() manipulates the engine
of a single car instance per invocation.

Class variables and methods


How would you count the number of cars created during the execution of the program? In a
non-object-oriented language, you would use a global variable and increment it every time you
created a new car. You cannot have global variables in Java, but you can have class variables,
which are like global variables scoped within a class definition. There is exactly one copy of
each class variable no matter how many instances of a class you create. If you have 10 cars,
they all share a single copy of a class variable. Class variables are defined just like instance
variables, but are modified with the static keyword. The constructor for a Car
would increment the count:

class Car {
// class variables
static int count = 0;
// instance variables; properties
String color;
int numberOfDoors;
...
Car( ) {
count = count + 1;
...
}
}

Reference class variables as ClassName.var as in Car.count because they exist as variables of


the class not any particular object. In fact, class variables may be referenced even if you have
not created any instances of that class.
Methods that operate on class variables only are called class methods and are also modified
with the static keyword. For example, here is a method called resetCount() that resets the car
object creation count:
class Car {
// class variables
static int count = 0;
static void resetCount() {
count = 0;
}
...
}

Encapsulation and Data Hiding


Summary
 Classes encapsulate state and behavior
 Data hiding isolates internals/implementation, providing an approved interface
 Encapsulation and data hiding:
 make team programming much easier (you are protected from them and they are
protected from themselves)
 reduce what you have to think about at once
 limit propagation of errors

Encapsulation
One of the best metaphors for the software object is the cell, the fundamental biologic a l
building block. Loosely speaking, the surface of a cell is a membrane that physically wraps the
contents (encapsulation), restricts chemical exchange with the outside environment (data
hiding), and has receptors (message interface) that receive chemical messages from other cells.
Cells interact by sending chemical messages not by directly accessing the internals of other
cells.

This metaphor extends nicely to designing programs. Imagine designing a program that tracks
your company's vehicle pool. The first thing you might do is write down all of the types of
vehicles in your company and then start to describe their characteristics and behavior. You
might have non-vehicle objects, such as the vehicle pool itself, that do not correspond directly
to physical objects. If you consider each object in your design a cell, then you will think of it
as an independent entity with an internal implementation and external interface for
interacting with other objects.
Good programmers have always tried to keep the data and the functions that manipulate that
data together, for example in the same file. Object-oriented languages formalize this strategy
by forcing you to encapsulate state and behaviour within classes.

Data hiding and interface versus implementation


Closely associated with encapsulation is the idea of data hiding where certain variables or
methods are hidden from other objects. You may prevent foreign objects from directly
manipulating the data or other implementation details of an object just as the membrane of a
cell hides its internal mechanism. Methods labeled as public are considered part of the approved
interface for using the object; assume for now that any other members are not part of the
interface. For example, the methods of the Car class above should be part of the interface, but
the variables should not:

class Car {
// properties
String color;
int numberOfDoors;
// contained objects (Engine,Body,Wheel defined elsewhere)
Engine theEngine;
Body theBody;
Wheel[] theWheels;
// behavior
public void start() {...}
public void stop() {...}
public void turnLeft() {...}
public void turnRight(){...}
}

The code within the methods and any of the member variables could be rewritten without
affecting the interface and, hence, without forcing changes in other objects.
So, encapsulation and data hiding work together as a barrier that separates the contractual
interface of an object from its implementation. This barrier is important for two reasons:
1. The interface/implementation separation protects your object from other objects. Also, when
looking for bugs, you can usually limit your search to the misbehaving object.
2. The separation also protects other objects from themselves. Changes in implementation of
one object will not require changes in the other objects that use it.

From a design perspective, this is great because it limits what you have to think about at once
and makes it much easier for multiple programmers to collaborate on a program because
objects may be implemented independently.

Sending Messages
Summary
 Objects communicate via messages
 A target object receives a message and implements it with a method
 You may overload method names
 Binding occurs at run-time
Objects in a running program collaborate by sending messages back and forth to elicit
responses, changes of state, or more message sends. An object's behavior is characterized by
how it reacts to received messages. Message sends are of the form:
targetObject.message(arguments);

At run-time, the targetObject receives the message, looks up the message name in its method
list, and executes the method with the matching signature; the signature includes the name of
the method plus the number and type of the arguments. Sending start to a Car object such as:
aCar.start(); means that aCar would look up start in its method table, find method start(), and
execute it. A method name may be reused, or overloaded, if the arguments differ in type or
number. For example, you could add another start() method to class Car as long as the signature
was different:

class Car {
...
public void start() {...}
public void start(float acceleration) {...}
...
}
Sending message start to aCar would now choose between the start() and start(float) methods
of Car depending on the argument list of the message send:
aCar.start(); // call empty-arg start method
aCar.start(9.08); // call acceleration start method

Method overloading makes an object choose between methods with different signatures within
a class and, while very convenient, is not a core principle of object-oriented programming.
Overloading should be distinguished from polymorphism, which is described in the next
section, where a message forces a choice between methods with the same signature, but in
different classes.

Polymorphism
Summary
Polymorphism is the ability for a single object reference to refer to more than a single concrete
type.
The benefit of polymorphism is that the introduction of a new type does not force widespread
code changes.
The more specific your program statements have to be, the less they can deal with change and
the shorter their useful lifespan. The primary adaptability of objectoriented programming
derives from the message sends between objects. Consider that, when you call a function in C
or other procedural language, you are specifying exactly the code you want to execute. While
easy to follow, such exactness renders your program extremely brittle. Sending a message to
an object, on the other hand, relies on the receiver object to react to the message by executing
an appropriate method. You can swap out the receiver object and replace it with any other
object as long as that object answers the same message. No change is required to the message
send code. For example, if someone hands you an object and you know it is one of 5 objects
that can respond to message start, there are 5 possible start() methods that could be executed in
response to the start message. This nonspecificity and flexibility is called polymorphism
because the object may take one of many forms.

If there are 5 different classes that can answer start, there are 5 possible methods that could be
executed by this one statement if v is an instance of one of those 5 classes:
v.start();
At run-time the exact type of object, such as Car, is known and the target object binds the start
message to its start() method.

The equivalent code written in C would use a function call instead of a message send and would
have to specify exactly which function to call. If there is only one data type that you can start,
a single function call suffices:
startCar(v);

Adding a new data type, say, Truck, would mean adding an if-then-else statement to deal with
the newly-introduced type; you would have to do something like this:

// C fragment
if ( v.type == CAR_TYPE ) { // assume vehicle stores type
startCar(v);
}
else if ( v.type == TRUCK_TYPE ) {
startTruck(v);
}

A non-object-oriented language simply has no mechanism for expressing the similarity of a


truck and a car and that start is understood by both. (Typically, procedural programmers will
make a single struct or record that can hold state for any similar type of thing and then they
make an integer variable that indicates what the struct is supposed to be--truck or car or
whatever). The big difference between the approaches is that in C you have to handle the
various types at the call site manually whereas an object-oriented language does this "switch
on type" for you.

Some object-oriented languages, such as SmallTalk, allow you to send any message you want
to any other object, which can lead to annoying run-time errors like "object does not impleme nt
that message." Further, while both Rectangle and GunFighter might understand message draw,
the same message is surely interpreted in two different ways. It would be nice if the compiler
informed you at compile-time that you were sending the same message to very differe nt
objects.

Java provides this service via strong typing. For each object you refer to, you have to specify
the type of object, kind of object, or behavior of the object. You cannot perform assignme nts
between dissimilar objects, which prevents you from sending a message to a radically differe nt
object inadvertently.

GunFighter gf = ...;
Rectangle r = gf; // INVALID!!! compile error!
r.draw();

So how does Java know which objects are similar and which are unrelated? Java uses the notion
of inheritance to express similarity between classes.

Inheritance
Summary
 Inheritance specifies the commonality or similarity relationship between classes.
 It is a relationship of identify
 A subclass inherits state and behavior from the superclass
 Any reference to an object can be given a reference to a subclass instead
 The inheritance relationships together form a class hierarchy, which helps to organize
your program
 Classes whose primary job is to group similar classes is often called an abstract class
 Inheritance lets you define by difference: augment, override, or refine members
inherited from superclass
 Polymorphism uses the inheritance relationship as its definition of similarity; Java
restricts a message send to similar objects as its definition of similarity; Java restricts
a message send to similar objects.

Object-oriented languages support a simple, but potent idea called inheritance that lets you
express the similarity between different class types. A class (the subclass) that inherits from
another acquires all of the properties and behavior of the parent class (superclass). For this
reason any two classes that inherit from the same superclass share a common set of properties
and behavior, namely that of the superclass, and you can treat the two subclasses similarly.

Inheritance therefore expresses a relationship of identity or commonality, sometimes called an


"is kind of" relationship. For example, to express that Manager and Developer are similar, make
them share a superclass (perhaps Employee) that encapsulates their common properties and
behavior. This indicates that managers and developers are kinds of employees. Java uses the
extends keyword to specify this inheritance relationship. Here are the skeletons for these three
class types:

class Employee {
...
}
class Manager extends Employee {
...
}
class Developer extends Employee {
...
}

Because inheritance is an expression of similarity, you can:


1. organize your program as a hierarchy of related classes
2. define new classes as they differ from existing classes
3. refer to many similar classes as a group rather than individually resulting in more flexible
programs.

Class hierarchies
Many procedural languages are just "bags" of variables, data types, and functions as there is
no language construct to group them or show the relationships betweenthem. Some langua ges
have modules or packages that can group things but, still, the relationship between functio ns
and data and between data types remains unspecified. Often, the proximity of one function to
another is due to the timing of when you typed in the code.

The class mechanism of object-oriented languages lets you encapsulate data and behavior
associated with each conceptual entity and the class inheritance relationship lets you specify
the relationship between the classes. The combined result of all inheritance relationships is a
tree, or class hierarchy. In Java, this class hierarchy has a single root, class Object, from which
all classes directly or indirectly inherit. You can interpret this to mean that all objects are related
in that they all share the characteristics of a basic Object.

When designing an object-oriented program, you decide what objects you will need in the
system and then you look for similarities between objects that you can exploit. For example,
given objects of types Manager, Developer, Marketeer, VP, CEO, and Guru, you would
probably define the following inheritance relationships:

When looking for similarities, you will often create abstract classes that exist purely to group
related classes and to contain the factored data and methods; abstract classes are organizatio na l
classes that have no run-time instances.

Creating class Employee helps to organize your classes as it shows the relationship now
between all classes: they are all employees. Further, Employee would probably contain
common data and behavior such as method getName(), automatically extending that
functionality to all employees, that is all subclasses of Employee.

For small sets of classes, a decent class hierarchy is usually obvious, but this is not true for
large programs. Different designers will analyze the same situation differently yielding
different classes and class organizations. Following this, different programmers will add
different "helper" classes to actually implement the design. The classes and their relations hips
depends on the perspective and experience of the designers and developers.

Defining by difference
Another way to exploit the class inheritance relationship is to define new classes as they differ
from existing classes. For example, to define SalesPerson, you could extend Employee and to
define Analyst you could extend Developer (assuming all analysts know how to develop
software).

Organizing your program into a hierarchy, then, means that you have less work to do to
augment a program. Inexperienced developers often take this to mean that you extend a class
if you need to reuse or borrow some or all of its code. Unfortunately, this practice results in
poorly organized and specified programs.

Use the class hierarchy to show the appropriate relationships between the classes and code
reuse occurs naturally as a side effect. A good rule of thumb is that if the phrase "x is a kind of
y" sounds correct in English, x is probably a appropriate subclass of y. For example, reconsider
the Point and Rectangle classes from above. A Rectangle has two points so you might be
tempted to have Rectangle extend Point so you could reuse all the Point code, but a Rectangle
contains two Point objects it is not a kind of Point.

When you extend a class you have the opportunity to add data or method members to specialize
or augment the characteristics of the super class. For example, you might define Employee as:
class Employee {
String name;
int id;
public Employee(String name, int id) {// constructor
this.name = name;
this.id = id;
}
public void setName(String name) { // "setter"
this.name = name;
}
public String getName() { // "getter"
return name;
}
}

Then, you can then augment Employee with, say, an office number (plus the usual
"getter/setter") methods to define Manager:

class Manager extends Employee {


int officeNumber;
public Manager(String name, int id) {
super(name, id);
}
public void setOfficeNumber(int n) {
officeNumber = n;
}
public int getOfficeNumber() {
return officeNumber;
}
}
The constructor does nothing in this case but delegate to the constructor of Employee. Another
thing you can do with inheritance is override methods inherited from your superclass to
specialize the behavior of a subclass. For example, you might want to redefine getName() in
Manager so that it always returns "Boss" instead of the manager's name:
class Manager extends Employee {
...
public String getName() {
return "Boss";
}
}
Manager m = new Manager("Jimbo", 213002);
String mname = m.getName(); // mname is "Boss"

A Developer on the other hand, would still respond with the employee's name unless it too
overrode getName():
Developer d = ...;
String dname = d.getName(); // dname is developer's name

One final thing you can do with inheritance is refine the behavior of the superclass. If you
wanted a Manager to respond to getName with the person's name prefixed with "Boss", the
following definition would suffice:

class Manager extends Employee {


...
public String getName() {
// return name prefixed with Boss
return "Boss" + super.getName();
}
}

The expression super.getName() is a reference to the definition of getName() in the superclass.


Simply referencing getName() within Manager's getName() would, naturally, result in an
infinite-recursion loop. You need the scope override reference "super." to disambiguate which
getName() method to call.

A tricky situation can arise when a subclass delegates functionality to a superclass that in turns
calls a method that is overridden in the subclass. For example, assume Manager does not
override getName(), delegating it to superclass Employee. But, Employee.getName() simply
returns the result of calling toString(). What value does name get in the following code?
Manager m = new Manager("Jimbo", 213002);
String name = m.getName();

It depends. Scenario 1: Employee.toString() returns "An Employee" and there is no


Manager.toString() method. Variable name will be "An Employee" because target m delegates
response to getName in Employee, which calls Employee's toString(). Scenario 2:
Manager.toString() returns "A Manager". Now, name will be "A Manager" because
Employee.getName() calls toString() which you knows is really this.toString(). The target, this
(which points at the same thing as m), is a manager and, hence, Manager's toString() is called.
You might wonder if you may remove or hide functionality inherited from an object's
superclass? No, because the subclass could no longer behave like the superclass and, hence,
the subclass would not be the same kind of object. If you hid getName() in Manager, a Manager
could not act like a generic Employee since you could not ask it to respond with its name.

Inheritance and type equivalence


Java does not let you send any message to any object--the objects must be similar by some
definition. Class inheritance is the primary definition of similarity for polymorphism in
strongly typed systems like Java. Two objects are similar if they inherit from the same parent
directly or indirectly; that is, they are both kinds of the same object. Java restricts message
sends to dissimilar objects by preventing assignment of completely dissimilar types. You may
assign an instance of any subclass to its superclass, but not the other way around:
Manager m;
Employee e;
Developer d;
e = m; // OK: a Manager is a kind of Employee
e = d; // OK: a Developer is a kind of Employee
m = e; // NO: an Employee is not necessarily a Manager

Nor can you assign instances of classes that are at the same level in the hierarchy:
m = d; // NO: a Manager is not a kind of Developer
d = m; // NO: a Developer is not a kind of Manager

The type of the object on the left-hand-side must be a superclass of the type on the right-ha nd-
side. This prevents you from sending subsequent messages to an object that might not respond
to that message. For example, you cannot create a generic employee and then treat it like a
manager:
Employee e = ...;
Manager boss = e; // compile-time type error!
boss.setOfficeNumber(210); // run-time error!

Inheritance and polymorphism


If you have an array of Employee objects such as developers, managers, gurus, and marketeers,
can you print out each object's name with the following loop?
Employee[] employees = ... ;
for (int i=0; i<employees.length; i=i+1) {
System.out.println(employees[i].getName());
}

Sure. The beauty of inheritance is that you can treat any subclasses of Employee just like an
Employee. Each time through the loop, the message getName is sent to the ith object in the
employees array. The employees[i].getName() expression does not uniquely specify what code
to run. It only specifies that the getName() method of one of Employee subclasses will be
executed. The exact method cannot be determined until run-time when the concrete object type
of each employees[i] is known. Therein lies the flexibility of polymorphism. Ten years from
now, if someone adds another kind of employee, this loop would still work without
modification.

You can see polymorphism at work everyday in the real world. Programming instructors use
polymorphism to teach, for example. Each student that walks into the classroom is treated
strictly as a developer and, since the instructor presumably does not know the students, that is
all the instructor can assume. Any other attributes or specialties of the person are irrelevant and
hidden such as their nationality, skill level, political affiliation, etc... As long as they are
developers, the instructor can teach them. The instructor teaches by sending messages that
developers understand to the students such as "open a new project called DBQuery." In Java,
the analogous situation might be represented as a teach(Developer) method within an Instructor
class:

class Instructor {
void teach(Developer d) {
d.openNewProject("DBQuery");
...
}
...
}
A company might have 5 different kinds of developers, but any kind can be passed to method
teach(Developer) if the developers are defined as subclasses of Developer. If a nondeveloper
happened to walk into the classroom, they would not understand what was being said.
Similarly, if a nondeveloper was somehow passed to teach(Developer) a run-time error would
occur indicating that the object does not know how to respond to the openNewProject message.
The instructor could be introduced to a random person in the hall, so another method, greet(),
would make sense:

class Instructor {
void teach(Developer d) {
d.openNewProject("DBQuery");
...
}
void greet(Employee e) {
String name = e.getName();
...
}
...
}

Method greet(Employee) is much more widely applicable than teach(Developer) and would
work for any kind of employee including developers:
Instructor instr = ...;
Developer jim = ...;
Marketeer pat = ...;
instr.greet(jim);
instr.greet(pat);
instr.teach(jim); // only jim can be taught programming

On the other hand, the instructor can assume much less about the incoming objects. The
instructor is limited to asking the Employee objects for their name.

Polymorphism and Interface Inheritance


Summary
 Class inheritance specifies identity and identity implies behavior, but behavior does not
imply
 Identity.
 Encapsulation and data hiding team up to separate the internal implementation of an
object from its external approved interface.
 Java formalizes the notion of an interface so that you may refer to objects by their partial
behaviour rather than their identity, resulting in more flexible and adaptable programs.
Procedural languages like C force you to specify exact types like "this is a car." Object-oriented
languages, because of class inheritance, let you be more flexible: "this is a kind of vehicle."
Java lets you be even more flexible; you can say "this object can turn left and right." The secret
to writing flexible code is putting the fewest restrictions possible on the types of objects you
refer to. If you can get away with specifying the kind of object instead of a concrete type, do
it. If you can get away with specifying a partial behavior of an object instead of the kind of
object, do it.

Class inheritance implies identity and identity implies a behavior, but similar behavior does
not necessarily imply the same identity. For example, broccoli and corn share the vegetable
identity and, hence, both behave like vegetables:

class Vegetable {
public void die() {...}
void growRoots() {...}
...
}
class Broccoli extends Vegetable {...}
class Corn extends Vegetable {...}

Cows and chickens share an animal identity and behave like animals:
class Animal {
public void die() {...}
void eat() {...}
...
}
class Cow extends Animal {...}
class Chicken extends Animal {...}

While all of these flora and fauna objects may share a common message such as die, being
mortal does not indicate vegetable or animal. If all you care about is an object's mortality, you
need a type system that lets you specify behavior rather than just identity: Java provides a
construct called an interface that looks exactly like a class, but without any method
implementations or data variables. Here is an interface that defines the mortality behavior:
// all Mortal objects implement method die().
interface Mortal {
public void die();
}

If a class of objects can answer die, you say that the class implements Mortal. You can then
rewrite Animal and Vegetable to indicate their mortality:
class Animal implements Mortal {
public void die() {...}
void eat() {...}
...
}
class Vegetable implements Mortal {
public void die() {...}
void growRoots() {...}
...
}

The power of interfaces comes when you reference objects by their behavior as in the following
method that accepts a Mortal object:
void kill(Mortal o) {
o.die(); // could be an Animal or Vegetable
}

As a more realistic example, consider the List behavior:


interface List {
public void add(Object o);
public Object getElementAt(int i);
}

Two totally different classes can implement the behavior:


class LinkedList implements List {
public void add(Object o) {
...
}
public Object getElementAt(int i) {
...
}
...
}
class DynamicArray implements List {
public void add(Object o) {
...
}
public Object getElementAt(int i) {
...
}
...
}
You can create either a LinkedList object or a DynamicArray object and refer to either by
their shared List behavior.
LinkedList llist = ...;
DynamicArray darray = ...;
List alist;
alist = llist;
alist = darray;
Given some method foo() that accepts an object behaving like a List:
void foo(List a) {
a.add("Joe");
a.getElementAt(0);
}
you can can pass either data structure to the same method:
foo(llist);
foo(darray);
The benefit is that you can pass the linked list if you need fast insertions or pass the dynamic
array if you need fast access to the ith element.

2.5 A Start-to-Finish Example

Analysis and Design


First, think about what your goals. You would like a Book class that you can addmchapters to
and that you can tell to print() out. Next, think about the players in your "simulation". What
does a book look like? It has a title, author, a table of contents, a number of chapters, and an
index. A chapter has sections and paragraphs. A section has sections and paragraphs too. You
might decide that your program then has the following kinds of objects:
 Book
 Title
 Author
 TOC
 Index
 Chapter
 Section
 Paragraph
The next step is often to design the containment relationship. In other words, what objects
refers to what objects? Who is composed of what other objects? In this case, you can represent
the containment relationships by imagining a sample book with a few chapters, sections, and
paragraphs. Indentation in the following "book" implies containment. For example, a book has
a title, an author, set of chapters, etc... Chapters have sections and so on:
Book Title: "JDK User's Guide"
Author: "MageLang"
TOC: "..."
Paragraph: "The Preface"
1. Chapter: "Overview of JDK"
1. Section: "Introduction"
1. Paragraph: "1st para of intro section of chapter 1"
2. Paragraph: "2nd para of intro section of chapter 1"
2. Section: "Features"
1. Paragraph: "1st para of features section of chapter 1"
3. Section: "Help"
1. Paragraph: "1st para of help section of chapter 1"
2. Paragraph: "2nd para of help section of chapter 1"
2. Chapter: Getting Started
1. Section: "Installation"
1. Paragraph: "1st para of install section of chapter 2"
2. Section: "Unzipping"
1. Paragraph: "1st para of unzip subsection of install section chapter 2"
3. Section: "Setting CLASSPATH"
1. Paragraph: "1st para of classpath subsection of install section chapter 2"
4. Section: "Setting PATH"
1. Paragraph: "1st para of setting PATH section of chapter 2"
2. Paragraph: "2nd para of setting PATH section of chapter 2"
Index: "..."

Coding
If you are going to walk through the complete coding of this problem, you may find the
following coding strategy helpful when implementing the design:
1. Define all the classes for the actors without inheritance
2. Add the inheritance relationships
3. Add the properties
4. Implement print() where appropriate
5. Implement the constructors and add() methods used for construction
6. Make a TestHarness class with a main() that constructs a book and does tells it to print itself
out
7. Walk through the execution of the program with a debugger to see how the objects get
constructed and how you can look at the contents of the overall book containment "tree".
8. Walk through the execution of the program with a debugger, tracing methods and watching
how it prints out and recurses the containment relationships.

Code Listing
class Book extends TextContainer {
private String TOC;
private String index;
private String title;
private String author;
public Book(String title, String author) {
this.title = title;
this.author= author;
}
public void print() {
// do title page
System.out.println();
System.out.println();
System.out.println();
System.out.println(title);
System.out.println();
System.out.println("BY");
System.out.println();
System.out.println(author);
System.out.println();
System.out.println(TOC);
super.print();
System.out.println();
System.out.println(index);
}
public void setIndex(String i) {
index = i;
}
public void setTOC(String t) {
TOC = t;
}
}
class Chapter extends TextContainer {
private String title;
public Chapter(String t) {
title = t;
}
public void print() {
System.out.println();
System.out.println(title);
System.out.println();
super.print();
}
}
class Section extends TextContainer {
private String title;
public Section(String title) {
this.title = title;
}
public void print() {
System.out.println();
System.out.println(title);
super.print();
}
}
class Paragraph extends TextEntity {
private String text;
public Paragraph(String t) {
text = t;
}
public void print() {
System.out.println(text);
}
}
abstract class TextEntity {
public abstract void print();
}
abstract class TextContainer extends TextEntity {
private static final int MAX_CONTAINED = 20;
private int n=0;
private TextEntity[] elements;
public void add(TextEntity e) {
if ( elements==null ) {
elements = new TextEntity[MAX_CONTAINED];
}
elements[n] = e;
n = n + 1;
}
public void print() {
for (int i=0; i<n; i=i+1) {
elements[i].print();
}
}
}
class TestHarness {
public static void main(String[] args) {
Book b = new Book("JDK User's Guide", "MageLang");
b.setTOC("Preface\n"+
"1. Overview of JDK\n"+
"2. Getting Started\n");
b.setIndex("the index");
Paragraph preface = new Paragraph("The Preface");
Chapter overview = new Chapter("1. Overview of JDK");
Chapter started = new Chapter("2. Getting Started");
b.add(preface);
b.add(overview);
b.add(started);
// chapter 1
Paragraph hello =
new Paragraph("Intro paragraph of chapter 1");
Section intro =
new Section("Introduction");
intro.add(
new Paragraph("1st para of intro section of chapter 1")
);
intro.add(
new Paragraph("2nd para of intro section of chapter 1")
);
Section features = new Section("Features");
features.add(
new Paragraph("1st para of features section of chapter 1")
);
Section help = new Section("Help");
help.add(
new Paragraph("1st para of help section of chapter 1")
);
help.add(
new Paragraph("2nd para of help section of chapter 1")
);
overview.add(hello);
overview.add(intro);
overview.add(features);
overview.add(help);
// chapter 2
Section install = new Section("Installation");
install.add(
new Paragraph("1st para of install section of chapter 2")
);
// 2 subsections of section 1
Section unzip = new Section("Unzipping");
unzip.add(
new Paragraph(
"1st para of unzip subsection of install section chapter 2")
);
Section classpath = new Section("Setting CLASSPATH");
classpath.add(
new Paragraph(
"1st para of classpath subsection of install section chapter 2")
);
Section path = new Section("Setting PATH");
path.add(
new Paragraph(
"1st para of setting PATH subsection of install chapter 2")
);
path.add(
new Paragraph(
"2nd para of setting PATH subsection of install chapter 2")
);
install.add(unzip);
install.add(classpath);
install.add(path);
started.add(install);
// NOW: PRINT THE BOOK!
b.print();
}
}

2.6 TEMPLATES
2.6.1 Function templates
Function templates are special functions that can operate with generic types. This allows us to
create a function template whose functionality can be adapted to more than one type or class
without repeating the entire code for each type.
In C++ this can be achieved using template parameters. A template parameter is a special kind
of parameter that can be used to pass a type as argument: just like regular function parameters
can be used to pass values to a function, template parameters allow to pass also types to a
function. These function templates can use these parameters as if they were any other regular
type.
The format for declaring function templates with type parameters is:
template <class identifier> function_declaration;
template <typename identifier> function_declaration;
The only difference between both prototypes is the use of either the keyword class or the
keyword typename. Its use is indistinct, since both expressions have exactly the same meaning
and behave exactly the same way.
For example, to create a template function that returns the greater one of two objects we could
use:
template <class myType> myType GetMax (myType a, myType b)
{return (a>b?a:b); }
Here we have created a template function with myTypeas its template parameter. This template
parameter represents a type that has not yet been specified, but that can be used in the template
function as if it were a regular type. As you can see, the function template GetMax returns the
greater of two parameters of this still- undefined type.
To use this function template we use the following format for the function call:
function_name <type> (parameters);

For example, to call GetMax to compare two integer values of type int we can write:
int x,y;
GetMax <int> (x,y);

When the compiler encounters this call to a template function, it uses the template to
automatically generate a function replacing each appearance of myType by the type passed as
the actual template parameter (int in this case) and then calls it. This process is automatica lly
performed by the compiler and is invisible to the programmer.

Here is the entire example:


// function template
#include <iostream>
usingnamespace std;

template <class T>


T GetMax (T a, T b)
{ T result;
result = (a>b)? a : b;
return (result);
}
int main ()
{ int i=5, j=6, k;
long l=10, m=5, n;
k=GetMax<int>(i,j);
n=GetMax<long>(l,m);
cout << k << endl;
cout << n << endl;
return 0;
}

oUTPUT: 6 10

In this case, we have used T as the template parameter name instead of myType because it is
shorter and in fact is a very common template parameter name. But you can use any identifier
you like. In the example above we used the function template GetMax() twice. The first time
with arguments of type int and the second one with arguments of type long. The compiler has
instantiated and then called each time the appropriate version of the function. As you can see,
the type T is used within the GetMax()template function even to declare new objects of that
type:
T result;
Therefore, result will be an object of the same type as the parameters a and b when the functio n
template is instantiated with a specific type.
In this specific case where the generic type T is used as a parameter for GetMax the compiler
can find out automatically which data type has to instantiate without having to explicitly
specify it within angle brackets (like we have done before specifying <int> and <long>). So
we could have written instead:
int i,j;
GetMax (i,j);
Since both i and j are of type int, and the compiler can automatically find out that the template
parameter can only be int. This implicit method produces exactly the same result:
// function template II
#include <iostream>
usingnamespace std;
template <class T>
T GetMax (T a, T b)
{ return (a>b?a:b);
}
int main ()
{ int i=5, j=6, k;
long l=10, m=5, n;
k=GetMax(i,j);
n=GetMax(l,m);
cout << k << endl;
cout << n << endl;
return 0;
}
6
10

Notice how in this case, we called our function template GetMax() without explicitly
specifying the type between angle-brackets <>. The compiler automatically determines what
type is needed on each call.
Because our template function includes only one template parameter (class T) and the functio n
template itself accepts two parameters, both of this T type, we cannot call our function template
with two objects of different types as arguments:
int i;
long l;
k = GetMax (i,l);

This would not be correct, since our GetMax function template expects two arguments of the
same type, and in this call to it we use objects of two different types.
We can also define function templates that accept more than one type parameter, simply by
specifying more template parameters between the angle brackets. For example:
template <class T, class U>
T GetMin (T a, U b)
{
return (a<b?a:b);
}

In this case, our function template GetMin() accepts two parameters of different types and
returns an object of the same type as the first parameter (T) that is passed. For example, after
that declaration we could call GetMin() with:

int i,j;
long l;
i = GetMin<int, long> (j,l);
or simply:
i = GetMin (j,l);
even though j and l have different types, since the compiler can determine the apropriate
instantiation anyway.

2.6.2 Class Templates


We also have the possibility to write class templates, so that a class can have members that
use template parameters as types. For example:

The class that we have just defined serves to store two elements of any valid type. For example,
if we wanted to declare an object of this class to store two integer values of type int with the
values 115 and 36 we would write:

this same class would also be used to create an object to store any other type:

The only member function in the previous class template has been defined inline within the
class declaration itself. In case that we define a function member outside the declaration of the
class template, we must always precede that definition with the template <...> prefix:
Notice the syntax of the definition of member function getmax:

Confused by so many T's? There are three T's in this declaration: The first one is the template
parameter. The second T refers to the type returned by the function. And the third T (the one
between angle brackets) is also a requirement: It specifies that this function's template
parameter is also the class template parameter.

2.6.3 Template Specialization


If we want to define a different implementation for a template when a specific type is passed
as template parameter, we can declare a specialization of that template. For example, let's
suppose that we have a very simple class called mycontainer that can store one element of any
type and that it has just one-member function called increase, which increases its value. But
we find that when it stores an element of type char it would be more convenient to have a
completely different implementation with a function member uppercase, so we decide to
declare a class template specialization for that type:
This is the syntax used in the class template specialization:

First of all, notice that we precede the class template name with an emptytemplate<> parameter
list. This is to explicitly declare it as a template specialization. But more important than this
prefix, is the <char>specialization parameter after the class template name. This specializa tio n
parameter itself identifies the type for which we are going to declare a template class
specialization (char). Notice the differences between the generic class template and the
specialization:
The first line is the generic template, and the second one is the specialization. When we declare
specializations for a template class, we must also define all its members, even those exactly
equal to the generic template class, because there is no "inheritance" of members from the
generic template to the specialization.
2.6.4 Non-type parameters for templates
Besides the template arguments that are preceded by the class or typename keywords, which
represent types, templates can also have regular typed parameters, similar to those found in
functions. As an example, have a look at this class template that is used to contain sequences
of elements:

It is also possible to set default values or types for class template parameters. For example, if
the previous class template definition had been:

We could create objects using the default template parameters by declaring:

which would be equivalent to:


2.6.5 Templates and multiple-file projects
From the point of view of the compiler, templates are not normal functions or classes. They are
compiled on demand, meaning that the code of a template function is not compiled until an
instantiation with specific template arguments is required. At that moment, when an
instantiation is required, the compiler generates a function specifically for those arguments
from the template.
When projects grow it is usual to split the code of a program in different source code files. In
these cases, the interface and implementation are generally separated. Taking a library of
functions as example, the interface generally consists of declarations of the prototypes of all
the functions that can be called. These are generally declared in a "header file" with a .h
extension, and the implementation (the definition of these functions) is in an independent file
with c++ code.
Because templates are compiled when required, this forces a restriction for multi- file projects:
the implementation (definition) of a template class or function must be in the same file as its
declaration.
That means that we cannot separate the interface in a separate header file, and that we must
include both interface and implementation in any file that uses the templates.
Since no code is generated until a template is instantiated when required, compilers are
prepared to allow the inclusion more than once of the sametemplate file with both declarations
and definitions in a project without generating linkage errors.

2.7 Memory Management


The model
In the model chosen for this document, we simplify both Java and C++ programs as single
processes with one thread of execution. A program is assigned three independent portions of
memory, as address space:
•Code area - where code to be executed is stored
•Dynamic Memory Area a.k.a. Heap - to store variables and objects allocated
dynamicallyaccessed with no restrictions
•Execution Stack - to perform computation, store local variables and perform function call
management accessed with a LIFO policy (Last In - First Out)

Code load and execution


Code is loaded on class-by-class basis
1. The class containing the main method is loaded
2. All the other classes are loaded, according to the flow of computation.
The execution is centered around the execution stack, the order of execution is LIFO -> the
last method called is the first to terminate.

Activation Record (AR)


Each time a method is called, all the information specifically needed for the method execution
is put on the stack. That information is called the activation record (AR) of the method call.
This system allows recursion, since for each call there will be a separate activation record on
the stack. When the call is completed the corresponding AR is destroyed. Activation records
are organized from bottom to top in memory diagram.
Example of function calls and activation record usage:
void f()
{ g();
}
void g()
{ h();
}
void h()
{ k();
}

Contents of the Activation Record


One call on the activation record contains the following information:
•Information to restart the execution at the end of the call (i.e., after the function returns)
Return value (if any) RV (what am I returning?)
Return address RA (where am I returning?)
Pointer to the Stack portion devoted to the calling function SP (where am I from?)
•Information needed to perform the computation (the actual arguments passed to the method)
•Local variables

Abbreviations for AR
Some definitions
Declaration vs. Definition
By declaring an entity we inform the compiler about the structure and the behavior of the
entity itself and bind unique name to it.
By defining an entity we instruct the compiler to generate code to performmemory allocatio n
for that entity
The scope of a variable
The scope of a variable is a portion of the source code in which that variable is visible.
Extent of a Variable
The extent (or lifetime) describes when in a program's execution a variable has a value. A
variable can exist (extent) but be not visible (scope). i.e., global variables hidden by
homonymous local variable in a scope.
Blocks
A block is a portion of code enclosed between two special symbols, which mark the beginning
and the end of the block.

Elements of Dynamic Memory Allocation and Handling


Dynamic Vs. Static memory allocation

Stack-based variables have their extent determined by their scope, so the former is constraine d
by the structure of the code at compile-time.
Sometimes there is a need for the variables with unconstrained extent in order to cope with
problems where lifetime of a variable can only be known at run-time. In this case heap-based
variables, whose extent is strictly under control of the programmer, are used.
The programmer can create such a variable any time and ask the system to dispose it when it
is no longer needed.

Dynamic Memory Scope and Extent

In majority of programming languages, the scope of an entity allocated on the heap is the union
of the scopes of all the variables referring to it. Usually, the extent of dynamic data starts when
they are created and lasts until they are destroyed or until the program terminates.

Accessing dynamic memory


Dynamic allocated entities have no name, thus an alternative way of referring to them is
required. This is achieved by using special kind of named variables (regular, stack-based) In
Java there are reference variables, whereas in C++ there are both reference and pointer
variables (which are conceptually the same).

2.8 Object Oriented Memory Management


Classes
A class represents the general properties and the structure shared by a group of entities: the
objects of that class. Classes are units of data abstraction and units of interaction (objects
interacting).
Objects
Objects are instances of classes (classes in action). Objects of the same class share the same
general structure and behavior.
Object instantiation
The creation of a new object of a given class is called instantiation.
In Java objects are created (allocated) using the keyword new. References to objects are usually
initialized with the value null. Objects can only be instantiated in heap.
In C++ classes are truly user defined types. Objects are treated as any other variable and are
allocated:

 on the stack, as regular local variables


 on on the heap, like in Java
Objects in Memory (Java):
Module-3

Design Patterns

3. 1 Introduction to Design Patterns


By definition, Design Patterns are reusable solutions to commonly occurring problems (in the
context of software design). Design patterns were started as best practices that were applied
again and again to similar problems encountered in different contexts. In 1994, four authors
Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides published a book titled Design
Patterns - Elements of Reusable Object-Oriented Software which initiated the concept of
Design Pattern in Software development. These authors are collectively known as Gang of
Four (GOF). According to these authors design patterns are primarily based on the following
principles of object orientated design.
 Program to an interface not an implementation
 Favour object composition over inheritance

Design patterns provide a standard terminology and are specific to particular scenario. For
example, a singleton design pattern signifies use of single object so all developers familiar with
single design pattern will make use of single object and they can tell each other that program
is following a singleton pattern.

3.2 Classification of Design Patterns


As per the design pattern reference book Design Patterns - Elements of Reusable Object-
Oriented Software, there are 23 design patterns which can be classified in three categories:
Creational, Structural and Behavioural patterns.

3.2.1 Creational Patterns


These design patterns provide a way to create objects while hiding the creation logic, rather
than instantiating objects directly using new operator. This gives program more flexibility in
deciding which objects need to be created for a given use case.
1. Factory Pattern: Factory pattern is one of the most used design patterns in Java. This
type of design pattern comes under creational pattern as this pattern provides one of
the best ways to create an object. In Factory pattern, we create object without exposing
the creation logic to the client and refer to newly created object using a common
interface.

Implementation of Factory Pattern


We're going to create a Shape interface and concrete classes implementing the Shape
interface. A factory class ShapeFactory is defined as a next step. FactoryPatternDe mo,
our demo class will use ShapeFactory to get a Shape object. It will pass informa tio n
(CIRCLE / RECTANGLE / SQUARE) to ShapeFactory to get the type of object it
needs.
FactoryPatternDemo, our demo class will use ShapeFactory to get a Shape object. It
will pass information (CIRCLE / RECTANGLE / SQUARE) to ShapeFactory to get
the type of object it needs.

Figure 3.1: Factory Pattern UML Diagram

Step 1: Create an interface.

Shape.java
public interface Shape {
void draw();
}
Step 2: Create concrete classes implementing the same interface.
Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java

public class Square implements Shape {

@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java
public class Circle implements Shape {

@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
Step 3: Create a Factory to generate object of concrete class based on given
information.

ShapeFactory.java

public class ShapeFactory {

//use getShape method to get object of type shape


public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();

} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();

} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}

return null;
}
}
Step 4: Use the Factory to get object of concrete class by passing information such as
type.

FactoryPatternDemo.java

public class FactoryPatternDemo {

public static void main(String[] args) {


ShapeFactory shapeFactory = new ShapeFactory();

//get an object of Circle and call its draw method.


Shape shape1 = shapeFactory.getShape("CIRCLE");
//call draw method of Circle
shape1.draw();

//get an object of Rectangle and call its draw method.


Shape shape2 = shapeFactory.getShape("RECTANGLE");

//call draw method of Rectangle


shape2.draw();

//get an object of Square and call its draw method.


Shape shape3 = shapeFactory.getShape("SQUARE");

//call draw method of square


shape3.draw();
}
}
Step 5: Verify the output.

Inside Circle::draw() method.


Inside Rectangle::draw() method.
Inside Square::draw() method.

2. Abstract Factory Pattern


Abstract Factory patterns work around a super-factory which creates other factories.
This factory is also called as factory of factories. This type of design pattern comes
under creational pattern as this pattern provides one of the best ways to create an object.

In Abstract Factory pattern an interface is responsible for creating a factory of related


objects without explicitly specifying their classes. Each generated factory can give the
objects as per the Factory pattern.

Implementation
We are going to create a Shape and Color interfaces and concrete classes implementing
these interfaces. We create an abstract factory class AbstractFactory as next step.
Factory classes ShapeFactory and ColorFactory are defined where each factory extends
AbstractFactory. A factory creator/generator class FactoryProducer is created.

AbstractFactoryPatternDemo, our demo class uses FactoryProducer to get a


AbstractFactory object. It will pass information (CIRCLE / RECTANGLE / SQUARE
for Shape) to AbstractFactory to get the type of object it needs. It also passes
information (RED / GREEN / BLUE for Color) to AbstractFactory to get the type of
object it needs.
Figure 3.2: Abstract Factory Pattern UML Diagram

We are going to create a Shape and Color interfaces and concrete classes implementing
these interfaces. We create an abstract factory class AbstractFactory as next step.
Factory classes ShapeFactory and ColorFactory are defined where each factory extends
AbstractFactory. A factory creator/generator class FactoryProducer is created.

AbstractFactoryPatternDemo, our demo class uses FactoryProducer to get a


AbstractFactory object. It will pass information (CIRCLE / RECTANGLE / SQUARE
for Shape) to AbstractFactory to get the type of object it needs. It also passes
information (RED / GREEN / BLUE for Color) to AbstractFactory to get the type of
object it needs.

Step 1: Create an interface for Shapes and Colors.

Shape.java

public interface Shape {


void draw();
}
Step 2: Create concrete classes implementing the same interface.

RoundedRectangle.java

public class RoundedRectangle implements Shape {


@Override
public void draw() {
System.out.println("Inside RoundedRectangle::draw() method.");
}
}
RoundedSquare.java

public class RoundedSquare implements Shape {


@Override
public void draw() {
System.out.println("Inside RoundedSquare::draw() method.");
}
}
Rectangle.java

public class Rectangle implements Shape {


@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Step 3: Create an Abstract class to get factories for Normal and Rounded Shape
Objects.

AbstractFactory.java

public abstract class AbstractFactory {


abstract Shape getShape(String shapeType) ;
}
Step 4: Create Factory classes extending AbstractFactory to generate object of concrete
class based on given information.

ShapeFactory.java

public class ShapeFactory extends AbstractFactory {


@Override
public Shape getShape(String shapeType){
if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
}else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
RoundedShapeFactory.java
public class RoundedShapeFactory extends AbstractFactory {
@Override
public Shape getShape(String shapeType){
if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new RoundedRectangle();
}else if(shapeType.equalsIgnoreCase("SQUARE")){
return new RoundedSquare();
}
return null;
}
}
Step 5: Create a Factory generator/producer class to get factories by passing an
information such as Shape

FactoryProducer.java

public class FactoryProducer {


public static AbstractFactory getFactory(boolean rounded){
if(rounded){
return new RoundedShapeFactory();
}else{
return new ShapeFactory();
}
}
}
Step 6: Use the FactoryProducer to get AbstractFactory in order to get factories of
concrete classes by passing information such as type.

AbstractFactoryPatte rnDemo.java

public class AbstractFactoryPatternDemo {


public static void main(String[] args) {
//get rounded shape factory
AbstractFactory shapeFactory = FactoryProducer.getFactory(false);
//get an object of Shape Rounded Rectangle
Shape shape1 = shapeFactory.getShape("RECTANGLE");
//call draw method of Shape Rectangle
shape1.draw();
//get an object of Shape Rounded Square
Shape shape2 = shapeFactory.getShape("SQUARE");
//call draw method of Shape Square
shape2.draw();
//get rounded shape factory
AbstractFactory shapeFactory1 = FactoryProducer.getFactory(true);
//get an object of Shape Rectangle
Shape shape3 = shapeFactory1.getShape("RECTANGLE");
//call draw method of Shape Rectangle
shape3.draw();
//get an object of Shape Square
Shape shape4 = shapeFactory1.getShape("SQUARE");
//call draw method of Shape Square
shape4.draw();

}
}
Step 7: Verify the output.

Inside Rectangle::draw() method.


Inside Square::draw() method.
Inside RoundedRectangle::draw() method.
Inside RoundedSquare::draw() method.

3. Singletone Pattern: Singleton pattern is one of the simplest design patterns in Java.
This type of design pattern comes under creational pattern as this pattern provides one
of the best ways to create an object. This pattern involves a single class which is
responsible to create an object while making sure that only single object gets created.
This class provides a way to access its only object which can be accessed directly
without need to instantiate the object of the class.
Implementation
We're going to create a SingleObject class. SingleObject class have its constructor as
private and have a static instance of itself. SingleObject class provides a static method
to get its static instance to outside world. SingletonPatternDemo, our demo class will
use SingleObject class to get a SingleObject object.
Figure 3.3: Singleton Pattern UML Diagram

Step 1: Create a Singleton Class.

SingleObject.java
public class SingleObject {
public class SingleObject {

//create an object of SingleObject


private static SingleObject instance = new SingleObject();

//make the constructor private so that this class cannot be


//instantiated
private SingleObject(){}

//Get the only object available


public static SingleObject getInstance(){
return instance;
}

public void showMessage(){


System.out.println("Hello World!");
}
}

Step 2: Get the only object from the singleton class.


SingletonPatternDemo.java

public class SingletonPatternDemo {


public static void main(String[] args) {

//illegal construct
//Compile Time Error: The constructor SingleObject() is not visible
//SingleObject object = new SingleObject();

//Get the only object available


SingleObject object = SingleObject.getInstance();

//show the message


object.showMessage();
}
}
Step 3: Verify the output.

Hello World!

3.2.2 Structural Patterns


These design patterns concern class and object composition. Concept of inheritance is used to
compose interfaces and define ways to compose objects to obtain new functionalities.
1. Bridge Pattern

Bridge is used when we need to decouple an abstraction from its implementation so that the
two can vary independently. This type of design pattern comes under structural pattern as this
pattern decouples implementation class and abstract class by providing a bridge structure
between them.

This pattern involves an interface which acts as a bridge which makes the functionality of
concrete classes independent from interface implementer classes. Both types of classes can be
altered structurally without affecting each other.

We are demonstrating use of Bridge pattern via following example in which a circle can be
drawn in different colors using same abstract class method but different bridge impleme nte r
classes.
Implementation

We have a DrawAPI interface which is acting as a bridge implementer and concrete


classes RedCircle, GreenCircle implementing the DrawAPI interface. Shape is an abstract
class and will use object of DrawAPI. BridgePatternDemo, our demo class will
use Shape class to draw different colored circle.

circle.

Figure 3.4: BridgePattern UML Diagram


Step 1: Create bridge implementer interface.
DrawAPI.java
public interface DrawAPI {
public void drawCircle(int radius, int x, int y);
}
Step 2: Create concrete bridge implementer classes implementing the DrawAPI interface.
RedCircle.java
public class RedCircle implements DrawAPI {
@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle[ color: red, radius: " + radius + ", x: " + x + ", " + y +
"]");
}
}
GreenCircle.java

public class GreenCircle implements DrawAPI {


@Override
public void drawCircle(int radius, int x, int y) {
System.out.println("Drawing Circle[ color: green, radius: " + radius + ", x: " + x + ", " + y
+ "]");
}
}

Step 3: Create an abstract class Shape using the DrawAPI interface.

Shape.java

public abstract class Shape {


protected DrawAPI drawAPI;

protected Shape(DrawAPI drawAPI){


this.drawAPI = drawAPI;
}
public abstract void draw();
}

Step 4: Create concrete class implementing the Shape interface.

Circle.java

public class Circle extends Shape {


private int x, y, radius;

public Circle(int x, int y, int radius, DrawAPI drawAPI) {


super(drawAPI);
this.x = x;
this.y = y;
this.radius = radius;
}

Step 5: Use the Shape and DrawAPI classes to draw different colored circles.

BridgePatternDemo.java

public class BridgePatternDemo {


public static void main(String[] args) {
Shape redCircle = new Circle(100,100, 10, new RedCircle());
Shape greenCircle = new Circle(100,100, 10, new GreenCircle());

redCircle.draw();
greenCircle.draw();
}
}

Step 6: Verify the output.

Drawing Circle[ color: red, radius: 10, x: 100, 100]


Drawing Circle[ color: green, radius: 10, x: 100, 100]

2. Flyweight Pattern
Flyweight pattern is primarily used to reduce the number of objects created and to decrease
memory footprint and increase performance. This type of design pattern comes under
structural pattern as this pattern provides ways to decrease object count thus improving the
object structure of application.

Flyweight pattern tries to reuse already existing similar kind objects by storing them and
creates new object when no matching object is found. We will demonstrate this pattern by
drawing 20 circles of different locations but we will create only 5 objects. Only 5 colors
are available so color property is used to check already existing Circle objects.

Implementation
We are going to create a Shape interface and concrete class Circleimplementing
the Shape interface. A factory class ShapeFactory is defined as a next step.

ShapeFactory has a HashMap of Circle having key as color of the Circle object. Whenever a
request comes to create a circle of particular color to ShapeFactory, it checks the circle object
in its HashMap, if object of Circlefound, that object is returned otherwise a new object is
created, stored in hashmap for future use, and returned to client.

FlyWeightPatternDemo, our demo class, will use ShapeFactory to get a Shapeobject. It will
pass information (red / green / blue/ black / white) to ShapeFactory to get the circle of desired
color it needs.

Figure: 3.4 FlyWeight Pattern UML Diagram

Step 1: Create an interface.

Shape.java
public interface Shape {
void draw();
}

Step 2: Create concrete class implementing the same interface.

Circle.java

public class Circle implements Shape {


private String color;
private int x;
private int y;
private int radius;

public Circle(String color){


this.color = color;
}

public void setX(int x) {


this.x = x;
}

public void setY(int y) {


this.y = y;
}

public void setRadius(int radius) {


this.radius = radius;
}

@Override
public void draw() {
System.out.println("Circle: Draw() [Color : " + color + ", x : " + x + ", y :" + y + ",
radius :" + radius);
}
}

Step 3: Create a factory to generate object of concrete class based on given informatio n.

ShapeFactory.java

import java.util.HashMap;

public class ShapeFactory {

// Uncomment the compiler directive line and


// javac *.java will compile properly.
// @SuppressWarnings("unchecked")
private static final HashMap circleMap = new HashMap();

public static Shape getCircle(String color) {


Circle circle = (Circle)circleMap.get(color);

if(circle == null) {
circle = new Circle(color);
circleMap.put(color, circle);
System.out.println("Creating circle of color : " + color);
}
return circle;
}
}

Step 4: Use the factory to get object of concrete class by passing an information such
as color.

FlyweightPatternDemo.java

public class FlyweightPatternDemo {


private static final String colors[] = { "Red", "Green", "Blue", "White", "Black" };
public static void main(String[] args) {

for(int i=0; i < 20; ++i) {


Circle circle = (Circle)ShapeFactory.getCircle(getRandomColor());
circle.setX(getRandomX());
circle.setY(getRandomY());
circle.setRadius(100);
circle.draw();
}
}
private static String getRandomColor() {
return colors[(int)(Math.random()*colors.length)];
}
private static int getRandomX() {
return (int)(Math.random()*100 );
}
private static int getRandomY() {
return (int)(Math.random()*100);
}
}

Step 5: Verify the output.


Creating circle of color : Black
Circle: Draw() [Color : Black, x : 36, y :71, radius :100
Creating circle of color : Green
Circle: Draw() [Color : Green, x : 27, y :27, radius :100
Creating circle of color : White
Circle: Draw() [Color : White, x : 64, y :10, radius :100
Creating circle of color : Red
Circle: Draw() [Color : Red, x : 15, y :44, radius :100
Circle: Draw() [Color : Green, x : 19, y :10, radius :100
Circle: Draw() [Color : Green, x : 94, y :32, radius :100
Circle: Draw() [Color : White, x : 69, y :98, radius :100
Creating circle of color : Blue
Circle: Draw() [Color : Blue, x : 13, y :4, radius :100
Circle: Draw() [Color : Green, x : 21, y :21, radius :100
Circle: Draw() [Color : Blue, x : 55, y :86, radius :100
Circle: Draw() [Color : White, x : 90, y :70, radius :100
Circle: Draw() [Color : Green, x : 78, y :3, radius :100
Circle: Draw() [Color : Green, x : 64, y :89, radius :100
Circle: Draw() [Color : Blue, x : 3, y :91, radius :100
Circle: Draw() [Color : Blue, x : 62, y :82, radius :100
Circle: Draw() [Color : Green, x : 97, y :61, radius :100
Circle: Draw() [Color : Green, x : 86, y :12, radius :100
Circle: Draw() [Color : Green, x : 38, y :93, radius :100
Circle: Draw() [Color : Red, x : 76, y :82, radius :100
Circle: Draw() [Color : Blue, x : 95, y :82, radius :100

3.2.3 Behavioral Patterns


These design patterns are specifically concerned with communication between objects.
1. The iterator pattern

Iterator pattern is very commonly used design pattern in Java and .Net programming
environment. This pattern is used to get a way to access the elements of a collection object in
sequential manner without any need to know its underlying representation. Iterator pattern falls
under behavioural pattern category.
Implementation
We're going to create an Iterator interface which narrates navigation method and a Container
interface which returns the iterator. Concrete classes implementing the Container interface will
be responsible to implement Iterator interface and use it IteratorPatternDemo, our demo class
will use NamesRepository, a concrete class implementation to print a Names stored as a
collection in NamesRepository.
Figure 3.5: Iterator Pattern UML Diagram
Step 1: Create interfaces.

Iterator.java

public interface Iterator {


public boolean hasNext();
public Object next();
}
Container.java

public interface Container {


public Iterator getIterator();
}

Step 2: Create concrete class implementing the Container interface. This class has inner
class NameIterator implementing the Iterator interface.

NameRepository.java

public class NameRepository implements Container {


public String names[] = {"Robert" , "John" ,"Julie" , "Lora"};

@Override
public Iterator getIterator() {
return new NameIterator();
}

private class NameIterator implements Iterator {

int index;
@Override
public boolean hasNext() {

if(index < names.length){


return true;
}
return false;
}

@Override
public Object next() {

if(this.hasNext()){
return names[index++];
}
return null;
}
}
}

Step 3: Use the NameRepository to get iterator and print names.

IteratorPatternDemo.java

public class IteratorPatternDemo {

public static void main(String[] args) {


NameRepository namesRepository = new NameRepository();

for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){


String name = (String)iter.next();
System.out.println("Name : " + name);
}
}
}
Step 4: Verify the output.

Name : Robert
Name : John
Name : Julie
Name : Lora

2. Observer pattern
Observer pattern is used when there is one-to-many relationship between objects such as if one
object is modified, its dependent objects are to be notified automatically. Observer pattern falls
under behavioural pattern category.
Implementation
Observer pattern uses three actor classes. Subject, Observer and Client. Subject is an object
having methods to attach and detach observers to a client object. We have created an abstract
class Observer and a concrete class Subject that is extending class Observer.
ObserverPatternDemo, our demo class, will use Subject and concrete class object to show
observer pattern in action.

Figure 3.6: Observer Pattern UML Diagram


Step 1: Create Subject class.
Subject.java

import java.util.ArrayList;
import java.util.List;

public class Subject {

private List<Observer> observers = new ArrayList<Observer>();


private int state;
public int getState() {
return state;
}

public void setState(int state) {


this.state = state;
notifyAllObservers();
}

public void attach(Observer observer){


observers.add(observer);
}

public void notifyAllObservers(){


for (Observer observer : observers) {
observer.update();
}
}
}

Step 2: Create Observer class.

Observer.java

public abstract class Observer {


protected Subject subject;
public abstract void update();
}

Step 3: Create concrete observer classes

BinaryObserver.java

public class BinaryObserver extends Observer{

public BinaryObserver(Subject subject){


this.subject = subject;
this.subject.attach(this);
}

@Override
public void update() {
System.out.println( "Binary String: " + Integer.toBinaryString( subject.getState() ) );
}
}
OctalObserver.java

public class OctalObserver extends Observer{

public OctalObserver(Subject subject){


this.subject = subject;
this.subject.attach(this);
}

@Override
public void update() {
System.out.println( "Octal String: " + Integer.toOctalString( subject.getState() ) );
}
}
HexaObserver.java

public class HexaObserver extends Observer{

public HexaObserver(Subject subject){


this.subject = subject;
this.subject.attach(this);
}

@Override
public void update() {
System.out.println( "Hex String: " + Integer.toHexString( subject.getState ()
).toUpperCase() );
}
}

Step 4: Use Subject and concrete observer objects.

ObserverPatternDemo.java

public class ObserverPatternDemo {


public static void main(String[] args) {
Subject subject = new Subject();

new HexaObserver(subject);
new OctalObserver(subject);
new BinaryObserver(subject);

System.out.println("First state change: 15");


subject.setState(15);
System.out.println("Second state change: 10");
subject.setState(10);
}
}

Step 5: Verify the output.

First state change: 15


Hex String: F
Octal String: 17
Binary String: 1111
Second state change: 10
Hex String: A
Octal String: 12
Binary String: 1010
3. Model-View-Controlle r Pattern (MVC). This pattern is used to separate applicatio n's
concerns.
 Model - Model represents an object or JAVA POJO carrying data. It can also have
logic to update controller if its data changes.

 View - View represents the visualization of the data that model contains.

 Controller - Controller acts on both model and view. It controls the data flow into
model object and updates the view whenever data changes. It keeps view and model
separate.
Implementation

We are going to create a Student object acting as a model.StudentView will be a view class
which can print student details on console and StudentControlleris the controller class
responsible to store data in Student object and update view StudentView accordingly.

MVCPatternDemo, our demo class, will use StudentController to demonstrate use of MVC
pattern.

Figure 3.7: StudentController as MVC Controller UML Diagram

Step 1: Create Model.

Student.java

public class Student {


private String rollNo;
private String name;

public String getRollNo() {


return rollNo;
}

public void setRollNo(String rollNo) {


this.rollNo = rollNo;
}

public String getName() {


return name;
}

public void setName(String name) {


this.name = name;
}
}

Step 2: Create View.

StudentView.java

public class StudentView {


public void printStudentDetails(String studentName, String studentRollNo){
System.out.println("Student: ");
System.out.println("Name: " + studentName);
System.out.println("Roll No: " + studentRollNo);
}
}

Step 3: Create Controller.

StudentController.java

public class StudentController {


private Student model;
private StudentView view;

public StudentController(Student model, StudentView view){


this.model = model;
this.view = view;
}

public void setStudentName(String name){


model.setName(name);
}

public String getStudentName(){


return model.getName();
}

public void setStudentRollNo(String rollNo){


model.setRollNo(rollNo);
}

public String getStudentRollNo(){


return model.getRollNo();
}

public void updateView(){


view.printStudentDetails(model.getName(), model.getRollNo());
}
}

Step 4: Use the StudentController methods to demonstrate MVC design pattern usage.

MVCPatternDemo.java

public class MVCPatternDemo {


public static void main(String[] args) {

//fetch student record based on his roll no from the database


Student model = retriveStudentFromDatabase();

//Create a view : to write student details on console


StudentView view = new StudentView();

StudentController controller = new StudentController(model, view);

controller.updateView();

//update model data


controller.setStudentName("John");

controller.updateView();
}

private static Student retriveStudentFromDatabase(){


Student student = new Student();
student.setName("Robert");
student.setRollNo("10");
return student;
}
}

Step 5: Verify the output.

Student:
Name: Robert
Roll No: 10
Student:
Name: John
Roll No: 10
MODULE 4:

GENERIC TYPES AND COLLECTIONS

4.1 Introduction
Generics and collections work well with a number of other new features introduced in the latest
versions of Java, including boxing and unboxing, a new form of loop, and functions that accept
a variable number of arguments. We begin with an example that illustrates all of these. As we
shall see, combining them is synergistic: the whole is greater than the sum of its parts.
Taking that as our motto, let’s do something simple with sums: put three numbers into a list
and add them together. Here is how to do it in Java with generics:
List<Integer> ints = Arrays.asList(1,2,3);
int s = 0;
for (int n : ints) { s += n; }
assert s == 6;

You can probably read this code without much explanation, but let’s touch on the key features.
The interface List and the class Arrays are part of the Collections Framework (both are found
in the package java.util). The type List is now generic; you write List<E> to indicate a list with
elements of type E. Here we write List<Integer> to indi-cate that the elements of the list belong
to the class Integer, the wrapper class that corresponds to the primitive type int. Boxing and
unboxing operations, used to convert from the primitive type to the wrapper class, are
automatically inserted. The static method asList takes any number of arguments, places them
into an array, and returns a new list backed by the array. The new loop form, foreach, is used
to bind a variable successively to each element of the list, and the loop body adds these into
the sum. The assertion statement (introduced in Java 1.4), is used to check that the sum is
correct; when assertions are enabled; it throws an error if the condition does not evaluate to
true.

Here is how the same code looks in Java before generics:

List ints = Arrays.asList( new Integer[] {


new Integer(1), new Integer(2), new Integer(3)
} );
int s = 0;
for (Iterator it = ints.iterator(); it.hasNext(); ) {
int n = ((Integer)it.next()).intValue();

Reading this code is not quite so easy. Without generics, there is no way to indicate in the type
declaration what kind of elements you intend to store in the list, so instead of writing
List<Integer>, you write List. Now it is the coder rather than the compiler who is responsible
for remembering the type of the list elements, so you must write the cast to (Integer) when
extracting elements from the list. Without boxing and unboxing, you must explicitly allocate
each object belonging to the wrapper class Integer and use the intValue method to extract the
corresponding primitive int. Without functions that accept a variable number of arguments, you
must explicitly allocate an array to pass to the asList method. Without the new form of loop,
you must explicitly declare
an iterator and advance it through the list. By the way, here is how to do the same thing with
an array in Java before generics:

int s = 0;
for (int i = 0; i < ints.length; i++) { s += ints[i]; }
assert s == 6;

This is slightly longer than the corresponding code that uses generics and collections, is
arguably a bit less readable, and is certainly less flexible. Collections let you easily grow or
shrink the size of the collection, or switch to a different representation when appropriate, such
as a linked list or hash table or ordered tree. The introduction of generics, boxing and unboxing,
foreach loops, and varargs in Java marks the first time that using collections is just as simple,
perhaps even simpler, than using arrays

4.2 Generics
An interface or class may be declared to take one or more type parameters, which are written
in angle brackets and should be supplied when you declare a variable belonging to the interface
or class or when you create a new instance of a class.
We saw one example in the previous section. Here is another:
List<String> words = new ArrayList<String>(); words.add("Hello ");
words.add("world!");
String s = words.get(0)+words.get(1); assert s.equals("Hello world!");
In the Collections Framework, class ArrayList<E> implements interface List<E>. This trivia l
code fragment declares the variable words to contain a list of strings, creates an instance of an
ArrayList, adds two strings to the list, and gets them out again.
In Java before generics, the same code would be written as follows:
List words = new ArrayList(); words.add("Hello ");
words.add("world!");
String s = ((String)words.get(0))+((String)words.get(1)) assert s.equals("Hello world!");
Without generics, the type parameters are omitted, but you must explicitly cast when- ever an
element is extracted from the list.
In fact, the bytecode compiled from the two sources above will be identical. We say that
generics are implemented by erasure because the types List<Integer>, List<String>, and
List<List<String>> are all represented at run-time by the same type, List. We also use erasure
to describe the process that converts the first program to the second. The term erasure is a slight
misnomer, since the process erases type parameters but adds casts.
Generics implicitly perform the same cast that is explicitly performed without generics. If such
casts could fail, it might be hard to debug code written with generics. This is why it is reassuring
that generics come with the following guarantee:
Cast-iron guarantee: the implicit casts added by the compilation of generics never fail.
There is also some fine print on this guarantee: it applies only when no unchecked warnings
have been issued by the compiler. Later, we will discuss at some length what causes unchecked
warnings to be issued and how to minimize their effect.
Implementing generics by erasure has a number of important effects. It keeps things simple, in
that generics do not add anything fundamentally new. It keeps things small,in that there is
exactly one implementation of List, not one version for each type. And it eases evolution, since
the same library can be accessed in both non generic and generic forms.
Another consequence of implementing generics by erasure is that array types differ in key ways
from parameterized types. Executing
new String[size] allocates an array, and stores in that array an indication that its components
are of type String. In contrast, executing: new ArrayList<String>() allocates a list, but does not
store in the list any indication of the type of its elements.
4.2.1 Generics versus Templates
Generics in Java resemble templates in C++. There are just two important things to bear in
mind about the relationship between Java generics and C++ templates: syntax and semantics.
The syntax is deliberately similar and the semantics are deliberately different.
Syntactically, angle brackets were chosen because they are familiar to C++ users, and because
square brackets would be hard to parse. However, there is one difference in syntax. In C++,
nested parameters require extra spaces, so you see things like this:List< List<String> In Java,
no spaces are required, and it’s fine to write this:List<List<String>>
You may use extra spaces if you prefer, but they’re not required. (In C++, a problem arises
because >> without the space denotes the right-shift operator. Java fixes the problem by a trick
in the grammar.)
Semantically, Java generics are defined by erasure, whereas C++ templates are defined by
expansion. In C++ templates, each instance of a template at a new type is compiled separately.
If you use a list of integers, a list of strings, and a list of lists of string, there will be three
versions of the code. If you use lists of a hundred different types, there will be a hundred
versions of the code—a problem known as code bloat. In Java, no matter how many types of
lists you use, there is always one version of the code, so bloat does not occur.
Expansion may lead to more efficient implementation than erasure, since it offers more
opportunities for optimization, particularly for primitive types such as int. For code that is
manipulating large amounts of data—for instance, large arrays in scientific computing—this
difference may be significant. However, in practice, for most purposes the difference in
efficiency is not important, whereas the problems caused by code bloat can be crucial. In C++,
you also may instantiate a template with a constant value rather than a type, making it possible
to use templates as a sort of “macroprocessor on steroids” that can perform arbitrarily complex
computations at compile time. Java generics are deliber- ately restricted to types, to keep them
simple and easy to understand.
4.3 Generics and Subtyping
Subtyping is a key feature of object-oriented languages such as Java. In Java, one type is a
subtype of another if they are related by an extends or implements clause. Here are some
examples:
Integer is a subtype of Number
Double is a subtype of Number
ArrayList<E> is a subtype of List<E>
List<E> is a subtype of Collection<E>
Collection<E> is a subtype of Iterable<E>

Subtyping is transitive, meaning that if one type is a subtype of a second, and the second is a
subtype of a third, then the first is a subtype of the third. So, from the last two lines in the
preceding list, it follows that List<E> is a subtype of Iterable<E>. If one type is a subtype of
another, we also say that the second is a supertype of the first. Every reference type is a subtype
of Object, and Object is a supertype of every reference type. We also say, trivially, that every
type is a subtype of itself.
The Substitution Principle tells us that wherever a value of one type is expected, one may
provide a value of any subtype of that type:
Substitution Principle: a variable of a given type may be assigned a value of any subtype of
that type, and a method with a parameter of a given type may be invoked with an argument of
any subtype of that type.
Consider the interface Collection<E>. One of its methods is add, which takes a param- eter of
type E:
interface Collection<E> { public boolean add(E elt);
...
}
According to the Substitution Principle, if we have a collection of numbers, we may add an
integer or a double to it, because Integer and Double are subtypes of Number.
List<Number> nums = new ArrayList<Number>(); nums.add(2);
nums.add(3.14);
assert nums.toString().equals("[2, 3.14]");

Here, subtyping is used in two ways for each method call. The first call is permitted because
nums has type List<Number>, which is a subtype of Collection<Number>, and 2 has type
Integer (thanks to boxing), which is a subtype of Number. The second call is simila r ly
permitted. In both calls, the E in List<E> is taken to be Number.
It may seem reasonable to expect that since Integer is a subtype of Number, it follows that
List<Integer> is a subtype of List<Number>. But this is not the case, because the
Substitution Principle would rapidly get us into trouble. It is not always safe to assign a value
of type List<Integer> to a variable of type List<Number>. Consider the following code
fragment:
List<Integer> ints = new ArrayList<Integer>(); ints.add(1);
ints.add(2);
List<Number> nums = ints; // compile-time error
nums.add(3.14);
assert ints.toString().equals("[1, 2, 3.14]");

This code assigns variable ints to point at a list of integers, and then assigns nums to point at
the same list of integers; hence the call in the fifth line adds a double to this
list, as shown in the last line. This must not be allowed! The problem is prevented by
observing that here the Substitution Principle does not apply: the assignment on the fourth line
is not allowed because List<Integer> is not a subtype of List<Number>, and the compiler
reports that the fourth line is in error.
What about the reverse? Can we take List<Number> to be a subtype of List<Integer>? No, that
doesn’t work either, as shown by the following code:
List<Number> nums = new ArrayList<Number>(); nums.add(2.78);
nums.add(3.14);
List<Integer> ints = nums; // compile-time error
assert ints.toString().equals("[2.78, 3.14]");

The problem is prevented by observing that here the Substitution Principle does not apply: the
assignment on the fourth line is not allowed because List<Number> is not a subtype of
List<Integer>, and the compiler reports that the fourth line is in error.
So List<Integer> is not a subtype of List<Number>, nor is List<Number> a subtype of
List<Integer>; all we have is the trivial case, where List<Integer> is a subtype of itself, and we
also have that List<Integer> is a subtype of Collection<Integer>.
Arrays behave quite differently; with them, Integer is a subtype of Number. We will compare
the treatment of lists and arrays later . Sometimes we would like lists to behave more like
arrays, in that we want to accept not only a list with elements of a given type, but also a list
with elements of any subtype of a given type. For this purpose, we use wildcards.

4.4 Wildcards with extends


Another method in the Collection interface is addAll, which adds all of the members of one
collection to another collection:
interface Collection<E> {
...
public boolean addAll(Collection<? extends E> c);
...
}
Clearly, given a collection of elements of type E, it is OK to add all members of another
collection with elements of type E. The quizzical phrase "? extends E" means that it is also OK
to add all members of a collection with elements of any type that is a subtype
of E. The question mark is called a wildcard, since it stands for some type that is a subtype of
E.
Here is an example. We create an empty list of numbers, and add to it first a list of integers and
then a list of doubles:

List<Number> nums = new ArrayList<Number>();


List<Integer> ints = Arrays.asList(1, 2);
List<Double> dbls = Arrays.asList(2.78, 3.14); nums.addAll(ints);
nums.addAll(dbls);
assert nums.toString().equals("[1, 2, 2.78, 3.14]");

The first call is permitted because nums has type List<Number>, which is a subtype of
Collection<Number>, and ints has type List<Integer>, which is a subtype of Collec tion<?
extends Number>. The second call is similarly permitted. In both calls, E is taken to be
Number. If the method signature for addAll had been written without the wildcard, then the
calls to add lists of integers and doubles to a list of numbers would not have been permitted;
you would only have been able to add a list that was explicitly declared to be a list of numbers.

We can also use wildcards when declaring variables. Here is a variant of the example at the
end of the preceding section, changed by adding a wildcard to the second line:

List<Integer> ints = new ArrayList<Integer>();


ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]");

Before, the fourth line caused a compile-time error (because List<Integer> is not a subtype of
List<Number>), but the fifth line was fine (because a double is a number, so you can add a
double to a List<Number>). Now, the fourth line is fine (because List<Integer> is a subtype of
List<? extends Number>), but the fifth line causes a com- pile-time error (because you cannot
add a double to a List<? extends Number>, since it might be a list of some other subtype of
number). As before, the last line shows why one of the preceding lines is illegal!
In general, if a structure contains elements with a type of the form ? extends E, we can get
elements out of the structure, but we cannot put elements into the structure. To put elements
into the structure we need another kind of wildcard, as explained in the next section.

4.5 Wildcards with super


Here is a method that copies into a destination list all of the elements from a source list, from
the convenience class Collections:

public static <T> void copy(List<? super T> dst, List<? extends T> src)
{
for (int i = 0; i < src.size(); i++) {
dst.set(i, src.get(i));
}
}
The quizzical phrase ? super T means that the destination list may have elements of any type
that is a supertype of T, just as the source list may have elements of any type that is a subtype
of T.
Here is a sample call.

List<Object> objs = Arrays.<Object>asList(2, 3.14, "four"); List<Integer> ints =


Arrays.asList(5, 6); Collections.copy(objs, ints);
assert objs.toString().equals("[5, 6, four]");

As with any generic method, the type parameter may be inferred or may be given ex- plicitly.
In this case, there are four possible choices, all of which type-check and all of which have the
same effect:

Collections.copy(objs, ints);
Collections.<Object>copy(objs, ints);
Collections.<Number>copy(objs, ints);
Collections.<Integer>copy(objs, ints);

The first call leaves the type parameter implicit; it is taken to be Integer, since that is the most
specific choice that works. In the third line, the type parameter T is taken to be Number. The
call is permitted because objs has type List<Object>, which is a subtype of List<? super
Number> (since Object is a supertype of Number, as required by the wildcard) and ints has
type List<Integer>, which is a subtype of List<? extends Num ber> (since Integer is a subtype
of Number, as required by the extends wildcard).
We could also declare the method with several possible signatures.

public static <T> void copy(List<T> dst, List<T> src)


public static <T> void copy(List<T> dst, List<? extends T> src) public static <T> void
copy(List<? super T> dst, List<T> src)
public static <T> void copy(List<? super T> dst, List<? extends T> src)

The first of these is too restrictive, as it only permits calls when the destination and source have
exactly the same type. The remaining three are equivalent for calls that use implicit type
parameters, but differ for explicit type parameters. For the example calls above, the second
signature works only when the type parameter is Object, the third signature works only when
the type parameter is Integer, and the last signature works (as we have seen) for all three type
parameters—i.e., Object, Number, and Integer. Al- ways use wildcards where you can in a
signature, since this permits the widest range of calls.

4.5.1 Wildcards versus Type Parameters

The contains method checks whether a collection contains a given object, and its gen-
eralization, containsAll, checks whether a collection contains every element of another
collection. This section presents two alternate approaches to giving generic signatures
for these methods. The first approach uses wildcards and is the one used in the Java Collectio ns
Framework. The second approach uses type parameters and is often a more appropriate
alternative.
Wildcards Here are the types that the methods have in Java with generics:

interface Collection<E> {
...
public boolean contains(Object o);
public boolean containsAll(Collection<?> c);
...
}
The first method does not use generics at all! The second method is our first sight of an
important abbreviation. The type Collection<?> stands for:
Collection<? extends Object>

Extending Object is one of the most common uses of wildcards, so it makes sense to provide a
short form for writing it.
These methods let us test for membership and containment:

Object obj = "one";


List<Object> objs = Arrays.<Object>asList("one", 2, 3.14, 4);
List<Integer> ints = Arrays.asList(2, 4);
assert objs.contains(obj);
assert objs.containsAll(ints);
assert !ints.contains(obj);
assert !ints.containsAll(objs);

The given list of objects contains both the string "one" and the given list of integers, but the
given list of integers does not contain the string "one", nor does it contain the given list of
objects.
The tests ints.contains(obj) and ints.containsAll(objs) might seem silly. Of course, a list of
integers won’t contain an arbitrary object, such as the string "one". But it is permitted because
sometimes such tests might succeed:

Object obj = 1;
List<Object> objs = Arrays.<Object>asList(1, 3);
List<Integer> ints = Arrays.asList(1, 2, 3, 4);
assert ints.contains(obj);
assert ints.containsAll(objs);

In this case, the object may be contained in the list of integers because it happens to be an
integer, and the list of objects may be contained within the list of integers because every object
in the list happens to be an integer. Type Parameters You might reasonably choose an
alternative design for collections a design in which you can only test containment for subtypes
of the element type:
interface MyCollection<E> { // alternative design
...
public boolean contains(E o);
public boolean containsAll(Collection<? extends E> c);
...
}
Say we have a class MyList that implements MyCollection. Now the tests are legal only one
way around:
Object obj = "one";
MyList<Object> objs = MyList.<Object>asList("one", 2, 3.14, 4);
MyList<Integer> ints = MyList.asList(2, 4);
assert objs.contains(obj); assert objs.containsAll(ints)
assert !ints.contains(obj); // compile-time error
assert !ints.containsAll(objs); // compile-time error

The last two tests are illegal, because the type declarations require that we can only test whether
a list contains an element of a subtype of that list. So we can check whether a list of objects
contains a list of integers, but not the other way around.
Which of the two styles is better is a matter of taste. The first permits more tests, and the second
catches more errors at compile time (while also ruling out some sensible tests). The designers
of the Java libraries chose the first, more liberal, alternative, be- cause someone using the
Collections Framework before generics might well have written a test such as ints
containsAll(objs), and that person would want that test to remain valid after generics were
added to Java. However, when designing a new generic library, such as MyCollection, when
backward compatibility is less important, the de- sign that catches more errors at compile time
might make more sense.
Arguably, the library designers made the wrong choice. Only rarely will a test such as
ints.containsAll(objs) be required, and such a test can still be permitted by declaring ints to
have type List<Object> rather than type List<Integer>. It might have been better to catch more
errors in the common case rather than to permit more-precise typing in an uncommon case.

The same design choice applies to other methods that contain Object or Collection<? > in their
signature, such as remove, removeAll, and retainAll.

4.5.2 Wildcard Capture


When a generic method is invoked, the type parameter may be chosen to match the unknown
type represented by a wildcard. This is called wildcard capture. Consider the method reverse
in the convenience class java.util.Collections, which accepts a list of any type and reverses it.
It can be given either of the following twosignatures, which are equivalent: public static void
reverse(List<?> list); public static void <T> reverse(List<T> list);

The wildcard signature is slightly shorter and clearer, and is the one used in the library. If you
use the second signature, it is easy to implement the method:

public static void <T> reverse(List<T> list) {


List<T> tmp = new ArrayList<T>(list);
for (int i = 0; i < list.size(); i++) { list.set(i, tmp.get(list.size()- i-1));
}
}
This copies the argument into a temporary list, and then writes from the copy back into the
original in reverse order.If you try to use the first signature with a similar method body, it won’t
work:

public static void reverse(List<?> list)


{
List<Object> tmp = new ArrayList<Object>(list);
for (int i = 0; i < list.size(); i++)
{
list.set(i, tmp.get(list.size()- i-1)); // compile-time error
}
}
Now it is not legal to write from the copy back into the original, because we are trying to write
from a list of objects into a list of unknown type. Replacing List<Object> with List<?> won’t
fix the problem, because now we have two lists with (possibly different) unknown element
types.
Instead, you can implement the method with the first signature by implementing a private
method with the second signature, and calling the second from the first:
public static void reverse(List<?> list)
{ rev(list); }
private static <T> void rev(List<T> list)
{
List<T> tmp = new ArrayList<T>(list);
for (int i = 0; i < list.size(); i++)
{
list.set(i, tmp.get(list.size()- i-1));
}
}
Here we say that the type variable T has captured the wildcard. This is a generally useful
technique when dealing with wildcards, and it is worth knowing.

Another reason to know about wildcard capture is that it can show up in error messages, even
if you don’t use the above technique. In general, each occurrence of a wildcard is taken to stand
for some unknown type. If the compiler prints an error message containing this type, it is
referred to as capture of ?. For instance, with Sun’s current compiler, the incorrect version of
reverse generates the following error message:
Capture.java:6: set(int,capture of ?) in java.util.List<capture of ?> cannot be applied to
(int,java.lang.Object)
list.set(i, tmp.get(list.size()- i-1));
^
Hence, if you see the quizzical phrase capture of ? in an error message, it will come from a
wildcard type. Even if there are two distinct wildcards, the compiler will print the type
associated with each as capture of ?. Bounded wildcards generate names that are even more
long-winded, such as capture of ? extends Number.

4.5.3 Restrictions on Wildcards


Wildcards may not appear at the top level in class instance creation expressions (new), in
explicit type parameters in generic method calls, or in supertypes (extends and implements).
Instance Creation In a class instance creation expression, if the type is a parameterized type,
then none of the type parameters may be wildcards. For example, the following are illegal:

List<?> list = new ArrayList<?>(); // compile-time error


Map<String, ? extends Number> map
= new HashMap<String, ? extends Number>(); // compile-time error

This is usually not a hardship. The Get and Put Principle tells us that if a structure contains a
wildcard, we should only get values out of it (if it is an extends wildcard) or only put values
into it (if it is a super wildcard). For a structure to be useful, we must do both. Therefore, we
usually create a structure at a precise type, even if we use wildcard types to put values into or
get values from the structure, as in the following example:

List<Number> nums = new ArrayList<Number>();


List<? super Number> sink = nums;
List<? extends Number> source = nums;
for (int i=0; i<10; i++) sink.add(i);
double sum=0;
for (Number num : source)
sum+=num.doubleValue();

Here wildcards appear in the second and third lines, but not in the first line that creates the
list.Only top-level parameters in instance creation are prohibited from containing wild- cards.
Nested wildcards are permitted. Hence, the following is legal:

List<List<?>> lists = new ArrayList<List<?>>();


lists.add(Arrays.asList(1,2,3)); lists.add(Arrays.asList("four","five"));
assert lists.toString().equals("[[1, 2, 3], [four, five]]");

Even though the list of lists is created at a wildcard type, each individual list within it has a
specific type: the first is a list of integers and the second is a list of strings. The wildcard type
prohibits us from extracting elements from the inner lists as any type other than Object, but
since that is the type used by toString, this code is well typed.

One way to remember the restriction is that the relationship between wildcards and ordinary
types is similar to the relationship between interfaces and classes—wildcards and interfaces
are more general, ordinary types and classes are more specific, and in- stance creation requires
the more specific information. Consider the following three statements:

List<?> list = new ArrayList<Object>(); // ok


List<?> list = new List<Object>() // compile-time error
List<?> list = new ArrayList<?>() // compile-time error

The first is legal; the second is illegal because an instance creation expression requires a class,
not an interface; and the third is illegal because an instance creation expression requires an
ordinary type, not a wildcard.
You might wonder why this restriction is necessary. The Java designers had in mind that every
wildcard type is shorthand for some ordinary type, so they believed that ultimately every object
should be created with an ordinary type. It is not clear whether this restriction is necessary, but
it is unlikely to be a problem. Generic Method Calls If a generic method call includes explic it
type parameters, those type parameters must not be wildcards. For example, say we have the
following generic method:

class Lists {
public static <T> List<T> factory() { return new ArrayList<T>(); }
}

You may choose for the type parameters to be inferred, or you may pass an explicit type
parameter. Both of the following are legal:

List<?> list = Lists.factory();


List<?> list = Lists.<Object>factory();
If an explicit type parameter is passed, it must not be a wildcard:
List<?> list = Lists.<?>factory(); // compile-time error
As before, nested wildcards are permitted:
List<List<?>> = Lists.<List<?>>factory(); //

The motivation for this restriction is similar to the previous one. Again, it is not clear whether
it is necessary, but it is unlikely to be a problem.

4.6 Generic Methods and Varargs


Here is a method that accepts an array of any type and converts it to a list:

class Lists {
public static <T> List<T> toList(T[] arr) { List<T> list = new ArrayList<T>();
for (T elt : arr) list.add(elt); return list;
}
}
The static method toList accepts an array of type T[] and returns a list of type List<T>, and
does so for any type T. This is indicated by writing <T> at the beginning of the method
signature, which declares T as a new type variable. A method which declares a type variable
in this way is called a generic method. The scope of the type variable T is local to the method
itself; it may appear in the method signature and the method body, but not outside the method.
The method may be invoked as follows:

List<Integer> ints = Lists.toList(new Integer[] { 1, 2, 3 });


List<String> words = Lists.toList(new String[] { "hello", "world" });

In the first line, boxing converts 1, 2, 3 from int to Integer. Packing the arguments into an array
is cumbersome. The vararg feature permits a spe- cial, more convenient syntax for the case in
which the last argument of a method is an array. To use this feature, we replace T[] with T…
in the method declaration:

class Lists {
public static <T> List<T> toList(T... arr) { List<T> list = new ArrayList<T>();
for (T elt : arr) list.add(elt); return list;
}
}
Now the method may be invoked as follows:
List<Integer> ints = Lists.toList(1, 2, 3);
List<String> words = Lists.toList("hello", "world");

This is just shorthand for what we wrote above. At run time, the arguments are packed into an
array which is passed to the method, just as previously.
Any number of arguments may precede a last vararg argument. Here is a method that accepts
a list and adds all the additional arguments to the end of the list:

public static <T> void addAll(List<T> list, T... arr) { for (T elt : arr) list.add(elt);
}
Whenever a vararg is declared, one may either pass a list of arguments to be implicitly packed
into an array, or explicitly pass the array directly. Thus, the preceding method may be invoked
as follows:
List<Integer> ints = new ArrayList<Integer>(); Lists.addAll(ints, 1, 2);
Lists.addAll(ints, new Integer[] { 3, 4 }); assert ints.toString().equals("[1, 2, 3, 4]");

We will see later that when we attempt to create an array containing a generic type, we will
always receive an unchecked warning. Since varargs always create an array, they should be
used only when the argument does not have a generic type.
In the preceding examples, the type parameter to the generic method is inferred, but it may also
be given explicitly, as in the following examples:
List<Integer> ints = Lists.<Integer>toList();
List<Object> objs = Lists.<Object>toList(1, "two");

Explicit parameters are usually not required, but they are helpful in the examples given here.
In the first example, without the type parameter there is too little information for the type
inference algorithm used by Sun's compiler to infer the correct type. It infers that the argument
to toList is an empty array of an arbitrary generic type rather than an empty array of integers,
and this triggers the unchecked warning described earlier. (The Eclipse compiler uses a
different inference algorithm, and compiles the same line correctly without the explic it
parameter.) In the second example, without the type parameter there is too much informa tio n
for the type inference algorithm to infer the correct type. You might think that Object is the
only type that an integer and a string have in common, but in fact they also both implement the
interfaces Serializable and Comparable. The type inference algorithm cannot choose which of
these three the correct type is.

In general, the following rule of thumb suffices: in a call to a generic method, if there are one
or more arguments that correspond to a type parameter and they all have the same type then
the type parameter may be inferred; if there are no arguments that correspond to the type
parameter or the arguments belong to different subtypes of the intended type then the type
parameter must be given explicitly.

When a type parameter is passed to a generic method invocation, it appears in angle brackets
to the left, just as in the method declaration. The Java grammar requires that
type parameters may appear only in method invocations that use a dotted form. Even if the
method toList is defined in the same class that invokes the code, we cannot shorten it as follows :
List<Integer> ints = <Integer>toList(); // compile-time error

This is illegal because it will confuse the parser.


Methods Arrays.asList and Collections.addAll in the Collections Framework are similar to
toList and addAll shown earlier. (Both classes are in package java.util.) The Collectio ns
Framework version of asList does not return an ArrayList, but instead returns a specialized list
class that is backed by a given array. Also, its version of addAll acts on general collections, not
just lists.

4.7 Set Interface


A Set is a Collection that cannot contain duplicate elements. It models the mathematical set
abstraction. The Set interface contains only methods inherited from Collection and adds the
restriction that duplicate elements are prohibited. Set also adds a stronger contract on the
behavior of the equals and hashCode operations, allowing Set instances to be compared
meaningfully even if their implementation types differ. Two Set instances are equal if they
contain the same elements.

The Java platform contains three general-purpose Set implementations: HashSet, TreeSet,
and LinkedHashSet. HashSet, which stores its elements in a hash table, is the best-performing
implementation; however it makes no guarantees concerning the order of iteration. TreeSet,
which stores its elements in a red-black tree, orders its elements based on their values; it is
substantially slower than HashSet. LinkedHashSet, which is implemented as a hash table with
a linked list running through it, orders its elements based on the order in which they were
inserted into the set (insertion-order). LinkedHashSet spares its clients from the unspecified,
generally chaotic ordering provided by HashSet at a cost that is only slightly higher.

Here's a simple but useful Set idiom. Suppose you have a Collection, c, and you want to create
another Collection containing the same elements but with all duplicates eliminated. The
following one-liner does the trick.

Collection<Type> noDups = new HashSet<Type>(c);


It works by creating a Set (which, by definition, cannot contain duplicates), initially containing
all the elements in c. It uses the standard conversion constructor described in the The Collectio n
Interfacesection.

Or, if using JDK 8 or later, you could easily collect into a Set using aggregate operations:

c.stream()
.collect(Collectors.toSet()); // no duplicates
Here's a slightly longer example that accumulates a Collection of names into a TreeSet:

Set<String> set = people.stream()


.map(Person::getName)
.collect(Collectors.toCollection(TreeSet::new));
And the following is a minor variant of the first idiom that preserves the order of the origina l
collection while removing duplicate elements:

Collection<Type> noDups = new LinkedHashSet<Type>(c);


The following is a generic method that encapsulates the preceding idiom, returning a Set of the
same generic type as the one passed.

public static <E> Set<E> removeDups(Collection<E> c) {


return new LinkedHashSet<E>(c);
}

4.7.1 Set Interface Basic Operations


The size operation returns the number of elements in the Set (its cardinality).
The isEmpty method does exactly what you think it would. The add method adds the specified
element to the Set if it is not already present and returns a boolean indicating whether the
element was added. Similarly, the remove method removes the specified element from
the Set if it is present and returns a boolean indicating whether the element was present.
The iterator method returns an Iterator over the Set.
The following program prints out all distinct words in its argument list. Two versions of this
program are provided. The first uses JDK 8 aggregate operations. The second uses the for-each
construct.

Using JDK 8 Aggregate Operations:

import java.util.*;
import java.util.stream.*;

public class FindDups {


public static void main(String[] args) {
Set<String> distinctWords = Arrays.asList(args).stream()
.collect(Collectors.toSet());
System.out.println(distinctWords.size()+
" distinct words: " +
distinctWords);
}
}
Using the for-each Construct:

import java.util.*;

public class FindDups {


public static void main(String[] args) {
Set<String> s = new HashSet<String>();
for (String a : args)
s.add(a);
System.out.println(s.size() + " distinct words: " + s);
}
}

Now run either version of the program.

java FindDups i came i saw i left


The following output is produced:

4 distinct words: [left, came, saw, i]

Note that the code always refers to the Collection by its interface type (Set) rather than by its
implementation type. This is a strongly recommended programming practice because it gives
you the flexibility to change implementations merely by changing the constructor. If either of
the variables used to store a collection or the parameters used to pass it around are declared to
be of the Collection's implementation type rather than its interface type, all such variables and
parameters must be changed in order to change its implementation type.

Furthermore, there's no guarantee that the resulting program will work. If the program uses any
nonstandard operations present in the original implementation type but not in the new one, the
program will fail. Referring to collections only by their interface prevents you from using any
nonstandard operations.
The implementation type of the Set in the preceding example is HashSet, which makes no
guarantees as to the order of the elements in the Set. If you want the program to print the word
list in alphabetical order, merely change the Set's implementation type
from HashSet to TreeSet. Making this trivial one-line change causes the command line in the
previous example to generate the following output.

java FindDups i came i saw i left

4 distinct words: [came, i, left, saw]

4.7.2 Set Interface Bulk Operations


Bulk operations are particularly well suited to Sets; when applied, they perform standard set-
algebraic operations. Suppose s1 and s2 are sets. Here's what bulk operations do:

 s1.containsAll(s2) — returns true if s2 is a subset of s1. (s2 is a subset of s1 if


set s1 contains all of the elements in s2.)
 s1.addAll(s2) — transforms s1 into the union of s1 and s2. (The union of two sets is
the set containing all of the elements contained in either set.)
 s1.retainAll(s2) — transforms s1 into the intersection of s1 and s2. (The intersection of
two sets is the set containing only the elements common to both sets.)
 s1.removeAll(s2) — transforms s1 into the (asymmetric) set difference of s1 and s2.
(For example, the set difference of s1 minus s2 is the set containing all of the elements
found in s1 but not in s2.)

To calculate the union, intersection, or set difference of two sets nondestructively (without
modifying either set), the caller must copy one set before calling the appropriate bulk operation.
The following are the resulting idioms.

Set<Type> union = new HashSet<Type>(s1);


union.addAll(s2);

Set<Type> intersection = new HashSet<Type>(s1);


intersection.retainAll(s2);

Set<Type> difference = new HashSet<Type>(s1);


difference.removeAll(s2);
The implementation type of the result Set in the preceding idioms is HashSet, which is, as
already mentioned, the best all-around Set implementation in the Java platform. However, any
general-purpose Set implementation could be substituted.

Let's revisit the FindDups program. Suppose you want to know which words in the argument
list occur only once and which occur more than once, but you do not want any duplicates
printed out repeatedly. This effect can be achieved by generating two sets — one containing
every word in the argument list and the other containing only the duplicates. The words that
occur only once are the set difference of these two sets, which we know how to compute. Here's
how the resulting program looks.
import java.util.*;

public class FindDups2 {


public static void main(String[] args) {
Set<String> uniques = new HashSet<String>();
Set<String> dups = new HashSet<String>();

for (String a : args)


if (!uniques.add(a))
dups.add(a);

// Destructive set-difference
uniques.removeAll(dups);

System.out.println("Unique words: " + uniques);


System.out.println("Duplicate words: " + dups);
}
}
When run with the same argument list used earlier (i came i saw i left), the program yields the
following output.

Unique words: [left, saw, came]


Duplicate words: [i]
A less common set-algebraic operation is the symmetric set difference — the set of elements
contained in either of two specified sets but not in both. The following code calculates the
symmetric set difference of two sets nondestructively.

Set<Type> symmetricDiff = new HashSet<Type>(s1);


symmetricDiff.addAll(s2);
Set<Type> tmp = new HashSet<Type>(s1);
tmp.retainAll(s2);
symmetricDiff.removeAll(tmp);

4.8 The List Interface


A List is an ordered Collection (sometimes called a sequence). Lists may contain duplicate
elements. In addition to the operations inherited from Collection, the List interface includes
operations for the following:

 Positional access — manipulates elements based on their numerical position in the list.
This includes methods such as get, set, add, addAll, and remove.
 Search — searches for a specified object in the list and returns its numerical position.
Search methods include indexOf and lastIndexOf.
 Iteration — extends Iterator semantics to take advantage of the list's sequential nature.
The listIterator methods provide this behavior.
 Range-view — The sublist method performs arbitrary range operations on the list.
The Java platform contains two general-purpose List implementations. ArrayList, which is
usually the better-performing implementation, and LinkedList which offers better performance
under certain circumstances.

4.8.1 Collection Operations


The operations inherited from Collection all do about what you'd expect them to do, assuming
you're already familiar with them. If you're not familiar with them from Collectio n.
The remove operation always removes the first occurrence of the specified element from the
list. The add and addAll operations always append the new element(s) to the end of the list.
Thus, the following idiom concatenates one list to another.

list1.addAll(list2);
Here's a nondestructive form of this idiom, which produces a third List consisting of the second
list appended to the first.

List<Type> list3 = new ArrayList<Type>(list1);


list3.addAll(list2);
Note that the idiom, in its nondestructive form, takes advantage of ArrayList's standard
conversion constructor.

And here's an example that aggregates some names into a List:

List<String> list = people.stream()


.map(Person::getName)
.collect(Collectors.toList());

Like the Set interface, List strengthens the requirements on the equals and hashCode methods
so that two List objects can be compared for logical equality without regard to their
implementation classes. Two List objects are equal if they contain the same elements in the
same order.

4.8.2 Positional Access and Search Operations


The basic positional access operations are get, set, add and remove.
(The set and remove operations return the old value that is being overwritten or removed.)
Other operations (indexOf and lastIndexOf) return the first or last index of the specified
element in the list.

The addAll operation inserts all the elements of the specified Collection starting at the
specified position. The elements are inserted in the order they are returned by the
specified Collection's iterator. This call is the positional access analog
of Collection's addAll operation.

Here's a little method to swap two indexed values in a List.

public static <E> void swap(List<E> a, int i, int j) {


E tmp = a.get(i);
a.set(i, a.get(j));
a.set(j, tmp);
}
Of course, there's one big difference. This is a polymorphic algorithm: It swaps two elements
in any List, regardless of its implementation type. Here's another polymorphic algorithm that
uses the preceding swap method.

public static void shuffle(List<?> list, Random rnd) {


for (int i = list.size(); i > 1; i--)
swap(list, i - 1, rnd.nextInt(i));
}
This algorithm, which is included in the Java platform's Collections class, randomly permutes
the specified list using the specified source of randomness. It's a bit subtle: It runs up the list
from the bottom, repeatedly swapping a randomly selected element into the current position.
Unlike most naive attempts at shuffling, it's fair (all permutations occur with equal likelihood,
assuming an unbiased source of randomness) and fast (requiring exactly list.size()-1 swaps).
The following program uses this algorithm to print the words in its argument list in random
order.

import java.util.*;

public class Shuffle {


public static void main(String[] args) {
List<String> list = new ArrayList<String>();
for (String a : args)
list.add(a);
Collections.shuffle(list, new Random());
System.out.println(list);
}
}
In fact, this program can be made even shorter and faster. The Arrays class has a static factory
method called asList, which allows an array to be viewed as a List. This method does not copy
the array. Changes in the List write through to the array and vice versa. The resulting List is
not a general-purpose List implementation, because it doesn't implement the
(optional) add and remove operations: Arrays are not resizable. Taking advantage
of Arrays.asList and calling the library version of shuffle, which uses a default source of
randomness, you get the following tiny program whose behavior is identical to the previous
program.

import java.util.*;

public class Shuffle {


public static void main(String[] args) {
List<String> list = Arrays.asList(args);
Collections.shuffle(list);
System.out.println(list);
}
}
4.8.3 Iterators
As you'd expect, the Iterator returned by List's iterator operation returns the elements of the list
in proper sequence. List also provides a richer iterator, called a ListIterator, which allows you
to traverse the list in either direction, modify the list during iteration, and obtain the current
position of the iterator.

The three methods that ListIterator inherits from Iterator (hasNext, next, and remove) do
exactly the same thing in both interfaces. The hasPrevious and the previous operations are
exact analogues of hasNext and next. The former operations refer to the element before the
(implicit) cursor, whereas the latter refer to the element after the cursor. The previous operation
moves the cursor backward, whereas next moves it forward.

Here's the standard idiom for iterating backward through a list.

for (ListIterator<Type> it = list.listIterator(list.size()); it.hasPrevious(); ) {


Type t = it.previous();
...
}
Note the argument to listIterator in the preceding idiom. The List interface has two forms of
the listIterator method. The form with no arguments returns a ListIterator positioned at the
beginning of the list; the form with an int argument returns a ListIterator positioned at the
specified index. The index refers to the element that would be returned by an initial call to next.
An initial call to previous would return the element whose index was index-1. In a list of
length n, there are n+1 valid values for index, from 0 to n, inclusive.

Intuitively speaking, the cursor is always between two elements — the one that would be
returned by a call to previous and the one that would be returned by a call to next.
The n+1 valid index values correspond to the n+ 1 gap between elements, from the gap before
the first element to the gap after the last one. The following figure shows the five possible
cursor positions in a list containing four elements.

Calls to next and previous can be intermixed, but you have to be a bit careful. The first call
to previous returns the same element as the last call to next. Similarly, the first call to next after
a sequence of calls to previous returns the same element as the last call to previous.

It should come as no surprise that the nextIndex method returns the index of the element that
would be returned by a subsequent call to next, and previousIndex returns the index of the
element that would be returned by a subsequent call to previous. These calls are typically used
either to report the position where something was found or to record the position of
the ListIterator so that another ListIterator with identical position can be created.

It should also come as no surprise that the number returned by nextIndex is always one greater
than the number returned by previousIndex. This implies the behaviour of the two boundary
cases: (1) a call to previousIndex when the cursor is before the initial element returns -1 and
(2) a call to nextIndex when the cursor is after the final element returns list.size(). To make all
this concrete, the following is a possible implementation of List.indexOf.
public int indexOf(E e) {
for (ListIterator<E> it = listIterator();
it.hasNext(); )
if (e == null ? it.next() == null : e.equals(it.next()))
return it.previousIndex();
// Element not found
return -1;
}
Note that the indexOf method returns it.previousIndex() even though it is traversing the list in
the forward direction. The reason is that it.nextIndex() would return the index of the element
we are about to examine, and we want to return the index of the element we just examined.

The Iterator interface provides the remove operation to remove the last element returned
by next from the Collection. For ListIterator, this operation removes the last element returned
by nextor previous. The ListIterator interface provides two additional operations to modify the
list — set and add. The set method overwrites the last element returned
by next or previous with the specified element. The following polymorphic algorithm
uses set to replace all occurrences of one specified value with another.

public static <E> void replace(List<E> list, E val, E newVal) {


for (ListIterator<E> it = list.listIterator(); it.hasNext(); )
if (val == null ? it.next() == null : val.equals(it.next()))
it.set(newVal);
}
The only bit of trickiness in this example is the equality test between val and it.next. You need
to special-case a val value of null to prevent a NullPointerException.

The add method inserts a new element into the list immediately before the current cursor
position. This method is illustrated in the following polymorphic algorithm to replace a ll
occurrences of a specified value with the sequence of values contained in the specified list.

public static <E>


void replace(List<E> list, E val, List<? extends E> newVals) {
for (ListIterator<E> it = list.listIterator(); it.hasNext(); ){
if (val == null ? it.next() == null : val.equals(it.next())) {
it.remove();
for (E e : newVals)
it.add(e);
}
}
}

4.8.4. Range-View Operation


The range-view operation, subList(int fromIndex, int toIndex), returns a List view of the
portion of this list whose indices range from fromIndex, inclusive, to toIndex, exclusive.
This half-open range mirrors the typical for loop.

for (int i = fromIndex; i < toIndex; i++) {


...
}
As the term view implies, the returned List is backed up by the List on which subList was
called, so changes in the former are reflected in the latter.

This method eliminates the need for explicit range operations (of the sort that commonly exist
for arrays). Any operation that expects a List can be used as a range operation by passing
a subList view instead of a whole List. For example, the following idiom removes a range of
elements from a List.

list.subList(fromIndex, toIndex).clear();
Similar idioms can be constructed to search for an element in a range.

int i = list.subList(fromIndex, toIndex).indexOf(o);


int j = list.subList(fromIndex, toIndex).lastIndexOf(o);
Note that the preceding idioms return the index of the found element in the subList, not the
index in the backing List.

Any polymorphic algorithm that operates on a List, such as the replace and shuffle examples,
works with the List returned by subList.

Here's a polymorphic algorithm whose implementation uses subList to deal a hand from a deck.
That is, it returns a new List (the "hand") containing the specified number of elements taken
from the end of the specified List (the "deck"). The elements returned in the hand are removed
from the deck.

public static <E> List<E> dealHand(List<E> deck, int n) {


int deckSize = deck.size();
List<E> handView = deck.subList(deckSize - n, deckSize);
List<E> hand = new ArrayList<E>(handView);
handView.clear();
return hand;
}
Note that this algorithm removes the hand from the end of the deck. For many
common List implementations, such as ArrayList, the performance of removing elements from
the end of the list is substantially better than that of removing elements from the beginning.

The following is a program that uses the dealHand method in combinatio n


with Collections.shuffle to generate hands from a normal 52-card deck. The program takes two
command-line arguments: (1) the number of hands to deal and (2) the number of cards in each
hand.

import java.util.*;

public class Deal {


public static void main(String[] args) {
if (args.length < 2) {
System.out.println("Usage: Deal hands cards");
return;
}
int numHands = Integer.parseInt(args[0]);
int cardsPerHand = Integer.parseInt(args[1]);

// Make a normal 52-card deck.


String[] suit = new String[] {
"spades", "hearts",
"diamonds", "clubs"
};
String[] rank = new String[] {
"ace", "2", "3", "4",
"5", "6", "7", "8", "9", "10",
"jack", "queen", "king"
};

List<String> deck = new ArrayList<String>();


for (int i = 0; i < suit.length; i++)
for (int j = 0; j < rank.length; j++)
deck.add(rank[j] + " of " + suit[i]);

// Shuffle the deck.


Collections.shuffle(deck);

if (numHands * cardsPerHand > deck.size()) {


System.out.println("Not enough cards.");
return;
}

for (int i = 0; i < numHands; i++)


System.out.println(dealHand(deck, cardsPerHand));
}

public static <E> List<E> dealHand(List<E> deck, int n) {


int deckSize = deck.size();
List<E> handView = deck.subList(deckSize - n, deckSize);
List<E> hand = new ArrayList<E>(handView);
handView.clear();
return hand;
}
}
Running the program produces output like the following.

% java Deal 4 5

[8 of hearts, jack of spades, 3 of spades, 4 of spades,


king of diamonds]
[4 of diamonds, ace of clubs, 6 of clubs, jack of hearts,
queen of hearts]
[7 of spades, 5 of spades, 2 of diamonds, queen of diamonds,
9 of clubs]
[8 of spades, 6 of diamonds, ace of spades, 3 of hearts,
ace of hearts]

Although the subList operation is extremely powerful, some care must be exercised when using
it. The semantics of the List returned by subList become undefined if elements are added to or
removed from the backing List in any way other than via the returned List. Thus, it's highly
recommended that you use the List returned by subList only as a transient object — to perform
one or a sequence of range operations on the backing List. The longer you use
the subList instance, the greater the probability that you'll compromise it by modifying the
backing List directly or through another subListobject. Note that it is legal to modify a sublist
of a sublist and to continue using the original sublist (though not concurrently).

4.9 Queue Interface


A Queue is a collection for holding elements prior to processing. Besides
basic Collection operations, queues provide additional insertion, removal, and inspectio n
operations. The Queue interface follows.

public interface Queue<E> extends Collection<E> {


E element();
boolean offer(E e);
E peek();
E poll();
E remove();
}

Each Queue method exists in two forms: (1) one throws an exception if the operation fails, and
(2) the other returns a special value if the operation fails (either null or false, depending on the
operation). The regular structure of the interface is illustrated in the following table.

Queue Interface Structure


Type of Operation Throws exception Returns special value
Insert add(e) offer(e)
Remove remove() poll()
Examine element() peek()

Queues typically, but not necessarily, order elements in a FIFO (first-in- first-out) manner.
Among the exceptions are priority queues, which order elements according to their values.
Whatever ordering is used, the head of the queue is the element that would be removed by a
call to remove or poll. In a FIFO queue, all new elements are inserted at the tail of the queue.
Other kinds of queues may use different placement rules. Every Queue implementation must
specify its ordering properties.

It is possible for a Queue implementation to restrict the number of elements that it holds; such
queues are known as bounded. Some Queue implementations in java.util.concurrent are
bounded, but the implementations in java.util are not.
The add method, which Queue inherits from Collection, inserts an element unless it would
violate the queue's capacity restrictions, in which case it throws IllegalStateExceptio n.
The offer method, which is intended solely for use on bounded queues, differs from add only
in that it indicates failure to insert an element by returning false.

The remove and poll methods both remove and return the head of the queue. Exactly which
element gets removed is a function of the queue's ordering policy.
The remove and poll methods differ in their behavior only when the queue is empty. Under
these circumstances, remove throws NoSuchElementException, while poll returns null.

The element and peek methods return, but do not remove, the head of the queue. They differ
from one another in precisely the same fashion as remove and poll: If the queue is
empty, element throws NoSuchElementException, while peek returns null.

Queue implementations generally do not allow insertion of null elements.


The LinkedList implementation, which was retrofitted to implement Queue, is an exception.
For historical reasons, it permits null elements, but you should refrain from taking advantage
of this, because null is used as a special return value by the poll and peek methods.

Queue implementations generally do not define element-based versions of


the equals and hashCode methods but instead inherit the identity-based versions from Object.

The Queue interface does not define the blocking queue methods, which are common in
concurrent programming. These methods, which wait for elements to appear or for space to
become available, are defined in the interface java.util.concurrent.BlockingQueue, which
extends Queue.

In the following example program, a queue is used to implement a countdown timer. The queue
is preloaded with all the integer values from a number specified on the command line to zero,
in descending order. Then, the values are removed from the queue and printed at one-second
intervals. The program is artificial in that it would be more natural to do the same thing without
using a queue, but it illustrates the use of a queue to store elements prior to subsequent
processing.

import java.util.*;

public class Countdown {


public static void main(String[] args) throws InterruptedException {
int time = Integer.parseInt(args[0]);
Queue<Integer> queue = new LinkedList<Integer>();

for (int i = time; i >= 0; i--)


queue.add(i);

while (!queue.isEmpty()) {
System.out.println(queue.remove());
Thread.sleep(1000);
}
}
}
In the following example, a priority queue is used to sort a collection of elements. Again this
program is artificial in that there is no reason to use it in favor of the sort method provided
in Collections, but it illustrates the behavior of priority queues.

static <E> List<E> heapSort(Collection<E> c) {


Queue<E> queue = new PriorityQueue<E>(c);
List<E> result = new ArrayList<E>();

while (!queue.isEmpty())
result.add(queue.remove());

return result;
}

4.10 The Deque Interface


Usually pronounced as deck, a deque is a double-ended-queue. A double-ended-queue is a
linear collection of elements that supports the insertion and removal of elements at both end
points. The Dequeinterface is a richer abstract data type than both Stack and Queue because it
implements both stacks and queues at the same time. The Deque interface, defines methods to
access the elements at both ends of the Deque instance. Methods are provided to insert, remove,
and examine the elements. Predefined classes like ArrayDeque and LinkedList impleme nt
the Deque interface.

Note that the Deque interface can be used both as last-in-first-out stacks and first-in- first- o ut
queues. The methods given in the Deque interface are divided into three parts:

4.10.1 Insert
The addfirst and offerFirst methods insert elements at the beginning of the Deque instance.
The methods addLast and offerLast insert elements at the end of the Deque instance. When the
capacity of the Deque instance is restricted, the preferred methods
are offerFirst and offerLast because addFirst might fail to throw an exception if it is full.

4.10.2 Remove
The removeFirst and pollFirst methods remove elements from the beginning of
the Deque instance. The removeLast and pollLast methods remove elements from the end. The
methods pollFirst and pollLast return null if the Deque is empty whereas the
methods removeFirst and removeLast throw an exception if the Deque instance is empty.

4.10.3 Retrieve
The methods getFirst and peekFirst retrieve the first element of the Deque instance. These
methods dont remove the value from the Deque instance. Similarly, the
methods getLast and peekLastretrieve the last element. The
methods getFirst and getLast throw an exception if the deque instance is empty whereas the
methods peekFirst and peekLast return NULL.
The 12 methods for insertion, removal and retieval of Deque elements are summarized in the
following table:

Deque Methods

Type of First Element (Beginning of Last Element (End of


Operation the Deque instance) the Deque instance)

addFirst(e) addLast(e)
Insert
offerFirst(e) offerLast(e)

removeFirst() removeLast()
Remove
pollFirst() pollLast()

getFirst() getLast()
Examine
peekFirst() peekLast()

In addition to these basic methods to insert,remove and examine a Deque instance,


the Deque interface also has some more predefined methods. One of these
is removeFirstOccurence, this method removes the first occurence of the specified element if
it exists in the Deque instance. If the element does not exist then the Deque instance remains
unchanged. Another similar method is removeLastOccurence; this method removes the last
occurence of the specified element in the Deque instance. The return type of these methods
is boolean, and they return true if the element exists in the Deque instance.

4.11 The Map Interface


A Map is an object that maps keys to values. A map cannot contain duplicate keys: Each key
can map to at most one value. It models the mathematical function abstraction.
The Map interface includes methods for basic operations (such
as put, get, remove, containsKey, containsValue, size, and empty), bulk operations (such
as putAll and clear), and collection views (such as keySet, entrySet, and values).

The Java platform contains three general-purpose Map implementations: HashMap, TreeMap,
and LinkedHashMap. Their behavior and performance are precisely analogous
to HashSet, TreeSet, and LinkedHashSet.
Modeling real-world objects is a common task in object-oriented programming, so it is
reasonable to think that some programs might, for example, group employees by department:

// Group employees by department


Map<Department, List<Employee>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
Or compute the sum of all salaries by department:

// Compute sum of salaries by department


Map<Department, Integer> totalByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.summingInt(Employee::getSalary)));
Or perhaps group students by passing or failing grades:

// Partition students into passing and failing


Map<Boolean, List<Student>> passingFailing = students.stream()
.collect(Collectors.partitioningBy(s -> s.getGrade()>= PASS_THRESHOLD));
You could also group people by city:

// Classify Person objects by city


Map<String, List<Person>> peopleByCity
= personStream.collect(Collectors.groupingBy(Person::getCity));
Or even cascade two collectors to classify people by state and city:

// Cascade Collectors
Map<String, Map<String, List<Person>>> peopleByStateAndCity
= personStream.collect(Collectors.groupingBy(Person::getState,
Collectors.groupingBy(Person::getCity)))

4.11.1 Map Interface Basic Operations


The basic operations of Map (put, get, containsKey, containsValue, size, and isEmpty) behave
exactly like their counterparts in Hashtable. The following program generates a frequency
table of the words found in its argument list. The frequency table maps each word to the number
of times it occurs in the argument list.

import java.util.*;

public class Freq {


public static void main(String[] args) {
Map<String, Integer> m = new HashMap<String, Integer>();

// Initialize frequency table from command line


for (String a : args) {
Integer freq = m.get(a);
m.put(a, (freq == null) ? 1 : freq + 1);
}

System.out.println(m.size() + " distinct words:");


System.out.println(m);
}
}
The only tricky thing about this program is the second argument of the put statement. That
argument is a conditional expression that has the effect of setting the frequency to one if the
word has never been seen before or one more than its current value if the word has already
been seen. Try running this program with the command:

java Freq if it is to be it is up to me to delegate


The program yields the following output.

8 distinct words:
{to=3, delegate=1, be=1, it=2, up=1, if=1, me=1, is=2}
Suppose you'd prefer to see the frequency table in alphabetical order. All you have to do is
change the implementation type of the Map from HashMap to TreeMap. Making this four-
character change causes the program to generate the following output from the same command
line.

8 distinct words:
{be=1, delegate=1, if=1, is=2, it=2, me=1, to=3, up=1}
Similarly, you could make the program print the frequency table in the order the words first
appear on the command line simply by changing the implementation type of the map
to LinkedHashMap. Doing so results in the following output.

8 distinct words:
{if=1, it=2, is=2, to=3, be=1, up=1, me=1, delegate=1}
This flexibility provides a potent illustration of the power of an interface-based framework.

Like the Setand Listinterfaces, Map strengthens the requirements on


the equals and hashCode methods so that two Map objects can be compared for logica l
equality without regard to their implementation types. Two Map instances are equal if they
represent the same key-value mappings.

By convention, all general-purpose Map implementations provide constructors that take


a Map object and initialize the new Map to contain all the key-value mappings in the
specified Map. This standard Mapconversion constructor is entirely analogous to the
standard Collection constructor: It allows the caller to create a Map of a desired
implementation type that initially contains all of the mappings in another Map, regardless of
the other Map's implementation type. For example, suppose you have a Map, named m. The
following one-liner creates a new HashMap initially containing all of the same key-value
mappings as m.

Map<K, V> copy = new HashMap<K, V>(m);

4.11.2 Map Interface Bulk Operations


The clear operation does exactly what you would think it could do: It removes all the mappings
from the Map. The putAll operation is the Map analogue of
the Collection interface's addAll operation. In addition to its obvious use of dumping
one Map into another, it has a second, more subtle use. Suppose a Map is used to represent a
collection of attribute-value pairs; the putAll operation, in combination with
the Map conversion constructor, provides a neat way to implement attribute map creation with
default values. The following is a static factory method that demonstrates this technique.

static <K, V> Map<K, V> newAttributeMap(Map<K, V>defaults, Map<K, V> overrides) {
Map<K, V> result = new HashMap<K, V>(defaults);
result.putAll(overrides);
return result;
}
4.11.3 Collection Views
The Collection view methods allow a Map to be viewed as a Collection in these three ways:

 keySet — the Set of keys contained in the Map.


 values — The Collection of values contained in the Map. This Collection is not a Set,
because multiple keys can map to the same value.
 entrySet — the Set of key-value pairs contained in the Map. The Map interface
provides a small nested interface called Map.Entry, the type of the elements in this Set.

The Collection views provide the only means to iterate over a Map. This example illustra tes
the standard idiom for iterating over the keys in a Map with a for-each construct:

for (KeyType key : m.keySet())


System.out.println(key);
and with an iterator:

// Filter a map based on some


// property of its keys.
for (Iterator<Type> it = m.keySet().iterator(); it.hasNext(); )
if (it.next().isBogus())
it.remove();

The idiom for iterating over values is analogous. Following is the idiom for iterating over key-
value pairs.

for (Map.Entry<KeyType, ValType> e : m.entrySet())


System.out.println(e.getKey() + ": " + e.getValue());
At first, many people worry that these idioms may be slow because the Map has to create a
new Collection instance each time a Collection view operation is called. There's no reason that
a Map cannot always return the same object each time it is asked for a given Collection view.
This is precisely what all the Map implementations in java.util do.

With all three Collection views, calling an Iterator's remove operation removes the associated
entry from the backing Map, assuming that the backing Map supports element removal to
begin with. This is illustrated by the preceding filtering idiom.

With the entrySet view, it is also possible to change the value associated with a key by calling
a Map. Entry's setValue method during iteration (again, assuming the Map supports value
modification to begin with). Note that these are the only safe ways to modify a Map during
iteration; the behavior is unspecified if the underlying Map is modified in any other way while
the iteration is in progress.

The Collection views support element removal in all its many forms
— remove, removeAll, retainAll, and clear operations, as well as
the Iterator.remove operation. (Yet again, this assumes that the backing Map supports element
removal.)
The Collection views do not support element addition under any circumstances. It would make
no sense for the keySet and values views, and it's unnecessary for the entrySet view, because
the backing Map's put and putAll methods provide the same functionality.

4.12 Object Ordering


A List l may be sorted as follows.

Collections.sort(l);
If the List consists of String elements, it will be sorted into alphabetical order. If it consists
of Date elements, it will be sorted into chronological order. How does this
happen? String and Date both impleme nt
the Comparable interface. Comparable implementations provide a natural ordering for a class,
which allows objects of that class to be sorted automatically. The following table summar izes
some of the more important Java platform classes that implement Comparable.

Classes Implementing Comparable

Class Natural Ordering

Byte Signed numerical

Character Unsigned numerical

Long Signed numerical

Integer Signed numerical

Short Signed numerical

Double Signed numerical

Float Signed numerical

BigInteger Signed numerical

BigDecimal Signed numerical

Boolean Boolean.FALSE < Boolean.TRUE

File System-dependent lexicographic on path name

String Lexicographic

Date Chronological

CollationKey Locale-specific lexicographic


If you try to sort a list, the elements of which do not implement Comparable, Collections.so rt
(list) will throw a ClassCastException. Similarly, Collections.sort (list, comparator) will throw
a ClassCastException if you try to sort a list whose elements cannot be compared to one
another using the comparator. Elements that can be compared to one another are
called mutually comparable. Although elements of different types may be mutua lly
comparable, none of the classes listed here permit interclass comparison.
This is all you really need to know about the Comparable interface if you just want to sort lists
of comparable elements or to create sorted collections of them. The next section will be of
interest to you if you want to implement your own Comparable type.

4.12.1 Writing Your Own Comparable Types


The Comparable interface consists of the following method.

public interface Comparable<T> {


public int compareTo(T o);
}
The compareTo method compares the receiving object with the specified object and returns a
negative integer, 0, or a positive integer depending on whether the receiving object is less than,
equal to, or greater than the specified object. If the specified object cannot be compared to the
receiving object, the method throws a ClassCastException.

The following class representing a person's name implements Comparable.

import java.util.*;

public class Name implements Comparable<Name> {


private final String firstName, lastName;

public Name(String firstName, String lastName) {


if (firstName == null || lastName == null)
throw new NullPointerException();
this.firstName = firstName;
this.lastName = lastName;
}

public String firstName() { return firstName; }


public String lastName() { return lastName; }

public boolean equals(Object o) {


if (!(o instanceof Name))
return false;
Name n = (Name) o;
return n.firstName.equals(firstName) && n.lastName.equals(lastName);
}

public int hashCode() {


return 31*firstName.hashCode() + lastName.hashCode();
}

public String toString() {


return firstName + " " + lastName;
}

public int compareTo(Name n) {


int lastCmp = lastName.compareTo(n.lastName);
return (lastCmp != 0 ? lastCmp : firstName.compareTo(n.firstName));
}
}
To keep the preceding example short, the class is somewhat limited: It doesn't support middle
names, it demands both a first and a last name, and it is not internationalized in any way.
Nonetheless, it illustrates the following important points:

 Name objects are immutable. All other things being equal, immutable types are the way
to go, especially for objects that will be used as elements in Sets or as keys in Maps.
These collections will break if you modify their elements or keys while they're in the
collection.
 The constructor checks its arguments for null. This ensures that all Name objects are
well formed so that none of the other methods will ever throw a NullPointerExceptio n.
 The hashCode method is redefined. This is essential for any class that redefines
the equals method. (Equal objects must have equal hash codes.)
 The equals method returns false if the specified object is null or of an inappropriate
type. The compareTo method throws a runtime exception under these circumstances.
Both of these behaviors are required by the general contracts of the respective methods.
 The toString method has been redefined so it prints the Name in human-readable form.
This is always a good idea, especially for objects that are going to get put into
collections. The various collection types' toString methods depend on
the toString methods of their elements, keys, and values.

Since this section is about element ordering, let's talk a bit more
about Name's compareTo method. It implements the standard name-ordering algorithm, where
last names take precedence over first names. This is exactly what you want in a natural
ordering. It would be very confusing indeed if the natural ordering were unnatural!

Take a look at how compareTo is implemented, because it's quite typical. First, you compare
the most significant part of the object (in this case, the last name). Often, you can just use the
natural ordering of the part's type. In this case, the part is a String and the natural
(lexicographic) ordering is exactly what's called for. If the comparison results in anything other
than zero, which represents equality, you're done: You just return the result. If the most
significant parts are equal, you go on to compare the next most-significant parts. In this case,
there are only two parts — first name and last name. If there were more parts, you'd proceed in
the obvious fashion, comparing parts until you found two that weren't equal or you were
comparing the least-significant parts, at which point you'd return the result of the comparison.

Just to show that it all works, here's a program that builds a list of names and sorts them.

import java.util.*;

public class NameSort {


public static void main(String[] args) {
Name nameArray[] = {
new Name("John", "Smith"),
new Name("Karl", "Ng"),
new Name("Jeff", "Smith"),
new Name("Tom", "Rich")
};

List<Name> names = Arrays.asList(nameArray);


Collections.sort(names);
System.out.println(names);
}
}
If you run this program, here's what it prints.

[Karl Ng, Tom Rich, Jeff Smith, John Smith]


There are four restrictions on the behavior of the compareTo method, which we won't go into
now because they're fairly technical and boring and are better left in the API documentatio n.
It's really important that all classes that implement Comparable obey these restrictions, so read
the documentation for Comparable if you're writing a class that implements it. Attempting to
sort a list of objects that violate the restrictions has undefined behavior. Technically speaking,
these restrictions ensure that the natural ordering is a total order on the objects of a class that
implements it; this is necessary to ensure that sorting is well defined.

4.13 The SortedSet Interface


A SortedSet is a Set that maintains its elements in ascending order, sorted according to the
elements' natural ordering or according to a Comparator provided at SortedSet creation time.
In addition to the normal Set operations, the SortedSet interface provides operations for the
following:

 Range view — allows arbitrary range operations on the sorted set


 Endpoints — returns the first or last element in the sorted set
 Comparator access — returns the Comparator, if any, used to sort the set

The code for the SortedSet interface follows.

public interface SortedSet<E> extends Set<E> {


// Range-view
SortedSet<E> subSet(E fromElement, E toElement);
SortedSet<E> headSet(E toElement);
SortedSet<E> tailSet(E fromElement);

// Endpoints
E first();
E last();

// Comparator access
Comparator<? super E> comparator();
}

4.13.1 Set Operations


The operations that SortedSet inherits from Set behave identically on sorted sets and normal
sets with two exceptions:

 The Iterator returned by the iterator operation traverses the sorted set in order.
 The array returned by toArray contains the sorted set's elements in order.

Although the interface doesn't guarantee it, the toString method of the Java
platform's SortedSet implementations returns a string containing all the elements of the sorted
set, in order.

4.13.2 Standard Constructors


By convention, all general-purpose Collection implementations provide a standard conversion
constructor that takes a Collection; SortedSet implementations are no exception. In TreeSet,
this constructor creates an instance that sorts its elements according to their natural ordering.
This was probably a mistake. It would have been better to check dynamically to see whether
the specified collection was a SortedSet instance and, if so, to sort the new TreeSet according
to the same criterion (comparator or natural ordering). Because TreeSet took the approach that
it did, it also provides a constructor that takes a SortedSet and returns a new TreeSet containing
the same elements sorted according to the same criterion. Note that it is the compile-time type
of the argument, not its runtime type, that determines which of these two constructors is
invoked (and whether the sorting criterion is preserved).

SortedSet implementations also provide, by convention, a constructor that takes


a Comparator and returns an empty set sorted according to the specified Comparator. If null is
passed to this constructor, it returns a set that sorts its elements according to their natural
ordering.

4.13.3 Range-view Operations


The range-view operations are somewhat analogous to those provided by the List interface, but
there is one big difference. Range views of a sorted set remain valid even if the backing sorted
set is modified directly. This is feasible because the endpoints of a range view of a sorted set
are absolute points in the element space rather than specific elements in the backing collectio n,
as is the case for lists. A range-view of a sorted set is really just a window onto whatever portion
of the set lies in the designated part of the element space. Changes to the range-view write back
to the backing sorted set and vice versa. Thus, it's okay to use range-views on sorted sets for
long periods of time, unlike range-views on lists.

Sorted sets provide three range-view operations. The first, subSet, takes two endpoints,
like subList. Rather than indices, the endpoints are objects and must be comparable to the
elements in the sorted set, using the Set's Comparator or the natural ordering of its elements,
whichever the Set uses to order itself. Like subList, the range is half open, including its low
endpoint but excluding the high one.
Thus, the following line of code tells you how many words between "doorbell" and "pickle ",
including "doorbell" but excluding "pickle", are contained in a SortedSet of strings
called dictionary:

int count = dictionary.subSet("doorbell", "pickle").size();


In like manner, the following one-liner removes all the elements beginning with the letter f.

dictionary.subSet("f", "g").clear();
A similar trick can be used to print a table telling you how many words begin with each letter.

for (char ch = 'a'; ch <= 'z'; ) {


String from = String.valueOf(ch++);
String to = String.valueOf(ch);
System.out.println(from + ": " + dictionary.subSet(from, to).size());
}
Suppose you want to view a closed interval, which contains both of its endpoints, instead of an
open interval. If the element type allows for the calculation of the successor of a given value
in the element space, merely request
the subSet from lowEndpoint to successor(highEndpoint). Although it isn't entirely obvious,
the successor of a string s in String's natural ordering is s + "\0" — that is, swith
a null character appended.

Thus, the following one-liner tells you how many words between "doorbell" and "pickle ",
including doorbell and pickle, are contained in the dictionary.

count = dictionary.subSet("doorbell", "pickle\0").size();


A similar technique can be used to view an open interval, which contains neither endpoint. The
open-interval view from lowEndpoint to highEndpoint is the half-open interva l
from successor(lowEndpoint) to highEndpoint. Use the following to calculate the number of
words between "doorbell" and "pickle", excluding both.

count = dictionary.subSet("doorbell\0", "pickle").size();


The SortedSet interface contains two more range-view operations — headSet and tailSet, both
of which take a single Object argument. The former returns a view of the initial portion of the
backing SortedSet, up to but not including the specified object. The latter returns a view of the
final portion of the backing SortedSet, beginning with the specified object and continuing to
the end of the backing SortedSet. Thus, the following code allows you to view the dictionar y
as two disjoint volumes (a-m and n-z).

SortedSet<String> volume1 = dictionary.headSet("n");


SortedSet<String> volume2 = dictionary.tailSet("n");

4.13.4 Endpoint Operations


The SortedSet interface contains operations to return the first and last elements in the sorted
set, not surprisingly called first and last. In addition to their obvious uses, last allows a
workaround for a deficiency in the SortedSet interface. One thing you'd like to do with
a SortedSet is to go into the interior of the Set and iterate forward or backward. It's easy enough
to go forward from the interior: Just get a tailSet and iterate over it. Unfortunately, there's no
easy way to go backward.

The following idiom obtains the first element that is less than a specified object o in the element
space.

Object predecessor = ss.headSet(o).last();


This is a fine way to go one element backward from a point in the interior of a sorted set. It
could be applied repeatedly to iterate backward, but this is very inefficient, requiring a lookup
for each element returned.

4.13.5 Comparator Accessor


The SortedSet interface contains an accessor method called comparator that returns
the Comparator used to sort the set, or null if the set is sorted according to the natural
ordering of its elements. This method is provided so that sorted sets can be copied into new
sorted sets with the same ordering. It is used by the SortedSet constructor described previous ly.

4.14 The SortedMap Interface


A SortedMap is a Map that maintains its entries in ascending order, sorted according to the
keys' natural ordering, or according to a Comparator provided at the time of
the SortedMap creation. Natural ordering and Comparators are discussed in the Object
Ordering section. The SortedMap interface provides operations for normal Map operations
and for the following:

 Range view — performs arbitrary range operations on the sorted map


 Endpoints — returns the first or the last key in the sorted map
 Comparator access — returns the Comparator, if any, used to sort the map

The following interface is the Map analog of SortedSet.

public interface SortedMap<K, V> extends Map<K, V>{


Comparator<? super K> comparator();
SortedMap<K, V> subMap(K fromKey, K toKey);
SortedMap<K, V> headMap(K toKey);
SortedMap<K, V> tailMap(K fromKey);
K firstKey();
K lastKey();
}

4.14.1 Map Operations


The operations SortedMap inherits from Map behave identically on sorted maps and normal
maps with two exceptions:

 The Iterator returned by the iterator operation on any of the sorted


map's Collection views traverse the collections in order.
 The arrays returned by the Collection views' toArray operations contain the keys,
values, or entries in order.
Although it isn't guaranteed by the interface, the toString method of the Collection views in all
the Java platform's SortedMap implementations returns a string containing all the elements of
the view, in order.

4.14.2 Standard Constructors


By convention, all general-purpose Map implementations provide a standard conversion
constructor that takes a Map; SortedMap implementations are no exception. In TreeMap, this
constructor creates an instance that orders its entries according to their keys' natural ordering.
This was probably a mistake. It would have been better to check dynamically to see whether
the specified Map instance was a SortedMap and, if so, to sort the new map according to the
same criterion (comparator or natural ordering). Because TreeMap took the approach it did, it
also provides a constructor that takes a SortedMapand returns a new TreeMap containing the
same mappings as the given SortedMap, sorted according to the same criterion. Note that it is
the compile-time type of the argument, not its runtime type, that determines whether
the SortedMap constructor is invoked in preference to the ordinary map constructor.

SortedMap implementations also provide, by convention, a constructor that takes


a Comparator and returns an empty map sorted according to the specified Comparator.
If null is passed to this constructor, it returns a Map that sorts its mappings according to their
keys' natural orderin
MODULE 5:
GRAPHICAL PROGRAMMING WITH SCALA AND SWING
Programs with a graphical user interface (GUI) are quite different from programs that use
terminal input and output. In a terminal-based program, the computation is drive by the
program: It does its job, sometimes printing some output, sometimes requesting input from the
user and pausing to wait for this input.

In a GUI program, the user is much more in control of the program: There are buttons to be
pressed, the window can be moved, the menu activated, or an animation may be playing. This
requires a different style of programming: Instead of following a fixed sequence of actions, a
GUI program reacts to events. There are many different kinds of events: the user may click
with the mouse, move the mouse button, press some key, close the window, move or resize the
window, and so on.

At first sight, it's therefore a bit difficult to figure out what is happing in a GUI program. There
are always two steps: First, we need to set up the windows and configure their contents. Then,
the program doesn't do anything actively — it only acts by responding to events. These
responses to events have been set up in the first phase. We will use the Scala Swing library for
building our user interfaces. Swing is actually a Java library, but using the Scala version is
easier and more elegant.
5.1 Basics-Creating a Window

As usual, when we run GuiProgramOne, its main method is executed. This method creates a
new window, a class of type UI. We make the window appear on the screen by setting its visib le
member.
If you look carefully, you will see that the output "End of main function" appears in the
terminal, but the program does not stop. Even though the main method of GuiProgra mO ne
returns, the program keeps running — the Swing library takes care of that.

We have not set up any responses to events in this program, but some actions already work
fine: For instance, you can move and resize the window, you can iconify it or maximize it. The
content is nicely moved to appear in the centre. Finally, when you press the X symbol in the
title bar, the window closes—and the program terminates! All these events are handled by the
Swing library itself, so we do not have to worry about them.

Let's have a look at the UI (for UserInterface) class. This class extends the Swing class
scala.swing.MainFrame, which is a window that can appear on the screen. This means that a
UI object has all the methods and fields of the MainFrame class in addition to its own methods
and fields, and can be used like a MainFrame.

In this small program, the UI constructor only sets three of its fields (which it has "inherited "
from the MainFrame class), namely title (which is the title that appears on top of the window),
the preferred size (which is the starting size of the window), and its contents. The contents is
what will appear inside the window, and it has to be a component, that is, an object of a class
that extends scala.swing.Component. There are many such component classes. Here we use a
very simple one, scala.swing.Label, which only displays a string.

5.2 Responding to an event


In our second program, gui2.scala, we learn how to program a response to an event. We will
do something unusual, and simply fill the entire window with a button:

When we run the program, it looks similar to the previous one, but the color of the window is
different, and when you move the mouse over it you notice that it is actually a large button:

When you press the button, the text "Thank you" appears on the terminal. Try it!

Note how we set this up:

contents = Button("Press me, please") { println("Thank you") }


The Button function constructs a Button object. It takes two argument lists. The first argument
list contains just the text that will appear inside the button. The second argument list contains
an action that will occur when the button is pressed. There is something funny about this second
argument list: The operation println is not executed when the Button function is called. Instead,
the operation is interpreted as a function object, to be used when the button is pressed.

By the way, we could have written the second argument list with the usual syntax with round
parentheses:

contents = Button("Press me, please")(println("Thank you"))

5.3 The scala.swing package


The scala.swing package provides a collection of thin wrappers around Java Swing classes.
Our goal is to deliver a library that at the same time feels natural to use in Scala and that is
sufficiently close to Java Swing to appeal to Java programmers with existing Swing experience.

The following example can be compiled as it stands to create a simple "HelloWor ld "
scala.swing program as depicted in Figure 5.1:

import swing. _
object HelloWorld extends SimpleSwingApplication {
def top = new MainFrame {
title = "Hello, World!"
contents = new Button {
text = "Click Me!"
}
}
}

Figure 5.1: The frame of a simple "Hello,World!" application

Most simple scala.swing applications consist of a main object such as HelloWorld above that
extends class SimpleSwingApplication. The main object needs to implement method top that
takes no arguments and returns a Frame, or as in this case, a MainFrame. A frame is a standard
operating system window with decorations such as a close button and a resize handle.

A MainFrame is a frame that automatically quits the application when closed and is thus used
by most applications that need only a single frame.
On startup, class SimpleSwingApplication takes care of initializing the Swing framework and
opens the frame returned by method top. The above implementation creates a main frame using
the standard scala.swing notation for member initialization. We are really creating an
anonymous class with the following expression.
new MainFrame {
title = "Hello, World!"
contents = new Button {
text = "Click Me!"
}
}
Anonymous class syntax lets us access all members of class MainFrame inside the inner curly
braces. We are setting the frame title property to the string "Hello, World!" and the contents of
the frame to a simple click button that reads "Click Me!" and does nothing when clicked. Many
classes provide a small number of convenience constructors, which are similar to those
provided by Java Swing and make component creation more concise. For the button
instantiation above, we could have written

contents = new Button("Click Me!")

When setting a larger number of properties, however, it is generally preferable to use


anonymous class syntax, since explicit naming of properties often makes scala.swing code
more readable.

5.3.1 Classes SwingApplication and SimpleSwingApplication


Class SimpleSwingApplication from above extends SwingApplication which implements a
default main method. It further provides the following three methods:
def startup(args: Array[String])
def quit() { ... }
def shutdown() { ... }
Method startup is called in the default main method implementation and needs to be
implemented by clients. It is called on the Swing event dispatching thread.
Method quit should be called to gracefully quit the application. It calls method shutdown as the
last step before ultimately terminating the application.
Method shutdown should be overwritten by clients who need to cleanup resources and run
shutdown specific code.
Subclass SimpleSwingApplication provides a default implementation for method startup that
shows the frame returned by the client’s implementation of method top.

5.4 Laying out components


Previously, we have seen how to add a button to a frame. For more complex layouts, we nest
components inside containers that arrange their chid components according to specific rules.
In scala.swing as well as in Java Swing, there are two types of container components: panes
and panels. Panes show a fixed number of child components, potentially together with
decorations specific to the respective pane. One example is a split pane that shows two
components side by side horizontally or vertically with a knob in between that lets the user
allocate more or less space for one or the other component. Another example is a scrollpane,
that contains a single component inside a canvas with scroll bars.
Panels, on the other hand, are containers that arrange an arbitrary number of child components
according to layout rules that are customizable by clients to a certain degree.

5.4.1 A strongly typed, concise container interface


In Java Swing, containers are decoupled from specific layout managers. To create a Java Swing
container, clients use the JPanel component together with a suitable layout manager, such as
follows.
val panel = new JPanel()
panel.setLayout(new BorderLayout())
Components can be added with one of the add methods from the java.awt.Component
base class:

def add(comp: Component, constraints: Object): Component


def add(comp: Component, index: Int): Component
def add(comp: Component,constraints: Object, index: Int): Component

Adding a button to the above panel with a BorderLayout manager looks as follows:
panel.add(new JButton("click me"), BorderLayout.CENTER)

The JPanel.add methods call the layout manager through a common interface to make a
given component and its layout constraints available.
Different from Java Swing, containers and layout manager in scala.swing are coupled. This
allows for an interface that is more concise and catches more type errors at compile time. In
Java Swing, layout constraints can only be of type Object or Int as dictated by the add methods’
interface above. A generic panel class that takes its layoutmanager and constraints type as a
type argument could cater for a better typed but also more complex interface:
class JGenericPanel [ L <: LayoutManager[C],
C <: LayoutConstraint ] extends JComponent {
protected def layout: L
def add(comp: Component, constraints: C) = {
...
layout.addLayoutComponent(comp, constraints)
...
}
}
Furthermore, by using object composition for layout managers and containers we do not gain
any flexibility, as changing a layout manager inevitably results in resetting incompatib le
constraints for every child component. This results in the same amount of work as creating a
new container and readding all child components.
The above two arguments have led us to the following unified layout container interface:

trait LayoutContainer extends Container.Wrapper {


type Constraints <: AnyRef

val layout: Map[Component, Constraints] = ...

protected def constraintsFor(c: Component): Constraints


protected def areValid(c: Constraints): (Boolean, String)
protected def add(comp: Component, c: Constraints)
}
Components can be added to the layout map that associates them to their constraints,
which are of type Constraints.

5.4.2 Extending Layout Container


Concrete subclasses of LayoutContainer need to fix the abstract Contraints type and impleme nt
three additional methods to connect the wrapper to the underlying peer:
constraintsFor(c: Component): Constraints obtains the current constraints for the given
component
areValid(c: Constraints): (Boolean, String) performs a runtime check to determine whether
the given constraints are valid, and if not, provides an optional error message
add(comp: Component, c: Constraints) adds the given component with the given constraints
to this container
Subclass BorderPanel, for instance, assigns the Contraints type to a custom Position
enumeration and obtains the constraints from the underlying layout manager while performing
some casting operations. As there are only valid Position enumeration values, method areValid
does not need to perform any runtime checks and thus always returns (true,""). Adding a
component to a BorderPanel is, as in most cases, trivial and simply calls a JPanel.add method
with the appropriate arguments:

class BorderPanel extends Panel with LayoutContainer {


import BorderPanel._
def layoutManager = peer.getLayout.asInstanceOf [BorderLayout]
type Constraints = Position.Value

protected def constraintsFor(comp: Component) =


wrapPosition(layoutManager.getConstraints(comp.peer).asInstanceOf[String])

protected def areValid(c: Constraints): (Boolean, String) = (true, "")

protected def add(c: Component, l: Constraints) {


peer.add(c.peer, l.toString)
}
}

5.5 Reacting to events


Observing and reacting to events in scala.swing is based around the concepts of publishers and
reactors. Any scala.swing component is automatically both a publisher that publishes events
and a reactor that reacts to events. Let’s have a look how a simple Celsius to Fahrenheit
converter as depicted in Figure5.2 could be implemented.
First, we first create a frame with two identical text fields where the user can enter degrees in
Celsius and Fahrenheit:

Figure 5.2: A simple Celsius to Fahrenheit converter


object Converter extends SimpleSwingApplication {
def newField = new TextField {
text = "0"
columns = 5
}
val celsius = newField
val fahrenheit = newField
def top = new MainFrame {
title = "Convert Celsius / Fahrenheit"
contents = new FlowPanel(celsius, new Label(" Celsius = "),fahrenheit, new Label("
Fahrenheit"))
}
}
A text field is a publisher of events of type EditDone indicating that the user has just finished
editing the field. In order to react to those events, we need to add reactions to a reactor listening
to our text fields. We can put the following code inside our main object Converter extending
SimpleSwingApplication which is an application global reactor per default:

listenTo(fahrenheit, celsius)
reactions += {
case EditDone(‘fahrenheit‘) =>
val f = Integer.parseInt(fahrenheit.text)
val c = (f 32)* 5 / 9
celsius.text = c.toString
case EditDone(‘celsius‘) =>
val c = Integer.parseInt(celsius.text)
val f = c * 9 / 5 + 32
fahrenheit.text = f.toString
}
We first indicate that our main object is interested in events from each text field by calling
method listenTo from class Reactor, which is a base class of SimpleSwingApplication. Then
we add two reactions to EditDone events from text field celsius and text field Fahrenheit that
each update the contents of the other text field that has not been edited. Events are usually case
classes or classes with extractors, so that clients can easily pattern match on them as in the
example above.
The complete source code for this example can be found in the scala.swing test package.

5.5.1 Java Swing Listeners versus scala.swing Reactions


As we have seen above, events in scala.swing are dispatched in pattern matchings. As each
event is a normal Scala object, it has a unique type, which determines how to pattern match on
it. Contrast this to Java Swing, where some events are dispatched in two phases, according to
their type plus an additional listener method. The following example shows how to listen to
mouse click events in Java Swing.

new JComponent {
addMouseListener(new MouseAdapter {
@Override
def mouseClicked(e: MouseEvent) {
System.out.println("Mouse clicked at " + e.getPoint)
}
})
}
Note that we are using the convenience class MouseAdapter, which implements all methods
from interface MouseListener, in order to avoid implementing the other methods from
MouseListener.
This interface is one example were we first dispatch on the event’s Java type (MouseEvent)
and then refine the match by implementing one of the listener’s methods which all take the
same parameter (using listener MouseListener and method mouseClicked). The equivalent in
scala.swing, which matches on the event’s type, looks as follows.

new Component {
listenTo(mouse)
reactions += {
case e: MouseClicked =>
println("Mouse clicked at " + e.point)
}
}
For efficiency reasons, mouse events are not published by a component itself, but by its
member mouse. Therefore, we first have to listenTo the mouse publisher.

5.5.2 Publishers and Reactors


The entire publish/react system is implemented in classes Publisher, Reactor, and Reactions.
For API users, the only interesting method in Publisher is

def publish(e: Event)

which notifies all registered reactors. The sole purpose of trait Event for now is to indicate that
some class defines an event. To make an object a publisher, simply extend class Publisher and
call the above method to publish events. A reactor can be registered and deregistered to one or
more publishers with the following methods in class Reactor:

def listenTo(ps: Publisher*)


def deafTo(ps: Publisher*)

The third interesting member of class Reactor is


val reactions: Reactions

which represent a collection of reactions. A reaction is an object of type Reactions. Reaction,


which is an alias for PartialFunction[Event, Unit]. We have seen how to add reactions with the
method += above. The other interesting method -= of class Reaction removes a previous ly
registered reaction. Both method return the receiving reactions object:

def +=(r: Reactions.Reaction): this.type


def -=(r: Reactions.Reaction): this.type
This fact allows clients to syntactically concatenate additions and removals of single reactions.

5.5.3 Actions
Java Swing provides an interface javax.swing.Action which encapsulates code to be executed
as a reaction to an event. Different from a listener, though, an action also captures informa tio n
that can be useful for several types of user interface components. Often used properties are an
action’s name that usually manifests itself as a button’s or menu item’s title and an action’s
mnemonic key which is used as a keyboard shortcut for various kinds of buttons and menu
items.
In scala.swing, we represent actions by the Action trait that is wrapper around it’s Java Swing
peer similar to components. The Action companion object defines several convenie nce
members. The following method lets clients create actions very conveniently.
def apply(title: String)(block: =>Unit) = new Action(title) {
def apply() { block }
}
We can create an action by writing:

Action("Run me") {
println("Someone executed this action.")
}

A component that can be associated with an action extends trait Action.Triger. The following
shows one way to associate a click button with an action.
val button = new Button {
action = Action("Click me") {
println("Someone executed clicked button " + this)
}
}
As it is very common for buttons to have actions, there is a convenience factory method in
companion object Button. The following is equivalent to the previous example.

val button = Button("Click me") {


println("Someone executed clicked button " + this)
}
A component with an action can use as much information from that action as it wants. Buttons
and menu items usually use their action’s body, name, accelerator key, icon and so on, whereas
a text field might use the action’s body only. To tell a component that it does not have an
associated action, one must use the Action.NoAction object which is the default value for a
component’s action.
button.action = Action.NoAction

A component with a NoAction uses it’s own properties instead of those of the action. In Java
Swing, the equivalent to NoAction is null.

5.6 List Views and Tables


List views and tables are components that show an arbitrary number of items in a unifo r m
arrangement. List views show them in a simple horizontal or vertical arrangement, each item
often rendered as a simple string, as depicted in Figure 5.3. Tables arrange elements in a grid
and optionally show row and column headers as depicted in Figure 5.4.

5.6.1 Class ListView


Class ListView has a convenience constructor that takes a sequence of items. A list view can
be instantiated as follows:
val items = List("Lausanne", "Paris", "New York", "Berlin", "Tokio")
val view = new ListView(items)
Figure 5.3: A ListView component

Figure 5.4: A Table component

In contrast to Java Swing, ListView and its constructor are polymorphic in the type of items:
class ListView[A] extends Component {
def this(items: Seq[A]) = ...
...
}
ListView defines a member selection that lets clients query information about the current
selection, for instance, the sequence of currently selected items. In order to see how we can
take advantage of class ListView being generic, we modify the above example slightly:
case class City(name: String, country: String, population: Int, capital: Boolean)
val items = List(City("Lausanne", "Switzerland", 129273, false),
City("Paris", "France", 2203817, true),
City("New York", "USA", 8363710 , false),
City("Berlin", "Germany", 3416300, true),
City("Tokio", "Japan", 12787981, true))
val view = new ListView(items)

We can now query the currently selected items and receive a sequence of City objects we
can immediately operate on:

val cityNames = view.selection.items.map(_.name)

In Java Swing, we would have to perform casts or go through the list of selected indices, both
leading to more error-prone and less concise code.

5.6.2 Renderers
If we actually take the list view code showing city objects from above, and put it inside a frame ,
we will get the result shown in Figure 5.5.
Figure 5.5: A ListView component showing a list of City objects

The reason is that by default, a list view calls toString on its items and displays each as a label
containing that string. Since for a case class such as City, method toString returns a string
composed of the class name and its constructor arguments.

Technically, class ListView uses an instance of class ListView.Renderer to render each item.
Companion object ListView contains a number of renderer related member that are useful to
clients. Object GenericRenderer is just a wrapper for the default renderer imposed by Java
Swing and calls toString for each item as mentioned above.

Companion object Renderer defines the following composition method:


def apply[A,B](f: A => B)(implicit renderer: Renderer[B]): Renderer[A]

It returns a renderer for item types A that uses an implicitly supplied renderer for item types B.
The resulting renderer obtains items of type B by applying function f to each item. Using this
function and the implicit GenericRenderer object, we can create a better city view:

import ListView._
val view = new ListView(items) {
renderer = Renderer(_.name)
}

The last useful class from object ListView is class AbstractRenderer that can be used to create
more elaborate item views. Its constructor takes a component whose paintComponent method
used to draw an item:

abstract class AbstractRenderer[-A, C<:Component](protected val component: C) extends


Renderer[A]

Clients need to configure that component for each item by implementing the following method:

def configure(list: ListView[_], isSelected: Boolean, focused: Boolean, a: A, index: Int)

Method configure for a view showing items of class Item could be implemented as follows:

def configure(list: ListView[_], isSelected: Boolean, focused: Boolean,icon: Icon, index: Int)
{
component.icon = icon
component.xAlignment = Alignment.Center
if(isSelected) {
component.border = Swing.LineBorder(list.selectionBackground, 3)
} else {
component.border = Swing.EmptyBorder(3)
}
}
A similar example can be found in the combo boxes demo from the sample package.

5.6.3 Class Table


The design of class Table and its renderers are similar to that of ListView and its renderers.
Class Table, however, is not tailored to a specific usage pattern and can be used to show lists
of items as well as more general grid data structures such as a two dimensional array, for
instance in a spreadsheet. Hence, an item for a Table could represent a row, a column, or single
cell. Consequently, class Table is not generic.

5.7 CustomPainting
Custom component painting in scala.swing is very similar to Java Swing. Most users that want
to perform custom painting inside a component will want to instantiate Component or one of
its subclasses and override method paintComponent. Here is how it is done in the LinePainting
demo from the scala.swing sample package:
new Component {
...
override def paintComponent(g: Graphics2D) = {
super.paintComponent(g)
g.setColor(new Color(100,100,100))
g.drawString("Press left mouse button and drag to paint.",10, size.height10)
g.setColor(Color.black)
g.draw(path)
}
}
This calls super.paintComponent() to make sure the component draws its background and
decorations properly. It then draws a custom string and a path that is maintained by the demo
while the user drags the mouse.
The available paint methods provided in class Component are
protected def paint(g: Graphics2D)
protected def paintBorder(g: Graphics2D)
protected def paintChildren(g: Graphics2D)
protected def paintComponent(g: Graphics2D)

Method paint is called by the Swing painting mechanism to draw a component and in its
standard implementation invokes the other three methods to paint the component specific
decorations, its border, and its children, respectively. Note that these methods take a
Graphics2D object in contrast to corresponding methods in javax.swing.Component which take
the Graphics object. Internally, Swing deals with Graphics2D objects in any event. The
scala.swing signatures eliminate the need to perform the common cast
g.asInstanceOf[Graphics2D] to access the newer interface of class Graphics2D.
5.8 Panels
The JPanel class provides general-purpose containers for lightweight components. By default,
panels do not add colors to anything except their own background; however, you can easily
add borders to them and otherwise customize their painting.
In many types of look and feel, panels are opaque by default. Opaque panels work well as
content panes and can help with painting efficiently. You can change a panel's transparency by
invoking the setOpaque method. A transparent panel draws no background, so that any
components underneath show through.
Like other containers, a panel uses a layout manager to position and size its components. By
default, a panel's layout manager is an instance of FlowLayout , which places the panel's contents
in a row. You can easily make a panel use any other layout manager by invoking the setLayout
method or by specifying a layout manager when creating the panel. The latter approach is
preferable for performance reasons, since it avoids the unnecessary creation of a FlowLayout
object.

Here is an example of how to set the layout manager when creating the panel.
package net.codejava.swing.jpanel;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

/**
* This program demonstrates how to use JPanel in Swing.
* @author www.codejava.net
*/
public class SwingJPanelDemo extends JFrame {

private JLabel labelUsername = new JLabel("Enter username: ");


private JLabel labelPassword = new JLabel("Enter password: ");
private JTextField textUsername = new JTextField(20);
private JPasswordField fieldPassword = new JPasswordField(20);
private JButton buttonLogin = new JButton("Login");

public SwingJPanelDemo() {
super("JPanel Demo Program");

// create a new panel with GridBagLayout manager


JPanel newPanel = new JPanel(new GridBagLayout());

GridBagConstraints constraints = new GridBagConstraints();


constraints.anchor = GridBagConstraints.WEST;
constraints.insets = new Insets(10, 10, 10, 10);

// add components to the panel


constraints.gridx = 0;
constraints.gridy = 0;
newPanel.add(labelUsername, constraints);

constraints.gridx = 1;
newPanel.add(textUsername, constraints);

constraints.gridx = 0;
constraints.gridy = 1;
newPanel.add(labelPassword, constraints);

constraints.gridx = 1;
newPanel.add(fieldPassword, constraints);

constraints.gridx = 0;
constraints.gridy = 2;
constraints.gridwidth = 2;
constraints.anchor = GridBagConstraints.CENTER;
newPanel.add(buttonLogin, constraints);

// set border for the panel


newPanel.setBorder(BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(), "Login Panel"));

// add the panel to this frame


add(newPanel);

pack();
setLocationRelativeTo(null);
}

public static void main(String[] args) {


// set look and feel to the system look and feel
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
ex.printStackTrace();
}

SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new SwingJPanelDemo().setVisible(true);
}
});
}
}

Reference: https://www.codejava.net/java-se/swing/jpanel-basic-tutorial-and-examples

5.9 Concurrency in Java Swing


Java Swing Programming is mainly used for developing GUI for desktop applications. To
create responsive UI, Swing employs threads.
The types of threads that Swing deals with are:

 Initial Threads – Threads which execute initial application code. This is where the
application logic begins.
 Event Dispatch Thread (EDT) – Threads where all event handling code is executed.
Event handling code is the major part of code in Swing programming that interacts with
Swing framework.
 Worker Threads / Background Threads – Threads where time consuming
background tasks (long-running tasks) are executed.

We do not need to explicitly provide code for creating these threads; Swing framework or run
time provides them. We can just utilize these threads to create a responsive Swing program.

5.9.1 Initial Threads


In Swing programming, initial threads create a Runnable object that initializes the GUI and
schedules that object for execution on the Event Dispatch thread. If the GUI is once created,
the program is mainly driven by GUI events.

An initial thread schedules the GUI creation task by invoking


javax.swing.SwingUtilities.invokeLater or javax.swing.SwingUtilities.invokeAndWait. Both
these methods take a single argument; the Runnable defines the new task. invokeLater simply
schedules the task and returns; invokeAndWait waits for the task to finish before returning.

Example :
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new MainFrame("SwingWorker Demo");
}
});
SwingUtilities.invokeAndWait(new Runnable(){
@Override
public void run(){
new MainFrame("InvokeAndWait Example");
}
});

5.9.2 Event Dispatch Thread


Almost all code that creates or interacts with Swing components must run on the event dispatch
thread. Swing Event handling code, runs on this special thread. Most code that invokes Swing
methods also runs on this thread. This is needed because most Swing object methods are not
‘thread safe’. In API specification, some Swing component methods are labelled ‘thread safe';
these can be safely invoked from any thread. All other Swing component methods must be
invoked from the event dispatch thread.

Code running on the event, dispatches thread as a series of short tasks. Most tasks are
invocations of event-handling methods, such as ActionListener.actionPerformed. Other tasks
can be scheduled by application code, using invokeLater or invokeAndWait. Tasks on the event
dispatch thread must finish quickly; otherwise, unhandled events back up and the UI becomes
unresponsive.

javax.swing.SwingUtilities.isEventDispatchThread is used to determine whether our code is


running the event dispatch thread.

Example:

private JLabel countLabel1 = new JLabel("0");


private JButton startButton = new JButton("Start");
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
start();
}
});
private void start() {
Thread worker = new Thread(){
public void run(){
for(int i=0; i<=10; i++) {
countLabel1.setText(Integer.toString(i));
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {

}
}
statusLabel.setText("Completed.");
System.out.println("Invoke and Wait...");
}

};

worker.start();
}

5.9.3 Worker Threads and SwingWorker

Worker threads are also known as background threads. For executing long-running tasks,
Swing program uses worker threads.

Tasks that run on Worker threads are created using javax.swing.SwingWorker. Each task
running on a worker thread is represented by an instance of this class. Communication and
coordination between worker thread tasks and the tasks on other threads are featured by this
class.
SwingWorker is an abstract class; we must define a subclass in order to create a SwingWorker
object; anonymous inner classes are often useful for creating very simple SwingWorker objects.

Important point to note: For each new background task, a new instance of
javax.swing.SwingWorker is needed. The instances are not reusable.

Communication & Control features of SwingWorker

 The SwingWorker subclass defines a method, done, which is automatically invoked on


the event dispatch thread when the background task is finished.
 SwingWorker implements util.concurrent.Future. This interface allows the background
task to provide a return value to the other thread. Other methods in this interface allow
cancellation of the background task and finding whether the background task has
finished or been cancelled.
 Intermediate results can be provided by the background task by invoking publish, which
causes SwingWorker.process to be invoked from the event dispatch thread.

All concrete subclasses of SwingWorker implement doInBackground; implementation of done


is optional.

SwingWorker is a generic class, with two type parameters. The first type parameter specifies a
return type for doInBackground, and also for the get method, which is invoked by other threads
to retrieve the object returned by doInBackground. SwingWorker's second type parameter
specifies a type for interim results returned while the background task is still active. Void can
be used as a placeholder, if our code doesn't return any interim results.
MODULE 6:
THE SOFTWARE DEVELOPMENT PROCESS

6.1 The Software Life Cycle


Program development is usually broken up into a number of phases: requirements analysis,
design, implementation and testing, acceptance testing, production, and modification and
maintenance. The process typically begins with someone we shall call the customer who wants
a program to provide a particular service. Sometimes the service is well understood and
described a priori in a complete and precise manner, but this is quite rare. More often customers
do not fully understand what they want the program to do. Even if the desired service is well
understood, it is probably not described precisely enough to serve as a basis for constructing a
program. The purpose of the requirements analysis phase is to analyze the needs of the
customer and produce a document describing a program that will meet those needs. This
process will require communication with the customer to make sure the needs are understood.

The document that results from requirements analysis is the input to the design phase. In this
phase, a modular decomposition of a program satisfying the specification is developed. In the
next phase, the individual modules are implemented and then tested to ensure that they perform
as intended. Unit test is used in which individual modules are tested in isolation, and integratio n
tests are those in which modules are tested in combination.
At best, integration testing shows that the modules together satisfy the implementor's
interpretation of the specification. The implementor may have misinterpreted the specifica tio n
or neglected to test some portion of the program's behavior, though, and the customer therefore
needs some other basis for deciding whether or not the program does what it is supposed to do.
This typically takes the form of acceptance tests. Acceptance tests provide an evaluation of the
program behavior that is independent of the design, and they are generally performed by an
organization other than the one that worked on the design and implementation. They should
include both trial runs under conditions approximating those the customer will actually
encounter and tests derived directly from the requirements specification.
When the program has passed the acceptance tests, it enters the production phase and becomes
a product that the customer can use. The useful life of the program occurs during this phase,
but the program is unlikely to remain unchanged even here. First, it almost certainly harbors
undetected errors that must be corrected during production. Correcting such errors is called
program maintenance. Second, the customer's requirements are likely to change. Responding
to such changes requires program modification.
Figure 6.1 illustrates the waterfall model, an idealized form of the software developme nt
process previously described, in which each phase is completed before work starts on the next
phase. The waterfall model is neither realistic nor practical: the software development process
is unlikely to proceed sequentially through the phases. There are two reasons for this. First,
some work can be done in parallel. The second reason that development isn’t entirely
sequential is errors, which may make it necessary to back to an earlier stage of the process.
Figure 6.2 illustrates the more realistic spiral model. In this model, phases can start before their
predecessor phase is complete, and the process includes many feedback loops.

6.2 Requirement Analysis


Recall that a program is developed to satisfy the needs of a customer. The customer might be
someone outside of the organization that will develop the program, or it might be someone on
the inside. Products are frequently developed in expectation of customers before any actual
customer exists. In this case, some group in the development organization needs to act as a
kind of "model" of the customer, whose needs are identified through market analysis.

The original product description produced by a customer is unlikely to be either complete or


precise. The purpose of requirements analysis is to analyze the customer's needs so that we can
identify and carefully describe the customer's requirements. This analysis must involve
consultation, since the customer is the ultimate judge of what is wanted.

A good way to get started on requirements analysis is to examine how the customer does things
at present. Almost always a product is intended to replace an existing system, possibly non-
computerized, or consisting of some combination of programs and external processes, or even
a program in which the performance was not satisfactory. The current system will contain
methods for normal processing and for coping with errors and a variety of contingencies. It can
be a source of ideas not only about how to do things, but about what needs to be done.
Additionally, the product must be compatible with other systems already in use. Studying the
environment in which the new product will be used can help ensure that it will fit in smoothly.
For example, studying the customer's organization will help insure that the program will fit
well into that organization and will be compatible with current practices.

Requirements analysis must consider both normal situations and errors. Studying normal case
behavior means defining the effect of all non-erroneous user interactions with a program under
the assumption that the program is in a normal state.
Cases with no errors represent only a small part of program behavior, however, and it is
essential to consider and describe how a program behaves in the presence of errors. This part
of the analysis should never be neglected or underemphasized. The analyst must try to uncover
all possible errors that might occur and develop the appropriate responses for each case. Errors
come from two sources: users interacting with the program, and hardware and software
malfunctions.
A good approach to studying both normal case behavior and behavior in the presence of user
errors is to work out scenarios. A scenario is step-by-step walk-through of an interaction with
the system, consisting of use/response pairs: the user or environment requests some action,
leading to a particular response by the system.

As far as software errors are concerned, the analyst must decide how much effort should be
expended to detect and cope with such errors. For example, if it is important to limit the scope
of software errors, the output of critical modules can be checked for reasonableness, and the
system shut down if the checks fail; the shutdown might simply terminate processing until the
problem is fixed, or it might be followed by a restart in a clean state, which is often suffic ie nt
for continued service. If this approach is taken, it is important to log information about failures
so that information about the errors that led to them is not lost.
The analyst must also decide what to do about hardware failures. It may be important for a
system to be highly available; that is, it should be highly likely that the system is up and running
all the time (or at certain times) Satisfying such a requirement may involve the use of redundant
hardware and software. A related requirement is that the system be highly reliable. The main
concern is to avoid loss of information because of failures. Determining how ambitious the
system must be in trying to recover from hardware and software malfunctions is an important
aspect of requirements analysis.
In addition to functional requirements, a program must satisfy performance requirements.
These requirements should be considered as the functional requirements are developed, and
also on their own, just to double-check that nothing has been forgotten. Time and space
efficiency should be considered together since it is often necessary to trade one off against the
other.
The customer's space and time requirements must be checked for compatibility with the
functional requirements, the hardware the customer intends to use, and the price the customer
is willing to pay for the system. For example, the customer may want some activity to satisfy
performance requirements that are either not possible given the hardware or can be satisfied
only with very sophisticated software. If an incompatibility is discovered, negotiations may
be necessary to produce new requirements.
These issues affect the content of the requirements specification directly. A number of other
issues should be considered during requirements analysis, not because they affect the
specification, but because they provide useful input for the designers. Two such issues are
modifiability and reusability. There will usually be areas with fixed requirements, and others
in which changes are likely. Information about likely changes is useful because a design can
be shaped in such a way as to make certain changes easy.
Pinning down constraints on the delivery schedule is another important part of requireme nts
analysis. Knowing that the customer is in a hurry, for example, may encourage the designer
of the software to trade noncritical features for simplicity.

The result of the requirements phase is a requirements document. This document contains the
requirements specification, which describes the program behavior, including its behavior in the
presence of errors. In addition, the document should explain the performance requirements, the
decisions made during analysis, and if it can be done with a reasonable amount of effort, the
alternatives that were rejected (and why they were rejected). The latter information is useful
when requirements must be rethought because of errors or changing customer needs.
The requirements document can be the input to two activities in addition to the design. It can
be used to produce acceptance tests and as a basis for a system user's manual. The user's manual
is something that must be produced anyway, but its production can provide an independent
check on the suitability of the specification. If the system is hard to use, this may be evident
when the manual is written. Also, by reading the manual, the customer may notice deficienc ies
in the specification that were overlooked earlier.
6.3 Requirement Specification
A program is a data object, and its specification will be similar to those for abstract types.
Specifications for abstract types rely on the overview section to define a model for the states
of their objects. In those specifications, we were able to make use of simple models that were
based on a small set of mathematical concepts. For requirements specifications, however, such
simple models aren't sufficient. Now we need to model the state of an entire program. This
state often has a complex structure, even when we limit our concerns to just that part of the
structure that is visible to users of the program. For example, users of a file system need to
understand about files and directories and how they are connected to one another.
Thus, we need a way to describe the program state. We can define the program state by means
of a data model. The model is then used in the requirements specification.
6.3.1 Data Models
A data model consists of a graph and a textual description. The graph defines the kinds of data
being manipulated and how they are related to one another. The graph and textual description
together define constraints on what the program does. Defining these constraints forces us to
pay attention to details that might otherwise be overlooked during requirements analysis.
The graph contains nodes and edges. The nodes represent the kinds of data being manipula ted
by a program. Each node is a named set of items. For example, a data model for a file system
would contain a File node, representing files, and a Dir node, representing directories. Each set
contains all items that exist at a particular moment (e.g., all files in existence at this time).
The items in the sets are structureless; no detail is given for them, except by means of
relationships to other sets. The edges represent these relationships. The purpose of the graph is
to provide a convenient pictorial mechanism for showing the relationships. The notation also
allows the relationships to be constrained in ways that will be explained later. Thus, a graph
expresses certain kinds of invariants.
The model describes the state of the system. This state may change over time (e.g., as a result
of the program responding to a user request). The model expresses the mutability in two ways.
First, the sets themselves can change: as the program state changes, items may be added to or
removed from sets. Second, the relationship between the sets can change.

6.3.1.1 Subsets
Some sets represented by nodes in the graph are subsets of other sets. We will call sets that
have no supersets domains. Each domain is disjoint from all other domains.
Subset edges are used to indicate that one set is a subset of another. We represent this
information with an arrow with a closed head. The arrow goes from the subset to the superset.
The arrowhead indicates whether the subset contains all elements of its superset (a filled
arrowhead) or just some elements of the superset (an unfilled arrowhead).
Subsets can share an arrow; in this case, they are mutually disjoint, and the arrowhead indicates
whether or not their union exhausts the superset. Subsets that don't share an arrow are not
necessarily disjoint.
Three constraints are useful to define for subsets. First, subsets can sometimes be fixed. This
means that the subset’s membership is fixed for all time; the subset never gains or loses
elements. A fixed subset is indicated by double lines on both sides of its node.

6.3.1.2 Relations
6.3.2 Requirements Specification
6.4 Designs

Potrebbero piacerti anche