Think Java: How to Think Like a Computer Scientist by Allen B. Downey - HTML preview

PLEASE NOTE: This is an HTML preview only and some elements such as links or page numbers may be incorrect.
Download the book in PDF, ePub, Kindle for a complete version.

Chapter 15

Object-oriented programming

15.1

Programming languages and styles

There are many programming languages and almost as many programming

styles (sometimes called paradigms). The programs we have written so far

are procedural, because the emphasis is on specifying computational pro-

cedures.

Most Java programs are object-oriented, which means that the focus is

on objects and their interactions. Here are some of its characteristics of

object-oriented programming:

❼ Objects often represent entities in the real world. In the previous chap-

ter, creating the Deck class was a step toward object-oriented program-

ming.

❼ The majority of methods are object methods (like the methods you

invoke on Strings) rather than class methods (like the Math methods).

The methods we have written so far have been class methods. In this

chapter we write some object methods.

❼ Objects are isolated from each other by limiting the ways they interact,

especially by preventing them from accessing instance variables without

invoking methods.

194

Chapter 15. Object-oriented programming

❼ Classes are organized in family trees where new classes extend existing

classes, adding new methods and replacing others.

In this chapter I translate the Card program from the previous chapter from

procedural to object oriented style. You can download the code from this

chapter from http://thinkapjava.com/code/CardSoln3.java.

15.2

Object methods and class methods

There are two types of methods in Java, called class methods and ob-

ject methods. Class methods are identified by the keyword static in the

first line. Any method that does not have the keyword static is an object

method.

Although we have not written object methods, we have invoked some. When-

ever you invoke a method “on” an object, it’s an object method. For example,

charAt and the other methods we invoked on String objects are all object

methods.

Anything that can be written as a class method can also be written as an

object method, and vice versa. But sometimes it is more natural to use one

or the other.

For example, here is printCard as a class method:

public static void printCard(Card c) {

System.out.println(ranks[c.rank] + " of " + suits[c.suit]);

}

Here it is re-written as an object method:

public void print() {

System.out.println(ranks[rank] + " of " + suits[suit]);

}

Here are the changes:

1. I removed static.

2. I changed the name of the method to be more idiomatic.

3. I removed the parameter.

15.3. The toString method

195

4. Inside an object method you can refer to instance variables as if they

were local variables, so I changed c.rank to rank, and likewise for

suit.

Here’s how this method is invoked:

Card card = new Card(1, 1);

card.print();

When you invoke a method on an object, that object becomes the current

object, also known as this. Inside print, this refers to the card the method

was invoked on.

15.3

The toString method

Every object type has a method called toString that returns a string repre-

sentation of the object. When you print an object using print or println,

Java invokes the object’s toString method.

The default version of toString returns a string that contains the type of

the object and a unique identifier (see Section 11.6). When you define a

new object type, you can override the default behavior by providing a new

method with the behavior you want.

For example, here is a toString method for Card:

public String toString() {

return ranks[rank] + " of " + suits[suit];

}

The return type is String, naturally, and it takes no parameters. You can

invoke toString in the usual way:

Card card = new Card(1, 1);

String s = card.toString();

or you can invoke it indirectly through println:

System.out.println(card);

196

Chapter 15. Object-oriented programming

15.4

The equals method

In Section 13.4 we talked about two notions of equality: identity, which

means that two variables refer to the same object, and equivalence, which

means that they have the same value.

The == operator tests identity, but there is no operator that tests equiva-

lence, because what “equivalence” means depends on the type of the objects.

Instead, objects provide a method named equals that defines equivalence.

Java classes provide equals methods that do the right thing. But for user

defined types the default behavior is the same as identity, which is usually

not what you want.

For Cards we already have a method that checks equivalence:

public static boolean sameCard(Card c1, Card c2) {

return (c1.suit == c2.suit && c1.rank == c2.rank);

}

So all we have to do is rewrite is as an object method:

