Chapter 15 Object-oriented programming15.1 Programming languages and stylesThere 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 has been on specifying computational procedures. Most Java programs are object-oriented, which means that the focus is on objects and their interactions. Here are some of the characteristics of object-oriented programming:
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/Card3.java. 15.2 Object methods and class methodsThere are two types of methods in Java, called class methods and object 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. Whenever 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:
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, the keyword this refers to the card the method was invoked on. 15.3 The toString methodEvery object type has a method called toString that returns a string representation 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); 15.4 The equals methodIn 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 equivalence, 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. 15.5 Oddities and errorsIf 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 variable... cannot be referenced from a static context.” By “non-static variable” it means “instance variable.” 15.6 InheritanceThe language feature most often associated with object-oriented programming is inheritance. Inheritance is the ability to define a new class that is a modified version of an existing class. 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 examples 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:
If you did the Graphics exercises in Appendix A, you saw another example: public class MyCanvas extends Canvas {
public void paint(Graphics g) {
g.fillOval(100, 100, 200, 200);
}
} 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 hierarchyIn Java, all classes extend some other class. The most basic class is called Object. It contains no instance variables, but it provides 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 much 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 designInheritance 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 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
15.10 ExercisesExercise 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). 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? Exercise 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 3
Transform the following object method into a class method. public boolean equals(Complex b) {
return(real == b.real && imag == b.imag);
} Exercise 4 This exercise is a continuation of Exercise 3. The purpose is to practice the syntax of object methods and get familiar with the relevant error messages.
Exercise 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
probability 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.
|
Like this book?
Are you using one of our books in a class?We'd like to know about it. Please consider filling out this short survey.
|