CS 1302 – Ch 11 – Inheritance & Polymorphism Read Sections 1-15 (pages 410-444) Section 11.1 - Introduction What is the big picture of this chapter? You can write a class that uses code already written in another class. Section 11.2 – Superclasses and Subclasses (and summary of things to come) 1. Java allows us to extend a class to create a subclass. All public members of the superclass are inherited by the subclass. 2. Example public class Dog { public class WolfDog extends Dog { private String name; public WolfDog( String name ) { super( name ); } public Dog( String name ) { this.name = name; } public String getName() { return name; } public void bark() { System.out.println("bark"); } public String toString() { return String.format( "My name is %s", name ); } } public void howl() { System.out.println("howl"); } } Test Code: Dog d = new Dog("Snoopy"); d.bark(); WolfDog wd = new WolfDog("Juno"); wd.bark(); wd.howl(); System.out.println( d.toString() ); System.out.println( wd.toString() ); 1 3. We can define as many subclasses as we want: 4. Check Point questions, p. 416 a. 11.1 True or false? A subclass is a subset of a superclass. b. 11.2 What keyword do you use to define a subclass? c. 11.3 What is single inheritance? What is multiple inheritance? Does Java support multiple inheritance? Section 11.3 – Using the super Keyword The following subsections show how a subclass constructor can call a superclass constructor and how a subclass can call superclass methods. Section 11.3.1 – Calling Superclass Constructors 1. A class (subclass) is not required to have a constructor (although most of the time we have one or more). 2. A class (subclass) can explicitly call a super-class constructor with the use of: super( argList ), which must be the first statement of the constructor. We saw this in the example in Section 11.2: public WolfDog( String name ) { super( name ); } Which calls the superclass matching constructor: public Dog( String name ) { this.name = name; } 2 3. If no super-class constructor is explicitly called, then the default super-class constructor will implicitly be called. In other words, the compiler inserts, super()as the first statement of any constructor that does not explicitly call a superclass constructor. Classes: Sample Code: class A { public A() { System.out.println("A"); } } B b = new B(4); Output: A B class B extends A { public B( int x ) { System.out.println("B"); } } 4. Example – The example on the left will not compile. Since B’s constructor does not explicitly call a superclass constructor, super() is inserted. However, there is no no-arg constructor in A. The compiler detects this and will not compile. The error message is a bit cryptic, so learn to recognize it: error: constructor A in class A cannot be applied to given types; Incorrect: Correct class A { public A( int x ) { System.out.println("A"); } } class A { public A() {} class B extends A { public B( int x ) { System.out.println("B"); } } } public A( int x ) { System.out.println("A"); } class B extends A { public B( int x ) { System.out.println("B"); } } 3 Section 11.3.2 – Constructor Chaining 5. A constructor can call another constructor in the same class with: this( argList ) which must be the first statement of the constructor. A constructor in a subclass can call a constructor in the immediate superclass with: super( argList ) which must be the first line in the constructor. Thus, a constructor can call another constructor in the same class (this) or a superclass (super), but not both. 6. Example – What is the output of the sample code (right) using the classes on the left? Classes: Sample Code: class A { public A() { System.out.println("1"); } public A( int x ) { this(); System.out.println("2"); } } B b1 = new B(); B b2 = new B(9); B b3 = new B("s"); Output: 1 2 3 4 Ï 1 2 3 class B extends A { public B() { this( 77 ); System.out.println("4"); } public B( int x ) { super( x ); System.out.println("3"); } public B( String s ) { System.out.println("2"); } } 1 2 5. Check Point questions, p. 419 a. 11.4 What is the output of running the class C? class A { public A() { System.out.println( "A's no-arg constructor is invoked"); } } class B extends A { } public class C { public static void main(String[] args) { B b = new B(); } } 4 b. What problem arises in compiling the program? class A { public A(int x) { } } class B extends A { public B() { } } public class C { public static void main(String[] args) { B b = new B(); } } c. 11.5 How does a subclass invoke its superclass’s constructor? d. 11.6 True or false? When invoking a constructor from a subclass, its superclass’s no-arg constructor is always invoked. Section 11.3.3 – Calling Superclass methods & Section 11.4 – Overriding Methods 1. Java allows us to override an inherited method by writing a method in the subclass with the same signature but different implementation. For example, suppose a WolfDog barks differently than a Dog. 2. Example – In the example below we override the bark method defined in the Dog class Classes: Sample Code: class Dog { ... public void bark() { System.out.println("Bark"); } } WolfDog juno = new WolfDog( "Juno" ); juno.bark(); Output: BarkHowl class WolfDog extends Dog { ... public void bark() { System.out.println("BarkHowl"); } } 5 3. Sometimes you want a subclass to call an overridden method. The syntax is: super.method( argList ); Example: class WolfDog extends Dog { ... public void bark() { super.bark(); System.out.println("BarkHowl"); } } 4. Overloading a method means to provide multiple methods with the same name but with different signatures. The overloaded method can be in the same class (either superclass or subclass) or, an inherited method can be overloaded in a subclass. 5. Check Point questions, p. 420 a. 11.7 True or false? You can override a private method defined in a superclass. b. 11.8 True or false? You can override a static method defined in a superclass. c. 11.9 How do you explicitly invoke a superclass’s constructor from a subclass? d. 11.10 How do you invoke an overridden superclass method from a subclass? Section 11.6 – The Object Class and its toString() Method 1. Every class in Java is-a Object. For instance, these two classes are identical public class Dog {} public class Dog extends Object {} If no inheritance is specified then the superclass is Object by default. Thus, the root of all classes (inheritance hierarchies) is the Object class which is found in the java.lang package: http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html 6 2. The toString method is defined in the Object class. The default implementation is to return the name of the class and the memory location (hex), something like: : Dog@334eb8. 3. When you print an object, the toString method is ultimately called. If it has not been overriden, then the Object classes toString will be executed as described above. Thus, these two statements are equivalent: System.out.println( dog ); System.out.println( dog.toString() ); Every class you write should override the toString method to give a meaningful description of the state of the object. If you do this work upfront, as you write each class, it will pay off greater when it comes time to test. Section 11.7 – Polymorphism 1. Every variable has a reference: Dog snoopy = new Dog( "Snoopy" ); WolfDog juno = new WolfDog( "Juno" ); Since Dog is a superclass of WolfDog, we also say that Dog is a supertype reference and WolfDog is a subtype reference. 2. Suppose the Dog class provides the following method: public class Dog { ... public void barkAt(Dog d) { StringBuilder msg = new StringBuilder( this.getName() ); msg.append(" barks at "); msg.append( d.getName() ); System.out.println(msg); } } We can call this method this way: Dog d1 = new Dog("Snoopy"); Dog d2 = new Dog("GiGi"); WolfDog wd = new WolfDog("Juno"); d1.barkAt(d2); d1.barkAt(wd); 3. Polymorphism means that a variable of a supertype can refer to a subtype object. In the example above. The barkAt method uses a supertype reference (Dog) for its parameter. However, we can pass an subtype instance (WolfDog) as an argument. You can always pass an instance of a subclass to a parameter defined as a supertype. 7 4. Example – Consider these classes: class A { public String toString() { return "A"; } } class B extends A { public String toString() { return "B"; } } class C extends B {} And suppose we define these static methods: public static void m( A a ) { System.out.println( a ); } public static void m2( C c ) { System.out.println( c ); } Consider the example method calls below. State whether each will compile, generate a run-time error, or what output will it produce? a. b. c. d. e. m( new A() ); m( new B() ); m( new C() ); m2( new C() ); m2( new B() ); 8 Section 11.8 – Dynamic Binding 1. Example public class Dog { ... public void bark() { System.out.println("bark"); } } public class WolfDog extends Dog { ... public void bark() { System.out.println("BARK Growl"); } public void howl() { System.out.println("howl"); } } Now, we can use a supertype reference to refer to a subclass object: Dog sam = new WolfDog("Sam"); And we can tell the dog to bark: sam.bark(); The actual bark method that runs is determined by the actual instance of the object not the reference type. Thus, the instance, sam is a WolfDog, so the WolfDog’s bark method is called. This is dynamic binding. 2. The JVM goes through a process like this to implement dynamic binding Get the type of the instance. If ( instance contains a definition for the behavior ) Execute behavior Else Find behavior in superclass & execute Thus, the method is bound to the call at run-time (dynamically). 3. Non-software example – Saxophone, Trumpet, and Trombone are subclasses of Horn. In an orchestra, we can refer (polymorphically) to the horn section. When the conductor asks the horns to play a few bars, they each play differently because they are different instruments (instances). This is dynamic binding. 4. Polymorphism and dynamic binding is really useful because it allows us to write code that uses supertypes that will operate on any subtypes (even ones that haven’t been written yet!). This gives programs flexibility and extensibility. It makes programs generic in a sense. It probably will take a long time before this fully sinks in. 9 5. In the example immediately above: Dog sam = new WolfDog("Sam"); sam.bark(); We can tell the dog to bark, but we cannot tell it to howl even though the instance is a WolfDog. sam.howl(); // Doesn’t compile You can think of it this way: the reference type is a lens through which you can view the instance. The lens belongs to the reference (superclass), not the instance. Thus, the only thing you can see through the lens is the public members as defined by the reference type, not the instance. The instance may have other behaviors, but the lens can’t see them. We can assign a subclass instance to a supertype, but the reference cannot see any behaviors that the subclass may have defined. To access those, we must use a reference with the subtype. 6. Consider the class hierarchy shown on the left, below. Thus, if a variable has a reference type of W, we can use it to hold references to an instance of W or any of its descendants (classes that extend W), X, Y, or Z, as shown in the figure below. Class Diagram Relationships an X is-a W a Y is-a W a Z is-a X a Z is-a W. an X is not a Y a Z is not a Y. References W X Y Z w x y z = = = = new new new new W(); X(); Y(); Z(); w=x; w=y; w=z; x=z; But 7. Example 8 – An Array of Dogs Classes Example Code Dog snoopy = new Dog("Snoopy"); WolfDog juno = new WolfDog("Juno"); FeralDog wildy = new FeralDog("Wildy"); Dog[] dogs = new Dog[3]; dogs[0] = snoopy; dogs[1] = juno; dogs[2] = wildy; for( Dog d : dogs ) d.run(); 10 Thus, we can define a Dog array which can store references to a Dog, a WolfDog, and a FeralDog. And, we can treat this collection of Dogs polymorphically. That is, we loop through the dogs and call the run method on each, not caring what the actual type is. We rely on dynamic binding to make sure the correct run method gets called. Section 11.9 – Casting Objects and the instanceof Operator 1. Casting refers to changing the reference type of an instance. You can always cast instances of a subclass to a variable of a supertype. This is known as upcasting or implicit casting. For instance, all these statements are legal: // Implicit Casting Object obj = new WolfDog( "Juno" ); Dog dog = new WolfDog( "Juno" ); 2. It is important to remember, the actual instance is a WolfDog, however you can only access instance through the “lens” of the reference type. For instance, dog cannot call the howl method because only the methods specified in the Dog class can be called. Thus, these statements would generate a compile error: obj.bark(); obj.run(); dog.howl(); However, through dynamic binding, dog.run() excecutes the run method in the WolfDog class. 3. Explicit Casting is used to cast from a superclass to a subclass. This is called downcasting. Eamples Correct (Explicit Cast) Dog dog = new WolfDog(); WolfDog wdog = (WolfDog)dog; Incorrect Dog dog = new WolfDog(); WolfDog wdog = dog; ***Compile error: incompatible types Correct (Implicit Cast) Incorrect WolfDog wdog = new WolfDog(); Dog dog = wdog; Dog dog = new Dog(); Person p = (Person)dog; Correct, but unnecessary: ***Compile error: inconvertible types Dog dog = (Dog)wdog; 11 4. For downcasting to be successful, you must make sure that the object being cast (dog) is really an instance of the subclass (WolfDog). Thus, a safe way to cast is to use the instanceof operator Dog dog = new WolfDog( "Juno" ); WolfDog wdog; if( dog instanceof WolfDog ){ wdog = (WolfDog)dog; wdog.howl(); } else System.out.println( "Can't cast as WolfDog" ); 5. The instanceof operator returns true for any class along the inheritance chain the class was created from. Thus, Dog juno = new WolfDog( "Juno" ); System.out.println( juno instanceof Object ); System.out.println( juno instanceof Dog ); System.out.println( juno instanceof WolfDog ); // true // true // true Dog dog = new Dog( "Binker" ); System.out.println( dog instanceof Object ); System.out.println( dog instanceof Dog ); System.out.println( dog instanceof WolfDog ); // true // true // false 6. You should always use reference variables with a type as high up the inheritance hierarchy as possible. For instance, we should use a Dog to reference an instance of a WolfDog (assuming we don’t need the howl method). 12 Section 11.10 – The Object’s equal’s Method 1. The Object class defines an equals method that allows you to compare two objects of the same class to see if they are “equal”. For instance, since every class is an Object, we can write: Person person1 = new Person(); Person person2 = new Person(); If ( person1.equals(person2) ) 2. The implementation of equals in the Object class returns true when the two instances are in fact the same instance. In other words, the comparison above would return false. The one below would return true: Person person1 = new Person(); Person person2 = person1; If ( person1.equals(person2) ) 3. It is convenient at times to override the equals method and supply our own, custom definition of equals. For example, suppose we have two different Person instances, but want to consider them equal if their SSN’s are the same. To override a method, we need to know it’s signature. The equals method defined in the Object class has this signature: public boolean equals(Object o) As stated, this method returns true if the two objects are the same instance (==). Overriding the equals method is important when we consider collections later in this course. 4. Example: public class Person { public int ssn; public Person( int ssn ) { this.ssn = ssn; } public boolean equals(Object o) { return this.ssn == ((Person)o).ssn; } public static void main( String[] args ) { Person p1 = new Person( 123 ); Person p2 = new Person( 123 ); System.out.println( p1.equals(p2) ); } } 13 // true