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.