public boolean equals(Card c2) {

return (suit == c2.suit && rank == c2.rank);

}

Again, I removed static and the first parameter, c1. Here’s how it’s invoked:

Card card = new Card(1, 1);

Card card2 = new Card(1, 1);

System.out.println(card.equals(card2));

Inside equals, card is the current object and card2 is the parameter, c2.

For methods that operate on two objects of the same type, I sometimes use

this explicitly and call the parameter that:

public boolean equals(Card that) {

return (this.suit == that.suit && this.rank == that.rank);

}

I think it improves readability.

Exercise 15.1. Download http: // thinkapjava. com/ code/ CardSoln2.

java and http: // thinkapjava. com/ code/ CardSoln3. java .

CardSoln2.java contains solutions to the exercises in the previous chapter.

It uses only class methods (except the constructors).

15.5. Oddities and errors

197

CardSoln3.java contains the same program, but most of the methods are

object methods. I left merge unchanged because I think it is more readable as

a class method.

Transform merge into an object method, and change mergeSort accordingly.

Which version of merge do you prefer?

15.5

Oddities and errors

If you have object methods and class methods in the same class, it is easy to

get confused. A common way to organize a class definition is to put all the

constructors at the beginning, followed by all the object methods and then

all the class methods.

You can have an object method and a class method with the same name, as

long as they do not have the same number and types of parameters. As with

other kinds of overloading, Java decides which version to invoke by looking

at the arguments you provide.

Now that we know what the keyword static means, you have probably

figured out that main is a class method, which means that there is no “current

object” when it is invoked.

Since there is no current object in a class method, it is an error to use the

keyword this. If you try, you get an error message like: “Undefined variable:

this.”

Also, you cannot refer to instance variables without using dot notation and

providing an object name. If you try, you get a message like “non-static vari-

able... cannot be referenced from a static context.” By “nonstatic variable”

it means “instance variable.”

15.6

Inheritance

The language feature most often associated with object-oriented program-

ming is inheritance. Inheritance is the ability to define a new class that is

a modified version of an existing class.

198

Chapter 15. Object-oriented programming

Extending the metaphor, the existing class is sometimes called the parent

class and the new class is called the child.

The primary advantage of this feature is that you can add methods and

instance variables without modifying the parent. This is particularly useful

for Java classes, since you can’t modify them even if you want to.

If you did the GridWorld exercises (Chapters 5 and 10) you have seen exam-

ples of inheritance:

public class BoxBug extends Bug {

private int steps;

private int sideLength;

public BoxBug(int length) {

steps = 0;

sideLength = length;

}

}

BoxBug extends Bug means that BoxBug is a new kind of Bug that inherits

the methods and instance variables of Bug. In addition:

❼ The child class can have additional instance variables; in this example,

BoxBugs have steps and sideLength.

❼ The child can have additional methods; in this example, BoxBugs have

an additional constructor that takes an integer parameter.

❼ The child can override a method from the parent; in this example, the

child provides act (not shown here), which overrides the act method

from the parent.

And if you did the Graphics exercises in Appendix A, you saw another ex-

ample:

public class MyCanvas extends Canvas {

public void paint(Graphics g) {

g.fillOval(100, 100, 200, 200);

}

}

15.7. The class hierarchy

199

MyCanvas is a new kind of Canvas with no new methods or instance variables,

but it overrides paint.

If you didn’t do either of those exercises, now is a good time!

15.7

The class hierarchy

In Java, all classes extend some other class. The most basic class is called

Object. It contains no instance variables, but it provide the methods equals

and toString, among others.

Many classes extend Object, including almost all of the classes we have

written and many Java classes, like java.awt.Rectangle. Any class that

does not explicitly name a parent inherits from Object by default.

Some

inheritance

chains

are

longer,

though.

For

example,

javax.swing.JFrame extends java.awt.Frame, which extends Window,

which extends Container, which extends Component, which extends Object.

No matter how long the chain, Object is the common ancestor of all classes.

