Lab 6 | CS 61BL Summer 2015 Main Course Info Staff Assignments Resources Piazza Lab 6: Inheritance, Interfaces, and Abstract Classes Table of Contents A. Intro Learning Goals B. Inheritance Review of Inheritance from CS 61A Inheritance Terminology Exercise: Extending the Counter Class Exercise: Private Fields and Inheritance Exercise: Extending the Point Class A Note About the TracedPoint Constructor Reminder: Another way to use super C. Static and Dynamic Type Introduction to Polymorphism Polymorphic Data Structures A Problem A Solution? Polymorphic Method Selection Static and Dynamic Type What the Compiler Does What Happens at Run Time Self-test: Identify Static Types Self-test: Polymorphism Puzzles Typing Summary D. Abstract Classes Motivation Abstract classes Abstract Classes in Java Libraries Abstract Dates Exercise: A nextDate Method E. Interfaces Interfaces Motivation for Interfaces Exercise: Sorting Dates F. A Better IntSequence Exercise: A Better IntSequence Exercise: One Last Step ArrayLists G. Conclusion Summary Submission Reading A. Intro Learning Goals The focus of today's lab is inheritance, a mechanism that allows a class to be defined that differs only slightly from an existing class. Inheritance provides numerous benefits. In particular, programmers can take advantage of already written correct code and avoid reinventing the wheel. A related concept is polymorphism, where a variable may store values of its subclasses. The procedure for de-tangling an instance of polymorphism can be somewhat complicated, so there are numerous exercises that provide you with practice. Inheritance can also be used to organize an already existing bunch of similar classes. From this situation emerges the concepts of abstract classes and interfaces. Both provide a way to require that certain methods be included in a given class without specifying how they are implemented. Later, the fully implemented methods can be called "under the table", for example to compare two elements of an array. We think this is neat. To get started , download the code for Lab 6 and create a new Eclipse project out of it. B. Inheritance Review of Inheritance from CS 61A You learned in CS 61A that a programmer can set up one class to inherit characteristics (methods and instance variables) from another. This is typically done to reuse most of an already-defined class that needs an extra method, or that requires a method to behave slightly differently. Inheritance Terminology We will refer to the inheriting class as the subclass of its parent or superclass, and say that the subclass extends the superclass. Methods in the superclass can be redefined in the subclass. This is called overriding the methods. In Java, we set up inheritance in a class's header, using the keyword extends. You may have noticed examples of its use already this semester; it looks something like this: public class SubClass extends SuperClass {
...
} or public class Dog extends Animal {
...
} or public class Dalmatian extends Dog {
...
} Note: If a class has the keyword final in its header, then it cannot have any subclasses. Discussion: Review the Lingo Link to the discussion There was a lot of lingo on the last step. Discuss each of the following terms with your partner to make sure you both understand what they mean. Then post a definition of each in your own words to the discussion. Check out your classmates' posts to see if they match your intuition. subclass superclass extends overrides Exercise: Extending the Counter Class Here's an example of inheritance. Recall the Counter class from earlier in the course. Note: this version is slightly modified from version we used before. public class Counter {
int myCount;
public Counter ( ) {
myCount = 0;
}
public void increment ( ) {
myCount++;
}
public void reset ( ) {
myCount = 0;
}
public int value ( ) {
return myCount;
}
} Let's revisit the example of the mod N counter. Remember before that to create ModNCounter.java we just wrote over the file Counter.java. Another way to set up ModNCounter.java is to have it inherit from Counter.java. Write a new version of ModNCounter.java that uses inheritance. You only have to replace the constructor and one other method; all others should be inherited. Once you're done, check that you can use all the methods correctly, even ones that you never directly defined in ModNCounter. For instance: ModNCounter modCounter = new ModNCounter(3);
modCounter.increment();
System.out.println(modCounter.value()); // prints 1
modCounter.reset();
modCounter.increment();
System.out.println(modCounter.value()); // still prints 1 Also check that the mod functionality works. ModNCounter modCounter = new ModNCounter(3);
modCounter.increment();
modCounter.increment();
modCounter.increment();
modCounter.increment();
System.out.println(modCounter.value()); // prints 1 Exercise: Private Fields and Inheritance The Counter class we just considered was modified. The original version is below and has a private instance variable myCount. Edit your Counter.java file so that it matches the code seen below. public class Counter {
private int myCount;
public Counter ( ) {
myCount = 0;
}
public void increment ( ) {
myCount++;
}
public void reset ( ) {
myCount = 0;
}
public int value ( ) {
return myCount;
}
} Subclasses do not have access to the private variables of their superclasses. So, actually ModNCounter cannot have access to the private instance variable myCount. This restriction makes sense. A programmer defining a variable as private presumably intends that access to the variable be limited. However, if all you had to do to gain access to a private variable was to define a subclass of the class containing it, it would be easy to subvert the limited access. (Note: We can use the keyword protected instead of private if we want to allow subclasses to access the variables, but not allow any other classes. However, this is discouraged because of the problem described above. In general, it is good style to make your variables as restricted as possible.) Modify ModNCounter to work even when Counter's myCount variable is private. Do not create a new myCount variable in ModNCounter, or override any more than the constructor and one method. This may be a bit tricky! Hint: If a subclass overrides a method from its superclass, it can still call the original method (if it is public) by prefacing the method name with the super keyword. Exercise: Extending the Point Class We can extend classes that we haven't written ourselves — such as those in the Java API — provided they aren't declared as final. Here's an example of extending the Point class (which we worked with earlier) from the java.awt library. Some classes provide "setter" methods. Setter methods allow you to change the values of instance variables, even if they are private (because the method is public). A useful debugging aid is to override a setter method to produce informative output every time the object's state changes. The setter method for the Point class is named move; the call p.move (27, 13); changes the x coordinate to 27 and the y coordinate to 13 in the Point referenced by p. Given below is the framework for a TracedPoint class intended to intercept calls to move and print information about the pre- and post-change state of the Point along with doing the move. You are to complete and test the framework after reading a note about super in the next step. import java.awt.*;
public class TracedPoint extends Point {
public TracedPoint (int x, int y) {
super (x, y);
}
// Your move method goes here.
public static void main (String [ ] args) {
TracedPoint p1 = new TracedPoint (5, 6);
p1.move (3, 4); // prints: "point moved from (5,6) to (3,4)
p1.move (9, 10); // prints: "point moved from (3,4) to (9,10)
TracedPoint p2 = new TracedPoint (25, 30);
p2.move (45, 50); // prints: "point moved from (25,30) to (45,50)
System.out.println ("p1 is " + p1);
System.out.println ("p2 is " + p2);
}
} A Note About the TracedPoint Constructor When constructing a subclass object, you must always construct its superclass first. In the constructor of a subclass, Java automatically supplies a call to the superclass constructor with no arguments. For example, If you write a TracedPoint constructor, it will automatically call Point(); before running the first line of the TracedPoint constructor. If you want to call a constructor of the superclass other than the no-argument constructor, use the super keyword as shown below. public class TracedPoint extends Point {
public TracedPoint (int x, int y) {
super (x, y);
}
// ...
} This calls the two int constructors of Point. The super keyword must be used on the first line of the constructor. An aside: similar to how you use super, you can also use the keyword this as a constructor. this calls other constructor methods within the same class. For example, if you wanted the zero-argument constructor for TracedPoint to initialize a traced point at (0, 0), you could write: public TracedPoint(){
this(0, 0);
} Reminder: Another way to use super The super keyword has another use besides for constructors. It also allows you to call superclass methods that the subclass has overriden. Use it similarly to how you would use the this keyword. this.method(); // calls the method in the current class
super.method(); // calls the method in the parent class Now implement TracedPoint and take advantage of the superclass's methods and variables as much as possible rather than reinventing the wheel. C. Static and Dynamic Type Introduction to Polymorphism We saw earlier that inheritance provides a way to reuse existing classes (Counter and Point), implementing small changes in behavior by overriding existing methods in the superclass or by adding new methods in the subclass. Inheritance also, however, makes it possible for us to design general data structures and methods using polymorphism. The word "polymorphism" comes from the Greek words for "many" and "forms". In the context of object-oriented programming, it means that a given object can be regarded either as an instance of its own class, as an instance of its superclass, as an instance of its superclass's superclass, and so on up the hierarchy. In particular, where a given reference type is requested for a method argument or needed for a return type, we can supply instead an instance of any subclass of that reference type. That's because inheritance implements an "is-a" relationship: for example, a TracedPoint is a Point with some extra properties. As an example, imagine you have the following method in some class (not necessarily Point or TracedPoint): public static void moveTo79 (Point p) {
p.move (7, 9);
} We can call moveTo79 and pass in either a Point object or a TracedPoint object. And if we pass in a TracedPoint object, the code will use the move method that you implemented in TracedPoint! Discussion: Thinking about Polymorphism Link to the discussion Would you expect the substitution mechanism to work in reverse? For example, would the following code work? public static void anotherMoveTo79 (TracedPoint tp){
tp.move(7, 9);
}
...
Point p = new Point(3, 4);
anotherMoveTo79(p); Briefly discuss with your partner why you would expect this to work, or not. Then try it out for yourself and see! Polymorphic Data Structures The java.util class library contains several collection classes that take advantage of polymorphism and are therefore able to store a variety of types of objects. We will examine the ArrayList class as an example. It represents an expandable array-like structure. (It's described in chapter 6 of Head First Java.) To declare an ArrayList reference, specify both the ArrayList class name and also the class of objects that the ArrayList will contain in angle brackets. For example, ArrayList values; declares a reference to an ArrayList that contains only String objects. Similarly, to construct an ArrayList object, you need to supply the element class name in angle brackets, ArrayList values = new ArrayList ( ); What happens if you don't specify the angled brackets?: ArrayList values = new ArrayList(); It turns out, this is equivalent to ArrayList