CarrCh02v2

CarrCh02v2 - C H A P T E R 2 Creating Classes from Other...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: C H A P T E R 2 Creating Classes from Other Classes CONTENTS Composition Adapters Inheritance Invoking Constructors from Within Constructors Private Fields and Methods of the Base Class Overriding and Overloading Methods Protected Access Multiple Inheritance Type Compatibility and Base Classes The Class Object Abstract Classes and Methods Polymorphism PREREQUISITES Chapter 1 Java Classes OBJECTIVES After studying this chapter, you should be able to G G G G G G Use an instance of an existing class in the definition of a new class Use inheritance to derive a new class from an existing class Override and overload method definitions Describe the purpose and methods of the class Object Describe the purpose of an abstract class Determine which overridden method an object invokes A major advantage of object-oriented programming is the ability to use existing classes when defining new classes. That is, you use classes that you or someone else 29 30 CHAPTER 2 Creating Classes from Other Classes has written to create new classes, rather than reinventing everything yourself. We begin this chapter with two ways to accomplish this feat. In the first way, you simply declare an instance of an existing class as a data field of your new class. In fact, you have done this already if you have ever defined a class that had a string as a data field. Since your class is composed of objects, this technique is called composition. The second way is to use inheritance, whereby your new class inherits properties and behaviors from an existing class, augmenting or modifying them as desired. This technique is more complicated than composition, so we will devote more time to it. As important as inheritance is in Java, you should not ignore composition as a valid and desirable technique in many situations. Both composition and inheritance define a relationship between two classes. These relationships are often called, respectively, has a and is a relationships. You will see why when we discuss them in this chapter. Polymorphism is another key feature of object-oriented programming. In fact object-oriented programming is usually described in terms of its main features: encapsulation, inheritance, and polymorphism. Used in conjunction with inheritance, polymorphism enables different objects to call methods having the same name and get different actions. Composition 2.1 Chapter 1 introduced you to the class Name to represent a person’s name. It defined constructors, accessor methods, and mutator methods that involved the first and last names. The data fields in Name are instances of the class String. A class uses composition when it has a data field that is an instance of another class. And since the class Name has an instance of the class String as a data field, the relationship between Name and String is called a has a relationship. Let’s create another class that uses composition. Consider a class of students, each of whom has a name and an identification number. Thus, the class Student contains two instances of objects as data fields: an instance of the class Name and an instance of the class String: private Name fullName; private String id; Figure 2-1 shows an object of type Student. Notice that it contains a object and that the Name object contains two String objects. Figure 2-1 A Student object is composed of other objects A String object A String object A Name object (fullName) A String object (id) A Student object Name object and a String Composition 31 For methods, we give the class Student constructors, accessors, mutators, and toString. Recall that the method toString is invoked when you use System.out.println to display an object, so it is a handy method to include in your class definitions. Note: Composition (has a) A class uses composition when it has objects as data fields. The class’s implementation has no special access to such objects and must behave as a client would. That is, the class uses an object’s methods to manipulate the object’s data. Since the class “has a,” or contains, an instance (object) of another class, the classes are said to have a has a relationship. 2.2 Look at the definition of the class Student, and then we will make a few more observations. public class Student { private Name fullName; private String id; // identification number public Student() { fullName = new Name(); id = ""; } // end default constructor public Student(Name studentName, String studentId) { fullName = studentName; id = studentId; } // end constructor public void setStudent(Name studentName, String studentId) { setName(studentName); // or fullName = studentName; setId(studentId); // or id = studentId; } // end setStudent public void setName(Name studentName) { fullName = studentName; } // end setName public Name getName() { return fullName; } // end getName public void setId(String studentId) { id = studentId; } // end setId public String getId() { return id; } // end getId 32 CHAPTER 2 Creating Classes from Other Classes public String toString() { return id + " " + fullName.toString(); } // end toString } // end Student The method setStudent is useful when we create a student object by using the default constructor or if we want to change both the name and identification number that we gave to a student object earlier. Notice that the method invokes the other set methods from this class to initialize the data fields. For example, to set the field fullName to the parameter studentName, setStudent uses the statement setName(studentName); We could also write this statement as this.setName(studentName); where this refers to the instance of write the assignment statement Student that invokes the method setStudent. Or we could fullName = studentName; Implementing methods in terms of other methods is usually desirable. It might not be desirable when implementing constructors, however, as you will see later in this chapter. Suppose that we want toString to return a string composed of the student’s identification number and name. It must use methods in the class Name to access the name as a string. For example, toString could return the desired string by using either return id + " " + fullName.getFirst() + " " + fullName.getLast(); or, more simply, return id + " " + fullName.toString(); The data field fullName references a Name object whose private fields are not accessible by name in the implementation of this class. We can access them indirectly via the accessor methods getFirst and getLast or by invoking Name’s toString method. Question 1 What data fields would you use in the definition of a class sent a student’s address? Question 2 Add a data field to the class new methods should you define? Student Address to repre- to represent a student’s address. What Question 3 What existing methods need to be changed in the class Student as a result of the added field that Question 2 described? Adapters 2.3 Suppose that you have a class, but the names of its methods do not suit your application. Or maybe you want to simplify some methods or eliminate others. You can use composition to write a new class that has an instance of your existing class as a data field and defines the methods that you want. Such a new class is called an adapter class. For example, suppose that instead of using objects of the class Name to name people, we want to use simple nicknames. We could use strings for nicknames, but since we cannot alter them, a class of nicknames would be more flexible. The following class has an instance of the class Name as Inheritance 33 a data field, a default constructor, and set and get methods. We use the first-name field of the class to store the nickname. Name public class NickName { private Name nick; public NickName() { nick = new Name(); } // end default constructor public void setNickName(String nickName) { nick.setFirst(nickName); } // end setNickName public String getNickName() { return nick.getFirst(); } // end getNickName } // end NickName Notice how this class uses the methods of the class Name to implement its methods. A NickName object now has only NickName’s methods, and not the methods of Name. Inheritance 2.4 Figure 2-2 Inheritance is a major component of object-oriented programming that enables you to organize classes. The name comes from the notion of inherited traits like eye color, hair color, and so forth, but it is perhaps clearer to think of inheritance as a classification system. Inheritance allows you to define a general class and then later to define more specialized classes simply by adding to or revising the details of the older, more general class definition. This saves work, because the specialized class inherits all the properties of the general class and you need only program the new or revised features. For example, you might define a generic class for vehicles and then define more specific classes for particular types of vehicles, such as automobiles, wagons, and boats. Similarly, the class of automobiles includes the classes of cars and trucks. Figure 2-2 illustrates this hierarchy of classes. The Vehicle class is the base class for the other derived classes. The Automobile class is the base class for the derived classes Car and Truck. Another term for base class is superclass, and another term for derived class is subclass. A hierarchy of classes Vehicle Automobile Car Boat Wagon Truck SailBoat PowerBoat 34 CHAPTER 2 Creating Classes from Other Classes As you move up in the diagram, the classes are more inclusive. A car is an automobile and, therefore, is also a vehicle. However, a vehicle is not necessarily a car. A sailboat is a boat and is also a vehicle, but a vehicle is not necessarily a sailboat. 2.5 Java and other programming languages use inheritance to organize classes in this hierarchical way. A programmer can then use an existing class to write a new one that has more features. For example, the class of vehicles has certain properties—like miles traveled—that its data fields record. The class also has certain behaviors—like going forward—that its methods define. The classes Automobile, Wagon, and Boat inherit these properties and behaviors. Everything that is true of all Vehicle objects, such as miles traveled, is described only once and inherited by the classes Automobile, Wagon, and Boat. Without inheritance, descriptions like miles traveled would have to be repeated for each of the derived classes Automobile, Wagon, Boat, Car, Truck, and so on. The derived classes then add to or revise the properties and behaviors that they inherit. Note: Inheritance Inheritance is a way of organizing classes so that common properties and behaviors can be defined only once for all the classes involved. Thus, you can define a general class and then later define more specialized classes simply by adding to or revising the details of the older, more general class definition. Since the Automobile class is derived from the Vehicle class, it inherits all the data fields and methods of that class. The Automobile class would have additional fields for such things as the amount of fuel in the fuel tank, and it would also have some added methods. Such fields and methods are not in the Vehicle class, because they do not apply to all vehicles. For example, wagons have no fuel tank. Inheritance gives an instance of a derived class all the behaviors of the base class. For example, an automobile will be able to do everything that a vehicle can do; after all, an automobile is a vehicle. In fact, inheritance is known as an is a relationship between classes. Since the derived class and the base class share properties, you should use inheritance only when it makes sense to think of an instance of the derived class as also being an instance of the base class. Note: An is a relationship Inheritance makes an instance of a derived class behave like an instance of its base class. Thus, you should use inheritance only when this is a relationship between classes is meaningful. Question 4 Revise the hierarchy in Figure 2-2 to categorize vehicles according to whether they have wheels. 2.6 Example. Let’s construct an example of inheritance within Java. Suppose we are designing a program that maintains records about students, including those in grade school, high school, and college. We can organize the records for the various kinds of students by using a natural hierarchy that begins with students. College students are then one subclass of students. College students divide into two smaller subclasses: undergraduate students and graduate students. These subclasses might further subdivide into still smaller subclasses. Figure 2-3 diagrams this hierarchical arrangement. A common way to describe derived classes is in terms of family relationships. For example, the class of students is said to be an ancestor of the class of undergraduate students. Conversely, the class of undergraduate students is a descendant of the class of students. Inheritance Figure 2-3 35 A hierarchy of student classes Student SchoolStudent ElementaryStudent MiddleSchoolStudent CollegeStudent HighSchoolStudent UndergradStudent GradStudent Although our program may not need any class corresponding to students in general, thinking in terms of such classes can be useful. For example, all students have names, and the methods of initializing, changing, and displaying a name will be the same for all student records. In Java, we can define a class called Student that includes data fields for the properties that belong to all subclasses of students, such as college students and high school students. The class definition can also contain all the methods that manipulate the data fields for the class Student. In fact, we have already defined such a Student class in Segment 2.2. 2.7 Now consider a class for college students. A college student is a student, so we use inheritance to derive the class CollegeStudent from the class Student. You define a derived class by adding data fields and methods to an existing class, the base class. In our example, Student is the base class and CollegeStudent is the derived class. The derived class has—that is, inherits—all the data fields and methods of the base class. In addition, the derived class defines whatever data fields and methods you wish to add. To indicate that CollegeStudent is a derived class of Student, we write the phrase extends Student on the first line of the class definition. Thus, the class definition of CollegeStudent begins public class CollegeStudent extends Student When you define a derived class, you write only the added data fields and the added methods. For example, the class CollegeStudent has all the data fields and methods of the class Student, but we do not mention them in the definition of CollegeStudent. In particular, every object of the class CollegeStudent has a data field called fullName, but we do not declare the data field fullName in the definition of the class CollegeStudent. Suppose that we create a new object of the class CollegeStudent as follows: CollegeStudent cs = new CollegeStudent(); The object cs has a data field fullName. Because fullName is a private data field, it is not legal to write cs.fullName outside of the definition of the class Student. However, the data field is there. We can access and change this data field by using Student’s methods, since the class CollegeStudent inherits all public methods from the base class Student. For example, we can write cs.setName(new Name("Warren", "Peace")); even though setName is a method of the base class Student. Since we have used inheritance to construct CollegeStudent from the class Student, every college student is a student. That is, a CollegeStudent object “knows” how to perform Student behaviors. 36 CHAPTER 2 2.8 Creating Classes from Other Classes A derived class, like CollegeStudent, can also add some data fields and/or methods to those it inherits from its base class. For example, CollegeStudent adds the data field year and the methods setYear and getYear. We can set the graduation year of the object cs by writing cs.setYear(2007); Suppose that we also add a data field that represents the degree sought and the methods to access and change it. We could also add fields for an address and grades, but to keep it simple, we will not. Let’s look at the class and focus on the constructors first. public class CollegeStudent extends Student { private int year; // year of graduation private String degree; // degree sought public CollegeStudent() { super(); // must be first year = 0; degree = ""; } // end default constructor public CollegeStudent(Name studentName, String studentId, int graduationYear, String degreeSought) { super(studentName, studentId); // must be first year = graduationYear; degree = degreeSought; } // end constructor { public void setStudent(Name studentName, String studentId, int graduationYear, String degreeSought) setName(studentName); // NOT fullName = studentName; setId(studentId); // NOT id = studentId; // or setStudent(studentName, studentId); (see Segment 2.15) year = graduationYear; degree = degreeSought; } // end setStudent < The methods setYear, getYear, setDegree, getDegree go here. > . . . public String toString() { return super.toString() + ", " + degree + ", " + year; } // end toString } // end CollegeStudent Invoking Constructors from Within Constructors 2.9 Calling the base class’s constructor. Constructors typically initialize a class’s data fields. In a derived class, how can the constructor initialize data fields inherited from the base class? One way is to call the base class’s constructor. The derived class’s constructor can use the reserved word super as a name for the constructor of the base class, also known as the superclass. Inheritance 37 Notice that the default constructor in the class CollegeStudent begins with the statement super(); This statement invokes the default constructor of the base class. The new default constructor must invoke the base class’s default constructor to properly initialize the data fields that are inherited from the base class. Actually, if you do not invoke super, Java will do it for you. In this book, we will always invoke super explicitly, to make the action a bit clearer. Note that the call to super must occur first in the constructor. You can use super to invoke a constructor only from within another constructor. In like fashion, the initializing constructor invokes a corresponding constructor in the base class with the statement super(studentName, studentId); Programming Tip: Calling the constructor of the base class The constructor of a derived class automatically calls the constructor of the base class. However, you can use super within the definition of the constructor of a derived class to call the constructor of the base class explicitly. When you do, super must always be the first action taken in the constructor definition. You cannot use the name of the constructor instead of super. 2.10 (Optional) Using this to invoke a constructor. You can use the reserved word this much as you use super, except that it calls a constructor of the same class instead of a constructor of the base class. For example, consider the following definition of a constructor that we might add to the class CollegeStudent in Segment 2.8: public CollegeStudent(Name studentName, String studentId) { this(studentName, studentId, 0, ""); } // end constructor The one statement in the body of this constructor definition is a call to the constructor whose definition begins public CollegeStudent(Name studentName, String studentId, int graduationYear, String degreeSought) As with super, any use of this must be the first action in a constructor definition. Thus, a constructor definition cannot contain both a call using super and a call using this. What if you want both a call with super and a call with this? In that case, you would use a call with this and have the constructor that is called with this have super as its first action. Private Fields and Methods of the Base Class 2.11 Accessing inherited data fields. The class CollegeStudent also has a setStudent method with four parameters, studentName, studentId, graduationYear, and degreeSought. To initialize the inherited data fields fullName and id, the method invokes the inherited methods setName and setId: setName(studentName); // NOT fullName = studentName setId(studentId); // NOT id = studentId Recall that fullName and id are private data fields in the definition of the base class Student. Only the definition of a method in the class Student can access fullName and id directly. Although the 38 CHAPTER 2 Creating Classes from Other Classes class CollegeStudent inherits these data fields, none of its methods can access them directly by name. Thus, setStudent cannot use an assignment statement such as id = studentId; // ILLEGAL in setStudent to initialize the data field id. Instead it must use some public mutator method such as setId. Programming Tip: A data field that is private in a base class is not accessible by name within the definition of a method for any other class, including a derived class. Even so, a derived class inherits the data fields of its base class. The fact that a private data field of a base class cannot be accessed in the definition of a method of a derived class often seems wrong to people. To do otherwise, however, would make the access modifier private pointless: Anytime you wanted to access a private data field, you could simply create a derived class and access it in a method of that class. Thus, all private data fields would be accessible to anybody who wanted to put in a little extra effort. 2.12 Private methods of the base class. Although a derived class cannot access the private data fields of its base class by name, it can access them indirectly by using set and get methods. In contrast, a derived class cannot invoke a base class’s private methods at all. While a derived class inherits the data fields of its base class, its does not inherit the base class’s private methods. This should not be a problem, since you should use private methods only as helpers within the class in which they are defined. If you want to use a base class’s method in a derived class, you should make the method either protected or public. We discuss protected methods later in this chapter. Programming Tip: A derived class does not inherit and cannot invoke a private method of the base class. Overriding and Overloading Methods 2.13 The set and get methods of the class CollegeStudent are straightforward, so we will not bother to look at them. However, we have provided the class with a method toString. Why did we do this, when our new class inherits a toString method from its base class Student? Clearly the string that the base class’s toString method returns can include the student’s name and identification number, but it cannot include the year and degree that are associated with the derived class. Thus, we need to write a new method toString. But why not have the new method invoke the inherited method? We can do this, but we’ll need to distinguish between the method that we are defining for CollegeStudent and the method inherited from Student. As you can see from the class definition in Segment 2.8, the new method toString contains the statement return super.toString() + ", " + degree + ", " + year; Since Student is the superclass, we write super.toString() to indicate that we are invoking the superclass’s toString. If we omitted super, toString would invoke itself recursively. Here we are using super as if it were an object. In contrast, we used super with parentheses as if it were a method within the constructor definitions. Inheritance If you glance back at Segment 2.2, you will see that follows: Student’s toString 39 method appears as public String toString() { return id + " " + fullName.toString(); } // end toString This method calls the toString method defined in the class Name, since the object fullName is an instance of the class Name. 2.14 Overriding a method. In the previous segment, you saw that the class CollegeStudent defines a method toString and also inherits a method toString from its base class Student. Both of these methods have no parameters. The class, then, has two methods with the same name and the same parameters. When a derived class defines a method with the same signature—that is, the same name, the same return type, and the same number and types of parameters—as a method in the base class, the definition in the derived class is said to override the definition in the base class. Objects of the derived class that invoke the method will use the definition in the derived class. For example, if cs is an instance of the class CollegeStudent, cs.toString() uses the definition of the method toString in the class CollegeStudent, not the definition of toString in the class Student, as Figure 2-4 illustrates. As you’ve already seen, however, the definition of toString in the derived class can invoke the definition of toString in the base class by using super. Figure 2-4 The method toString in CollegeStudent overrides the method toString in Student The class Student The class CollegeStudent public String toString() { . . . public String toString() { . . . super.toString() The client class CollegeStudent cs; . . . cs.toString() Note: Overriding a method definition A method in a derived class overrides a method in the base class when both methods have the same signature—that is, the same name, the same return type, and the same number and types of parameters. Programming Tip: You can use super in a derived class to call an overridden method of the base class. 2.15 Overloading a method. Suppose that a derived class has a method with the same name as a method in its base class, but the methods do not have identical parameters. The derived class would 40 CHAPTER 2 Creating Classes from Other Classes have both methods—the one it defines and the one it inherits from the base class. Java is able to distinguish between these methods since their parameters differ in number or data type. We say that the method in the derived class overloads the method in the base class. For example, the base class Student and the derived class CollegeStudent each have a method named setStudent. The methods are not exactly the same, however, as they have a different number of parameters. In Student, the method’s signature is public void setStudent(Name studentName, String studentId) whereas in CollegeStudent it is public void setStudent(Name studentName, String studentId, int graduationYear, String degreeSought) An instance of the class Student can invoke only Student’s version of the method, but an instance of CollegeStudent can invoke either method. Again, Java can distinguish between the two methods because they have different parameters. Within the class CollegeStudent, the implementation of setStudent can invoke Student’s setStudent to initialize the fields fullName and id by including the statement setStudent(studentName, studentId); instead of making calls to the methods setName and setId, as we did in Segment 2.11. Since the two versions of setStudent have different parameter lists, we do not need to preface the call with super to distinguish the two methods. However, we are free to do so by writing super.setStudent(studentName, studentId); Overloading methods is not restricted to cases of inheritance. One class can have several methods with the same name as long as they differ in their parameters. Note: Overloading a method definition A method in a class overloads a method in either the same class or its base class when both methods have the same name but differ in the number or types of parameters. Overloaded methods must have the same return type. Overloading and overriding are easy to confuse, but they are both legal. It is more important to learn the concepts than to distinguish between the terms. 2.16 Multiple use of super. As we have already noted, within the definition of a method of a derived class, you can call an overridden method of the base class by prefacing the method name with super and a dot. However, if the base class is itself derived from some other superclass, you cannot repeat the use of super to invoke a method from that superclass. For example, suppose that the class UndergradStudent is derived from the class CollegeStudent, which is derived from the class Student. You might think that you can invoke a method of the class Student within the definition of the class Undergraduate, by using super.super, as in super.super.toString(); // ILLEGAL! As the comment indicates, this repeated use of super is not allowed in Java. Programming Tip: super Although a method in a derived class can invoke an overridden method defined in the base class by using super, the method cannot invoke an overridden method that is defined in the base class’s base class. That is, the construct super.super is illegal. Inheritance 41 Question 5 Are the two definitions of the constructors for the class Student (Segment 2.2) an example of overloading or overriding? Question 6 If you add the method public void setStudent(Name studentName, String studentId) to the class CollegeStudent, and let it give some default values to the fields degree, are you overloading or overriding setStudent? Why? 2.17 year and The final modifier. Suppose that a constructor calls a method other than another constructor. For simplicity, imagine that this method—call it m—is in the same class C as the constructor. Now imagine that we derive a new class from C and we override the method m. If we invoke the constructor of our new class, it will call the base class’s constructor, which will call our overridden version of the method m. This method might use data fields that the constructor has not yet initialized, causing an error. Even if no error occurs, we will, in effect, have altered the behavior of the base class’s constructor. To specify that a method definition cannot be overridden with a new definition in a derived class, you make it a final method by adding the final modifier to the method signature. For example, you can write public final void m() { . . . } Note that private methods are automatically final methods, since you cannot override them in a derived class. Programming Tip: If a constructor invokes a method in its class, declare that method to be final so that no subclass can override the method and hence change the behavior of the constructor. You can declare an entire class as a final class, in which case you cannot use it as base class to derive any other class from it. Java’s String class is an example of a final class. Programming Tip: class. String cannot be the base class for any other class because it is a final Protected Access 2.18 You know that you control access to a class’s data fields and methods by using an access modifier like public or private. As you saw in Chapter 1, you can omit the access modifier entirely when the class is within a package and you want the class to be available only to other classes in the package. You also have one other choice for controlling access: You can use the access modifier protected for methods and data fields. A method or data field that is modified by protected can be accessed by name only within G G G Its own class definition C Any class derived from C Any class within the same package as C For example, if a method is marked protected in class C, you can invoke it from within any method definition in a class derived from class C. However, with classes that are not derived from C or that are not in the same package as C, a protected method behaves as if it were private. 42 CHAPTER 2 Creating Classes from Other Classes You should continue to declare all data fields as private. If you want a derived class to have access to a data field in the base class, define protected accessor or mutator methods within the base class. Note that package access is more restricted than protected access and gives more control to the programmer defining the classes. If you control the package directory, you control who is allowed package access. Figure 2-5 illustrates the various kinds of access modiers. Figure 2-5 Public, private, protected, and package access Derived class outside of package containing base class C Public Private Protected Package Base class C Client of C Any class within package containing C Programming Tip: When you design a class, consider the classes derived from it, either now or in the future. They might need access to your class’s data fields. If your class does not have public accessor or mutator methods, provide protected versions of such methods. Multiple Inheritance 2.19 Some programming languages allow one class to be derived from two different base classes. That is, you can derive class C from classes A and B. This feature, known as multiple inheritance, is not allowed in Java. In Java a derived class can have only one base class. You can, however, derive class B from class A and then derive class C from class B, since this is not multiple inheritance. A derived class can implement any number of interfaces—which we describe in Chapter 3—in addition to extending any one base class. This capability gives Java an approximation to multiple inheritance without the complications that arise with multiple base classes. Type Compatibility and Base Classes 2.20 Object types of a derived class. Previously, you saw the class CollegeStudent, which was derived from the class Student. In the real world, every college student is also a student. This relationship holds in Java as well. Every object of the class CollegeStudent is also an object of the class Student. Thus, if we have a method that has a formal parameter of type Student, the argument in an invocation of this method can be an object of type CollegeStudent. Type Compatibility and Base Classes 43 Specifically, suppose that the method in question is in some class and begins as follows: public void someMethod(Student scholar) Within the body of someMethod, the object scholar can invoke public methods that are defined in the class Student. For example, the definition of someMethod could contain the expression scholar.getId(). That is, scholar has Student behaviors. Now consider an object joe of CollegeStudent. Since the class CollegeStudent inherits all the public methods of the class Student, joe can invoke those inherited methods. That is, joe can behave like an object of Student. (It happens that joe can do more, since it is an object of CollegeStudent, but that is not relevant right now.) Therefore, joe can be the argument of someMethod. That is, for some object o, we can write o.someMethod(joe); No automatic type casting has occurred here. An object of the class CollegeStudent also is an object of the class Student. Thus, joe also is of type Student. The object joe need not be, and is not, type-cast to an object of the class Student. We can take this idea further. Suppose that we derive the class UndergradStudent from the class CollegeStudent. In the real world, every undergraduate is a college student, and every college student is also a student. Once again, this relationship holds in Java. Every object of the class UndergradStudent is also an object of the class CollegeStudent and so is also an object of the class Student. Thus, if we have a method that has a formal parameter of type Student, the argument in an invocation of this method can be an object of type UndergradStudent. Thus, an object can actually have several types as a result of inheritance. Note: An object of a derived class has more than one type. Everything that works for objects of an ancestor class also works for objects of any descendant class. 2.21 Because an object of a derived class also has the types of all of its ancestor classes, you can assign an object of a class to a variable of any ancestor type, but not the other way around. For example, since the class UndergradStudent is derived from the class CollegeStudent, which is derived from the class Student, the following are legal: Student amy = new CollegeStudent(); Student brad = new UndergradStudent(); However, the following statements are all illegal: CollegeStudent cs = new Student(); // ILLEGAL! UndergradStudent ug = new Student(); // ILLEGAL! UndergradStudent ug2 = new CollegeStudent(); // ILLEGAL! This makes perfectly good sense. For example, a college student is a student, but a student is not necessarily a college student. Some programmers find the phrase “is a” to be useful in deciding what types an object can have and what assignments to variables are legal. Question 7 If HighSchoolStudent is a derived class of Student, can you assign an object of HighSchoolStudent to a variable of type Student? Why or why not? Question 8 Can you assign an object of Student to a variable of type HighSchoolStudent? Why or why not? 44 CHAPTER 2 Creating Classes from Other Classes The Class Object 2.22 As you have already seen, if you have a class A and you derive class B from it, and then you derive class C from B, an object of class C is of type C, type B, and type A. This works for any chain of derived classes no matter how long the chain is. Java has a class—named Object—that is at the beginning of every chain of derived classes. This class is an ancestor of every other class, even those that you define yourself. Every object of every class is of type Object, as well as being of the type of its class and also of the types of all the other ancestor classes. If you do not derive your class from some other class, Java acts as if you had derived it from the class Object. Note: Every class is a descendant class of the class Object. The class Object contains certain methods, among which are toString, equals, and clone. Every class inherits these methods, either from Object directly or from some other ancestor class that ultimately inherited the methods from the class Object. The inherited methods toString, equals, and clone, however, will almost never work correctly in the classes you define. Typically, you need to override the inherited method definitions with new, more appropriate definitions. Thus, whenever you define the method toString in a class, for example, you are actually overriding Object’s method toString. 2.23 The toString method. The method toString takes no arguments and is supposed to return all the data in an object as a String. However, you will not automatically get a nice string representation of the data. The inherited version of toString returns a value based upon the invoking object’s memory address. You need to override the definition of toString to cause it to produce an appropriate string for the data in the class being defined. You might want to look again at the toString methods in Segments 2.2 and 2.8. 2.24 The equals method. The method equals has the following definition in the class Object: public boolean equals(Object obj) { return (this == obj); } // end equals The expression x.equals(y) is true if x and y reference the same object. You might, however, want an equals method in your class that returns true if the two distinct objects that x and y reference are equal in some sense. If so, you need to override equals in your class. For example, consider an equals method for the class Name that we defined in Chapter 1. As you recall, Name has two data fields, first and last, that are instances of String. We could decide that two Name objects are equal if they have equal first names and equal last names. The following method determines whether two Name objects are equal by comparing their data fields: public boolean equals(Object otherObject) { boolean result = false; if (otherObject instanceof Name) // NOT instanceOf { Name otherName = (Name) otherObject; String otherFirst = otherName.first; String otherLast = otherName.last; Type Compatibility and Base Classes 45 result = first.equals(otherFirst) && last.equals(otherLast); } // end if return result; } // end equals Notice the use of the operator instanceof, which returns true if the object on its left is an instance of the class on its right. If we pass an object to this equals method that is not an instance of the class Name, the method returns false. Otherwise, the method compares the data fields. Notice that we first must cast1 the type of the parameter otherObject from Object to Name so that we can access Name’s data fields. Question 9 The previous method that method belong? equals invokes an equals Question 10 If sue and susan are two instances of the class determine whether they represent the same name? 2.25 method. To what class does Name, what if statement can The clone method. Another method inherited from the class Object is the method clone. This method takes no arguments and returns a copy of the calling object. The returned object is supposed to have data identical to that of the calling object, but it is a different object (an identical twin or a “clone”). As with other methods inherited from the class Object, we need to override the method clone before it can behave properly in our class. However, in the case of the method clone, there are other things we must do as well. A discussion of the method clone appears in Chapter 15. Abstract Classes and Methods 2.26 The class Student defined in Segment 2.2 is a base class for other classes such as CollegeStudent. We really do not need to create objects of type Student, although it is certainly legal to do so. We might, however, want to prevent a client from creating objects of type Student. To do so, we can declare the class to be an abstract class by including the reserved word abstract in the signature of the class definition, as follows: public abstract class Student { . . . Often when programmers define an abstract class, they declare one or more methods that have no body. The intention in doing so is to require that every derived class implement such methods in an appropriate way for that class. For example, we might want every derived class of Student to implement a display method. We certainly cannot write a display method for a future class that is not yet defined, but we can require one. To do so, we declare display to be an abstract method by including the reserved word abstract in the signature of the method, as follows: public abstract void display(); Note that the method signature is followed by a semicolon; the method has no body. 1. Segment A.17 of Appendix A reviews type casts. 46 CHAPTER 2 Creating Classes from Other Classes Note: An abstract class will be the base class of another class. Thus, an abstract class is sometimes called an abstract base class. 2.27 If a class has at least one abstract method, Java requires that you declare the class itself as abstract. This makes sense, for otherwise you could create an object of an incomplete class. In our example, the object would have a method display without an implementation. What if the derived class of an abstract class does not implement all of the abstract methods? Java will treat the derived class as abstract and prevent you from creating an object of its type. For example, if the class CollegeStudent, which is derived from Student, did not implement display, CollegeStudent would have to be abstract. Programming Tip: A class with at least one abstract method must be declared as an abstract class. Even after we’ve made the class Student abstract by adding the abstract method display, not all of its methods are abstract. All the method definitions, except for the method display, are exactly the same as in our original definition. They are full definitions that do not use the reserved word abstract. When it makes sense to implement a method in an abstract class, you should do so. In this way, you include as much detail as possible in the abstract class, detail that need not be repeated in derived classes. 2.28 Example. Let’s add another method to the class Student, one that invokes the abstract method display. Before you complain about invoking a method that has no body, remember that Student is an abstract class. When we finally derive a class from Student that is not abstract, display will be implemented. The method we have in mind serves mainly as an example, rather than doing anything useful. It skips the specified number of lines before displaying an object: /** Task: Displays the object after skipping * numberOfLines lines. */ public void displayAt(int numberOfLines) { for (int count = 0; count < numberOfLines; count++) System.out.println(); display(); } // end displayAt The method displayAt invokes the abstract method display. Here the abstract method serves as a placeholder for a method that will be defined in a future derived class. If display were not abstract, we would have to give it a body that really would be useless, since every derived class would override it. Question 11 Suppose that you change the name of the previous method displayAt to disDoes the resulting method overload or override the method display()? Why? play. Polymorphism 2.29 “Polymorphism” comes from a Greek word meaning “many forms.” Polymorphism is common in English, and its use in a programming language makes the programming language more like a Polymorphism 47 human language. For example, the English instruction “Play your favorite sport” means different things to different people. To one person it means to play baseball. To another person it means to play soccer. In Java, polymorphism allows the same program instruction to mean different things in different contexts. In particular, one method name, used as an instruction, can cause different actions depending on the kind of object performing the action. Originally, overloading a method name was considered polymorphism. However, the modern usage of the term refers to an object determining at execution time which action of a method it will use for a method name that is overridden either directly or indirectly. Note: Polymorphism One method name in an instruction can cause different actions according to the kinds of objects that invoke the method. 2.30 Example. For example, a method named display can display the data in an object. But the data it displays and how much it displays depend on the kind of object invoking the method. Let’s add the method display to the class Student of Segment 2.2 and assume that neither the method nor the class is abstract. Thus, display has an implementation within the class Student. Now add to the class the method displayAt as it appears in Segment 2.28. If the only class around were Student, these changes would not be exciting. But we derived the class UndergradStudent from the class CollegeStudent, which we derived from the class Student. The class UndergradStudent inherits the method displayAt from the class Student. In addition, UndergradStudent overrides the method display defined in Student by providing its own implementation. So what? you might be wondering. Well, look at the poor compiler’s job when it encounters the following Java statements:2 UndergradStudent ug = new UndergradStudent(. . .); ug.displayAt(2); The method displayAt was defined in the class Student, but it calls the method display that is defined in the class UndergradStudent. The code for displayAt could have been compiled with the class Student before the class UndergradStudent was even written. In other words, this compiled code could use a definition of the method display that was not even written at the time that displayAt was compiled. How can that be? When the code for displayAt is compiled, the call to display produces an annotation that says, “use the appropriate definition of display.” Then, when we invoke ug.displayAt(2), the compiled code for displayAt reaches this annotation and replaces it with an invocation of the version of display that goes with ug. Because in this case ug is of type UndergradStudent, the version of display that is used will be the definition in the class UndergradStudent. 2.31 The decision as to which method definition to use depends on the invoking object’s place in the inheritance chain. It is not determined by the type of the variable naming the object. For example, consider the following code: UndergradStudent ug = new UndergradStudent(. . .); Student s = ug; s.displayAt(2); 2. The arguments of the constructor are not relevant to the examples in this section, so we have replaced them with ellipses to save space. 48 CHAPTER 2 Creating Classes from Other Classes As we noted in Segment 2.21, assigning an object of the class UndergradStudent to a variable of type Student is perfectly legal. Here, the variable s is just another name for the object that ug references, as Figure 2-6 illustrates. That is, s and ug are aliases. But the object still remembers that it was created as an UndergradStudent. In this case, s.displayAt(2) ultimately will use the definition of display given in UndergradStudent, not the definition of display given in Student. A variable’s static type is the type that appears in its declaration. For example, the static type of the variable s is Student. The static type is fixed and determined when the code is compiled. The type of object that a variable references at a point in time during execution is called its dynamic type. A variable’s dynamic type can change as execution progresses. When the assignment s = ug executes in the previous code, the dynamic type of s is UndergradStudent. A variable of a reference type is called a polymorphic variable, since its dynamic type can differ from its static type and change during execution. To determine which definition of display to use in our example, Java examines the code to see which class’s constructor created the object. That is, Java uses the dynamic type of the variable s to make this determination. Figure 2-6 The variable s is another name for an undergraduate object ug Object of type UndergradStudent s Note: An object, not its reference, determines which method is invoked. This way of handling a call to a method that might be overridden later is called dynamic binding or late binding, because the meaning of the method invocation is not bound to the location of the method invocation until you run the program. If Java did not use dynamic binding when you ran the preceding code, you would not see the data for an undergraduate student. Instead you would see only what the method display of the class Student provided. Note: Dynamic binding Dynamic binding is the process that enables different objects to use different method actions for the same method name. 2.32 Java is so good at figuring out which definition of a method to use that even a type cast will not fool it. Recall that you use a type cast to change the type of a value to some other type. The meaning of s.displayAt(2) will always be appropriate for an UndergradStudent, even if we use a type cast to change the type of ug to the type Student, as in the following statements: UndergradStudent ug = new UndergradStudent(. . .); Student s = (Student) ug; s.displayAt(2); Polymorphism 49 Despite the type cast, s.displayAt(2) will use the definition of display given in UndergradStudent, not the definition of display given in Student. The object, not its name, determines which method to use. To see that dynamic binding really is a big deal, consider the following code: UndergradStudent ug = new UndergradStudent(. . .); Student s = ug; s.displayAt(2); GradStudent g = new GradStudent(. . .); s = g; s.displayAt(2); The two lines shown in color are identical, yet each one invokes a different version of display. The first line displays an UndergradStudent and the second displays a GradStudent, as Figure 2-7 illustrates. An object remembers what method definitions it had when the new operator created it. You can place the object in a variable of a different (but ancestor) class type, but that has no effect on which method definition the object uses for an overridden method. Let’s pursue this process a bit more to see that it is even more dramatic than it may appear at first glance. Note that objects of the classes UndergradStudent and GradStudent inherit the method displayAt from the class Student and do not override it. Thus, the text of the method definition is even the same for objects of the classes UndergradStudent and GradStudent. It is the method display, invoked in the definition of displayAt, that is overridden. Note: Objects know how they are supposed to act When an overridden method (or a method that uses an overridden method) is invoked, the action of that method is the one defined in the class whose constructor created the object. This action is not determined by the static type of the variable naming the object. A variable of any ancestor class can reference an object of a descendant class, but the object always remembers which method actions to use for every method name, because Java uses dynamic binding. Figure 2-7 An object, not its name, determines its behavior ug s g s Object of type UndergradStudent s.displayAt(2) invokes the method display in the class UndergradStudent Object of type GradStudent s.displayAt(2) invokes the method display in the class GradStudent 50 CHAPTER 2 2.33 Creating Classes from Other Classes Type checking and dynamic binding. You need to be aware of how dynamic binding interacts with Java’s type checking. For example, if UndergradStudent is a derived class of the class Student, we can assign an object of type UndergradStudent to a variable of type Student, as in Student s = new UndergradStudent(); But that is not the end of the story. Although we can assign an object of type UndergradStudent to a variable of type Student, we can invoke a method that is in the class Student only when the calling object is the variable s (of type Student). However, if the method is overridden in the definition of the class UndergradStudent and the object named by the variable s is of type UndergradStudent, it is the version of the method defined in UndergradStudent that will be used. In other words, the variable determines what method names can be used, but the object determines which definition of the method name will be used. If we want to use a method name that was first introduced in the class UndergradStudent with the object named by the variable s of type Student, we must use a type cast. 2.34 Example. For example, recall that UndergradStudent is a derived class of Student. The following statements are legal: Student s = new UndergradStudent(. . .); s.setName(new Name("Jamie", "Jones")); s.display(); The definition of display given in the class UndergradStudent is used. Remember, the object, not the variable, determines which definition of a method will be used. On the other hand, the following is illegal: s.setDegree(2); // ILLEGAL because setDegree is not the name of a method in the class Student. Remember, the variable determines which method names can be used. The variable s is of type Student, but it references an object of type UndergradStudent. That object can still invoke the method setDegree, but the compiler does not know this. To make the invocation legal, we need a type cast, such as the following: UndergradStudent ug = (UndergradStudent) s; ug.setDegree(2); // LEGAL You may think this is all just a silly exercise, because you would never assign an object of type to a variable of type Student. Not so. You may not often make such an assignment directly, but you frequently will do so unwittingly. Recall that we can have an argument of type UndergradStudent for a method parameter of type Student and that a formal parameter behaves like a local variable that is assigned the value of its corresponding argument. In this case an object of type UndergradStudent (the argument in the method invocation) is assigned to a variable of type Student (the formal parameter in the method definition). UndergradStudent 2.35 Example. If we add an appropriate toString method to the class Student, we can display a student object by using the toString method, as follows: Name joe = new Name("Joe", "Student"); Student s = new Student(joe, 123456789); System.out.println(s.toString()); But thanks to dynamic binding, we do not even need to write toString in our invocation of System.out.println. The following will work just as well and will produce exactly the same output: Student s = new Student(joe, 123456789); System.out.println(s); Polymorphism 51 The method invocation System.out.println(s) invokes the method println with the calling object System.out. One definition of the method println has a single parameter of type Object. The definition is equivalent to the following: public void println(Object theObject) { System.out.println(theObject.toString()); } // end println (By the way, the invocation of the method println inside the braces is a different, overloaded definition of the method println that has a parameter of type String, not Object.) This definition of println existed before the class Student was defined. Yet the invocation System.out.println(s); with an object s of type Student—and hence also of type Object—uses Student’s toString, not Object’s toString. Dynamic binding is what makes this work. Question 12 Is a method display with no parameters that is defined explicitly in each of the classes Student, CollegeStudent, and UndergradStudent an example of overloading or overriding? Why? Question 13 Is overloading a method name an example of polymorphism? Question 14 In the following code, will the two invocations of same output? displayAt produce the Student s = new UndergradStudent(. . .); s.displayAt(2); s = new GradStudent(. . .); s.displayAt(2) C HAPTER S UMMARY G When you use composition to define a class, you use objects of one or more existing classes as data fields. The new class treats these objects as it would any other client of the existing classes. G Composition defines a has a relationship between two classes. G Inheritance groups classes that have properties and behaviors in common. The common properties and behaviors are defined only once for all the classes. Thus, you can define a general class—the base class—and later define more specialized classes—the derived classes—by simply adding to or revising the details of the older, more general class definition. G Use inheritance when you have an is a relationship between two classes. G A method in a derived class overrides a method in the base class when both methods have the same name, the same return type, and the same number and types of parameters. G A method in a class overloads a method in either the same class or its base class when both methods have the same name but differ in the number or types of parameters. G Everything that works for objects of an ancestor class also works for objects of any descendant class. G A protected method can be accessed by name within its own class, a derived class, or a class within its class’s package. Other classes cannot invoke it; the protected method behaves as if it were private. 52 CHAPTER 2 Creating Classes from Other Classes G G An abstract class has no instances and serves only as a base class. Any class that omits the definition of one or more of its methods must be declared abstract. G Polymorphism is the concept whereby an object determines at execution time which action of a method it will use for an overridden method name. Dynamic binding is the process that implements polymorphism. G When an overridden method (or a method that uses an overridden method) is invoked, the action of that method is the one defined in the class whose constructor created the object. This action is not determined by the type of the variable naming the object. A variable of any ancestor class can hold an object of a descendant class, but the object always remembers which method actions to use for every method name, because Java uses dynamic binding. G The variable that names an object determines which method names an object can invoke. The object, not its name, determines which definition of a method it will use. G The constructor of a derived class automatically calls the constructor of the base class. However, you can use super within the definition of the constructor of a derived class to call the constructor of the base class explicitly. When you do, super must always be the first action taken in the constructor definition. You cannot use the name of the constructor instead of super. G A data field or method that is private in a base class is not accessible by name in the definition of a method for any other class, including a derived class. G A derived class cannot invoke a private method of the base class by name. G You can use super in a derived class to call an overridden method of the base class. G A method in a derived class cannot invoke an overridden method that is defined in the base class’s base class. That is, the construct super.super is illegal. G P ROGRAMMING T IPS Every class is a descendant of the class Object. If a constructor invokes a method in its class, declare that method to be final so that no subclass can override the method and hence change the behavior of the constructor. G String cannot be the base class for any other class because it is a final class. G G E XERCISES When you design a class, consider the classes derived from it, either now or in the future. They might need access to your class’s data fields. If your class does not have public accessor or mutator methods, provide protected versions of such methods. A class with at least one abstract method must be declared an abstract class. 1. Given the class Student defined in Segment 2.2, write Java statements that create a Student object for Jill Jones. Do this in two ways, using a different constructor each time. Jill’s ID number is 8001. Polymorphism 53 2. If joe is an object of the class Student, as defined in Segment 2.2, is it legal to write joe.getFirst() to get joe’s first name? Why or why not? 3. Consider the class Student defined in Segment 2.2. a. Add a constructor that has three String parameters representing the student’s first name, last name, and identification number. b. Add a method getFirst that returns a student’s first name. 4. Consider the class NickName as defined in Segment 2.3. Why is composition a more appropriate way to reuse the class Name than inheritance? 5. Revise the hierarchy in Figure 2-2 to include categories of land, sea, air, and space vehicles. 6. Consider the class CollegeStudent as defined in Segment 2.8. The method toString contains the invocation super.toString(). What string does this invocation return? 7. The class CollegeStudent has the method setStudent. Does this method overload or override the setStudent method in the class Student? Explain. 8. The class CollegeStudent has no definitions for methods that set or get the student’s name. Why? 9. Can you omit the call to super in the default constructor of the class CollegeStudent? Can you omit it in the second constructor? Give reasons for your answers. 10. Suppose that joe is an object of the class CollegeStudent. Write Java statements that display joe’s name. Repeat this in two other distinct ways. 11. You could revise the constructor in the class Student as follows: public Student(Name studentName, String studentId) { setStudent(studentName, studentId); } // end constructor The derived class CollegeStudent could override setStudent, thereby affecting this constructor. How can you prevent any derived class of Student from overriding setStudent? 12. Given the class Student and its derived class CollegeStudent, which of the following statements are legal and which are not? a. b. c. d. Student bob = new Student(); Student bob = new CollegeStudent(); CollegeStudent bob = new Student(); CollegeStudent bob = new CollegeStudent(); 13. Assuming that you have added an equals method to the class Name, as described in Segment 2.24, write an equals method for the class Student and one for the class CollegeStudent. 14. An abstract class can have methods that are implemented as well as other methods that are not. A method without an implementation—that is, without a body—is abstract. a. What is the purpose of an abstract method? b. What is the purpose of an abstract class? 54 CHAPTER 2 Creating Classes from Other Classes 15. Consider the following Java statements: jillJones = new Name("Jill", "Jones"); joeCool = new Name("Joseph", "Cool"); Student jill = new Student(jillJones, "2222"); CollegeStudent joe = new CollegeStudent(joeCool, "33", 2004, "B.S."); a. Can joe be the argument of a method whose parameter’s type is Student? Why or why not? b. Can jill be the argument of a method whose parameter’s type is CollegeStudent? Why or why not? c. Is jill.getYear() legal? Why or why not? d. Is joe.getId() legal? Why or why not? 16. Suppose that the class Student has the methods display and displayAt, as described in Segment 2.28. Suppose also that the class CollegeStudent has a method display. Assume that jill and joe are defined as in Exercise 15. Which version of the method display will displayAt invoke when each of the following statements executes? a. b. c. d. jill.displayAt(2); joe.displayAt(2); Student s = joe; s.displayAt(2); Student s = (Student)joe; s.displayAt(2); 17. Imagine two classes, A and B. Class A has a private data field theData and the following methods: public void w(); public void x(); protected void y(); private void z(); Class B extends class A and has the following methods: public void x(); protected void r(); private void s(); Suppose that the client declares instances of these classes, as follows: A inA = new A(); B inB = new B(); a. b. c. d. e. f. g. h. Which of the objects inA and inB can access the field theData? Which of these objects can invoke the method w? Which of these objects can invoke the method y? Which of these objects can invoke the method r? Which of these objects can invoke the method z? Which version of the method x does inB.x() invoke? Which methods are available to the implementation of the class B? Which methods are available to clients of B? Polymorphism P ROJECTS 55 1. Implement the class Die, as described in Project 4 of Chapter 1. Then implement a class TwoDice that represents two six-sided dice. You should be able to roll the dice and determine the sum of their face values. You should also be able to determine various special cases, such as a pair of ones (snake eyes) or a pair of sixes (box cars). 2. Design and implement a class to play a game that uses dice. Use the class TwoDice that Project 1 describes. You can use the following simple game or choose one of your own. Players take turns in rolling the dice. For each player, maintain a cumulative sum of the face values of the rolled dice. Double the value of any matching pair of dice. The first player to reach a certain number, such as 50 or 100, wins. 3. Define the class Address to represent a person’s mailing address. Include data fields for at least the street address, city, and state. Provide reasonable constructors and set and get methods. Next add an Address field to the class Student, as defined in Segment 2.2. Add methods to Student that access or modify the address. Revise any existing methods of Student to accommodate this new field. 4. Define the class Transcript to record a student’s grades for a semester. Begin by creating other classes such as Course and Grade. A Course object could contain the title, number of credits, and grade for a course. A Grade object could contain a letter grade and the quality points that the grade represents. The class Transcript then contains an instance of Course for each course taken by a student in one semester. Now add an instance of Transcript as a data field of the class Student. Add appropriate methods to Student that deal with the transcript. 5. Textile designers often use computers to create new patterns. Design and implement a class SquarePattern that represents a pattern for a fabric square of a given dimension. Methods in the class should enable you to create objects that have different patterns. Use this class in another class Fabric that represents a piece of fabric whose pattern is composed of various squares. Your classes should be able to display the patterns on the screen. 6. The class Name given in Segment 1.16 of Chapter 1 represents a person’s first and last names. Derive the class ProperName from Name, adding data fields for a middle initial and a title such as Ms., Mr., Dr., or Sir. Provide reasonable constructors and set and get methods for the new fields. Override the toString method so that it behaves correctly for the new class. Explain why inheritance is appropriate in the definition of ProperName but is not a reasonable choice for the definition of the class NickName that Segment 2.3 describes. 7. Project 1 in Chapter 1 describes the class Counter that records a nonnegative integer count. Given this class, define a class GraphicCounter by using either composition or inheritance. You should be able to display a GraphicCounter object on the screen, as Figure 2-8 illustrates. Provide the class with reasonable methods. Use the class to create an application or applet that behaves like a timer. You set the timer to an initial value in seconds and, at a signal, it counts down to zero. You should see the timer’s value change as it counts. Figure 2-8 A graphic counter for Project 7 42 56 CHAPTER 2 Creating Classes from Other Classes 8. Project 3 of Chapter 1 asked you to define a class GenericCoin. Each GenericCoin object can be tossed so that it randomly lands either heads up or tails up. Derive a class Coin from GenericCoin that adds a monetary value and a name as data fields. Provide your class with appropriate methods. Write a program that tests your class and shows that your new coins inherit the behaviors of a GenericCoin object. In particular, your new coins can be tossed. Now write a program that creates ten of each kind of coin (for example, ten pennies, ten nickels, and so on).Toss each coin once and form two groups, one for heads and one for tails. Compute the monetary value of the coins in each group. 9. Design and implement the classes Student, CollegeStudent, UndergradStudent, and GradStudent according to the hierarchy shown in Figure 2-3. Although you can use aspects of these classes that you saw in this chapter, make the classes Student and CollegeStudent abstract classes. The classes UndergradStudent and GradStudent should each ensure that the degree field of CollegeStudent is set to a legal value. For example, you could restrict an undergraduate student to B.A. and B.S. degree programs and a graduate student to M.S. and Ph.D. programs. ...
View Full Document

Ask a homework question - tutors are online