CS 1302 – Ch 11 – Inheritance & Polymorphism

advertisement
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
Download