Title: 7:%20Polymorphism
17 Polymorphism
- Upcasting revisited
- Forgetting the object type
- The twist
- Method-call binding
- Producing the right behavior
- Extensibility
- Overriding vs. overloading
- Abstract classes and methods
- Constructors and polymorphism
- Designing with inheritance
- Exercises
27 Polymorphism
- Polymorphism is the third essential feature of an
object-oriented programming language, after data
abstraction and inheritance.
37 Polymorphism
- // Wind objects are instruments
- // because they have the same interface
- class Wind extends Instrument
- // Redefine interface method
- public void play(Note n)
- System.out.println("Wind.play()")
-
-
- public class Music
- public static void tune(Instrument i)
- i.play(Note.MIDDLE_C)
-
- public static void main(String args)
- Wind flute new Wind()
- tune(flute) // Upcasting
-
-
class Note private int value private
Note(int val) value val public static
final Note MIDDLE_C new Note(0),
C_SHARP new Note(1), B_FLAT new
Note(2) // Etc. class Instrument public
void play(Note n) System.out.println("Instrum
ent.play()")
4The twist
- The difficulty with Music.java can be seen by
running the program. The output is Wind.play( ).
This is clearly the desired output, but it
doesnt seem to make sense that it would work
that way. Look at the tune( ) method - public static void tune(Instrument i) // ...
i.play(Note.MIDDLE_C) -
- It receives an Instrument reference. So how can
the compiler possibly know that this Instrument
reference points to a Wind in this case and not a
Brass or Stringed? The compiler cant.
5Method-call binding
- Connecting a method call to a method body is
called binding. - early binding When binding is performed before
the program is run (by the compiler and linker) - You might not have heard the term before because
it has never been an option with procedural
languages. - C compilers have only one kind of method call,
and thats early binding. - late binding means that the binding occurs at
run-time based on the type of object. - Late binding is also called dynamic binding or
run-time binding. - There must be some mechanism to determine the
type of the object at run-time and to call the
appropriate method. - The compiler still doesnt know the object type.
6Producing the right behavior
- The classic example in OOP is the shape
example. This is commonly used because it is easy
to visualize.
7- class Shape
- void draw()
- void erase()
-
- class Circle extends Shape
- void draw() System.out.println("Circle.draw()")
void erase() System.out.println("Circle.eras
e()") -
- class Square extends Shape
- void draw() System.out.println("Square.draw()")
void erase() System.out.println("Square.eras
e()") -
- class Triangle extends Shape
- void draw() System.out.println("Triangle.draw()
") void erase() System.out.println("Triangle.
erase()") -
public class Shapes public static Shape
randShape() // Upcasting switch((int)(Math
.random() 3)) default case 0 return new
Circle() case 1 return new Square() case
2 return new Triangle() public
static void main(String args) Shape s
new Shape9 // Fill up the array with
shapes for(int i 0 i lt s.length i)
si randShape() // Make polymorphic
method calls for(int i 0 i lt s.length i)
si.draw() /// Circle.draw()
Triangle.draw() Circle.draw() Circle.draw()
Circle.draw() Square.draw() Triangle.draw()
Square.draw() Square.draw()
8Exercise
- class Shape
- void draw()
- void erase()
- void flash() draw() erase() draw()
-
- class Circle extends Shape
- void draw() System.out.println("Circle.draw()")
- void erase() System.out.println("Circle.erase()
") -
- class Square extends Shape
- void draw() System.out.println("Square.draw()")
- void erase() System.out.println("Square.erase()
") -
- class Triangle extends Shape
- void draw() System.out.println("Triangle.draw()
") - void erase() System.out.println("Triangle.erase
()") -
- public class Shapes ?main
- for(int i 0 i lt s.length i) si. flash()
9Extensibility
- Because of polymorphism, you can add as many new
types as you want to the system without changing
the tune( ) method. - All these new classes work correctly with the
old, unchanged tune( ) method.
10- import java.util.
- class Instrument
- public void play() System.out.println("Instrume
nt.play()") - public String what() return "Instrument"
- public void adjust()
-
-
- class Wind extends Instrument
- public void play() System.out.println("Wind.pla
y()") - public String what() return "Wind"
- public void adjust()
-
- class Percussion extends Instrument
- public void play() System.out.println("Percussi
on.play()") - public String what() return "Percussion"
- public void adjust()
-
class Stringed extends Instrument class
Woodwind extends Wind class Brass extends
Wind public
class Music3 // Doesn't care about type, so
new types static void tune(Instrument i) //
... i.play() static void tuneAll(Instrument
e) for(int i 0 i lt e.length i)
tune(ei) public static void
main(String args) Instrument orchestra
new Instrument5 int i 0 //
Upcasting during addition to the array
orchestrai new Wind() orchestrai
new Percussion() orchestrai new
Stringed() orchestrai new Brass()
orchestrai new Woodwind()
tuneAll(orchestra) ///
11Overriding vs. overloading
- class NoteX
- public static final int MIDDLE_C 0, C_SHARP
1, C_FLAT 2 -
- class InstrumentX
- public void play(int NoteX) System.out.println(
"InstrumentX.play()") -
-
- class WindX extends InstrumentX
- // OOPS! Changes the method interface
- public void play(NoteX n) System.out.println("
WindX.play(NoteX n)") -
- public class WindError
- public static void tune(InstrumentX i)
i.play(NoteX.MIDDLE_C) - public static void main(String args)
- WindX flute new WindX()
- tune(flute) // Not the desired behavior!
-
- ///
12Abstract classes and methods
- In above examples, the methods in the base class
Instrument were always dummy methods. - If these methods are ever called, youve done
something wrong. - The intent of Instrument is to create a common
interface for all the classes derived from it. - Java provides a mechanism for doing this called
the abstract method. A class containing abstract
method is a abstract class. - abstract method is a method that is incomplete
it has only a declaration and no method body. - abstract void f()
- It cannot create an object of an abstract class
- If you inherit from an abstract class and you
want to make objects of the new type, you must
override (redefine) all the abstract methods in
the base class. - If you dont (and you may choose not to), then
the derived class is also abstract.
13Abstract classes and methods
- import java.util.
- abstract class Instrument
- int i // storage allocated for each
- public abstract void play()
- public String what() return "Instrument"
- public abstract void adjust()
-
- class Wind extends Instrument
- public void play()
- System.out.println("Wind.play()")
-
- public String what() return "Wind"
- public void adjust()
-
14Designing with inheritance
- Once you learn about polymorphism, it can seem
that everything ought to be inherited because
polymorphism is such a clever tool. This can
burden your designs - A better approach is to choose composition first,
when its not obvious which one you should use. - A Stage object contains a reference to an Actor,
which is initialized to a HappyActor object. At
run-time, a reference for a SadActor object can
be substituted in a and then the behavior
produced by go( ) changes. - Thus you gain dynamic flexibility at run-time.
(This is also called the State Pattern. )
15Designing with inheritance
- // Dynamically changing the behavior of // an
object via composition. - abstract class Actor
- abstract void act()
-
- class HappyActor extends Actor
- public void act() System.out.println("HappyActo
r") -
- class SadActor extends Actor
- public void act() System.out.println("SadActor"
) -
- class Stage
- Actor a new HappyActor()
- void change() a new SadActor()
- void go() a.act()
-
- public class Transmogrify
- public static void main(String args)
- Stage s new Stage()
- s.go() // Prints "HappyActor"
16Downcasting and run-time type identification
- While upcasting is a useful and sensible approach
(depending on the situation) it has a drawback.
The extended part of the interface in the derived
class is not available from the base class, so
once you upcast you cant call the new methods
17Downcasting and run-time type identification
- import java.util.
- class Useful
- public void f()
- public void g()
-
- class MoreUseful extends Useful
- public void f()
- public void g()
- public void u()
- public void v()
- public void w()
import java.util. public class RTTI public
static void main(String args) Useful x
new Useful(), new MoreUseful() x0.f()
x1.g() // Compile-time method not found
in Useful //! x1.u() //
Downcast/RTTI ((MoreUseful)x1).u() //
Exception thrown ((MoreUseful)x0).u()
///
18Downcasting and run-time type identification
- Since you lose the specific type information via
an upcast (moving up the inheritance hierarchy),
you can use a downcast to retrieve the type
information (moving down the inheritance
hierarchy ) - An upcast is always safe
- the base class cannot have a bigger interface
than the derived class, therefore every message
you send through the base class interface is
guaranteed to be accepted. - But a downcast is unsafe,
- A shape (for example) may be a circle or a
triangle or a square or some other type. - In Java every cast is checked!
- At run-time this cast is checked to ensure that
it is in fact the type you think it is. - If it isnt, you get a ClassCastException. This
act of checking types at run-time is called
run-time type identification (RTTI).