The “family tree” of classes is called the class hierarchy. Object usually

appears at the top, with all the “child” classes below. If you look at the

documentation of JFrame, for example, you see the part of the hierarchy

that makes up JFrame’s pedigree.

15.8

Object-oriented design

Inheritance is a powerful feature. Some programs that would be complicated

without it can be written concisely and simply with it. Also, inheritance can

facilitate code reuse, since you can customize the behavior of existing classes

without having to modify them.

On the other hand, inheritance can make programs hard to read. When you

see a method invocation, it can be hard to figure out which method gets

invoked.

Also, many of the things that can be done with inheritance can be done

as well or better without it. A common alternative is composition, where

200

Chapter 15. Object-oriented programming

new objects are composed of existing objects, adding new capability without

inheritance.

Designing objects and the relationships among them is the topic of object-

oriented design, which is beyond the scope of this book. But if you are

interested, I recommend Head First Design Patterns, published by O’Reilly

Media.

15.9

Glossary

object method: A method that is invoked on an object, and that operates

on that object, which is referred to by the keyword this in Java or “the

current object” in English. Object methods do not have the keyword

static.

class method: A method with the keyword static. Class methods are not

invoked on objects and they do not have a current object.

current object: The object on which an object method is invoked. Inside

the method, the current object is referred to by this.

this: The keyword that refers to the current object.

implicit: Anything that is left unsaid or implied. Within an object method,

you can refer to the instance variables implicitly(without naming the

object).

explicit: Anything that is spelled out completely. Within a class method,

all references to the instance variables have to be explicit.

15.10

Exercises

Exercise 15.2. Transform the following class method into an object method.

public static double abs(Complex c) {

return Math.sqrt(c.real * c.real + c.imag * c.imag);

}

Exercise 15.3. Transform the following object method into a class method.

15.10. Exercises

201

public boolean equals(Complex b) {

return(real == b.real && imag == b.imag);

}

Exercise 15.4. This exercise is a continuation of Exercise 11.3. The purpose

is to practice the syntax of object methods and get familiar with the relevant

error messages.

1. Transform the methods in the Rational class from class methods to

object methods, and make the necessary changes in main.

2. Make a few mistakes. Try invoking class methods as if they were object

methods and vice-versa. Try to get a sense for what is legal and what

is not, and for the error messages that you get when you mess things

up.

3. Think about the pros and cons of class and object methods. Which

is more concise (usually)? Which is a more natural way to express

computation (or, maybe more fairly, what kind of computations can be

expressed most naturally using each style)?

Exercise 15.5. The goal of this exercise is to write a program that generates

random poker hands and classifies them, so that we can estimate the proba-

bility of the various poker hands. If you don’t play poker, you can read about

it here http: // en. wikipedia. org/ wiki/ List_ of_ poker_ hands .

1. Start with http: // thinkapjava. com/ code/ CardSoln3. java . and

make sure you can compile and run it.

2. Write a definition for a class named PokerHand that extends Deck.

3. Write a Deck method named deal that creates a PokerHand, transfers

cards from the deck to the hand, and returns the hand.

4. In main use shuffle and deal to generate and print four PokerHands

with five cards each. Did you get anything good?

5. Write a PokerHand method called hasFlush returns a boolean indicat-

ing whether the hand contains a flush.

6. Write a method called hasThreeKind that indicates whether the hand

contains Three of a Kind.

202

Chapter 15. Object-oriented programming

7. Write a loop that generates a few thousand hands and checks whether

they contain a flush or three of a kind. Estimate the probability of

getting one of those hands. Compare your results to the probabilities at

http: // en. wikipedia. org/ wiki/ List_ of_ poker_ hands .

8. Write methods that test for the other poker hands. Some are easier

than others. You might find it useful to write some general-purpose

helper methods that can be used for more than one test.

9. In some poker games, players get seven cards each, and they form a

hand with the best five of the seven. Modify your program to generate

seven-card hands and recompute the probabilities.