Polymorphism 1 Upcasting revisited Taking an object reference and

Polymorphism
2017/7/28
1
Upcasting revisited
• Taking an object reference and
treating it as a reference to its base
type is called upcasting because of the
way inheritance trees are drawn with
the base class at the top.
2017/7/28
2
Forgetting the object type
• Why should anyone intentionally
forget the type of an object?
• This is what happens when you upcast.
• It is nice if you could forget that there
are derived classes, and write your
code to talk only to the base class.
2017/7/28
3
Method-call binding
• Connecting a method call to a method
body is called binding. When binding
is performed before the program is
run (by the compiler and linker, if
there is one), it’s called early binding.
• And the late binding means that the
binding occurs at run time, based on
the type of object.
• Late binding is also called dynamic
binding or runtime binding.
2017/7/28
4
• All method binding in Java uses late
binding unless the method is static or
final (private methods are implicitly
final). This means that ordinarily you
don’t need to make any decisions
about whether late binding will
occur—it happens automatically.
2017/7/28
5
• Why would you declare a method final? As
noted in the last chapter, it prevents anyone
from overriding that method. Perhaps more
important, it effectively “turns off” dynamic
binding, or rather it tells the compiler that
dynamic binding isn’t necessary. This allows
the compiler to generate slightly more
efficient code for final method calls.
• However, in most cases it won’t make any
overall performance difference in your
program, so it’s best to only use final as a
design decision, and not as an attempt to
improve performance.
2017/7/28
6
Producing the right behavior
• “send a message to an object and let
the object figure out the right thing to
do.”
• you can write your code to talk to the
base class and know that all the
derived-class cases will work correctly
using the same code.
2017/7/28
7
Classic Shape Example
• The shape example has a base class
called Shape and various derived
types: Circle, Square, Triangle, etc.
2017/7/28
8
Extensibility
• Because of polymorphism, you can
add as many new types as you want
to the system without changing the
method. In a well-designed OOP
program, most or all of your methods
will follow the model of this method
and communicate only with the baseclass interface.
2017/7/28
9
• Such a program is extensible because
you can add new functionality by
inheriting new data types from the
common base class.
• The methods that manipulate the
base-class interface will not need to
be changed at all to accommodate the
new classes.
2017/7/28
10
Example
2017/7/28
11
Pitfall: “overriding” private
methods
• Only non-private methods may be
overridden, but you should watch out
for the appearance of overriding
private methods, which generates no
compiler warnings, but doesn’t do
what you might expect.
• To be clear, you should use a different
name from a private base-class
method in your derived class.
2017/7/28
12
Pitfall: fields and static methods
2017/7/28
• For one thing, you’ll generally make
all fields private and so you won’t
access them directly, but only as side
effects of calling methods. In addition,
you probably won’t give the same
name to a base-class field and a
derived-class field, because its
confusing.
• If a method is static, it doesn’t behave
polymorphically.
13
Constructors and polymorphism
• Constructors are different from other
kinds of methods. This is also true
when polymorphism is involved.
• Even though constructors are not
polymorphic (they’re actually static
methods, but the static declaration is
implicit), it’s important to understand
the way constructors work in complex
hierarchies and with polymorphism
2017/7/28
14
Order of constructor calls
• A constructor for the base class is
always called during the construction
process for a derived class, chaining
up the inheritance hierarchy so that a
constructor for every base class is
called.
• This makes sense because the
constructor has a special job: to see
that the object is built properly.
2017/7/28
15
• A derived class has access to its own
members only, and not to those of the
base class (whose members are
typically private). Only the base-class
constructor has the proper knowledge
and access to initialize its own
elements.
• Therefore, it’s essential that all
constructors get called; otherwise the
entire object wouldn’t be constructed.
2017/7/28
16
• It will silently call the default
constructor if you don’t explicitly call
a base-class constructor in the
derived-class constructor body.
• If there is no default constructor, the
compiler will complain.
2017/7/28
17
• When you inherit, you know all about
the base class and can access any public
and protected members of the base class.
• You must be able to assume that all the
members of the base class are valid
when you’re in the derived class.
• The only way to guarantee this is for the
base-class constructor to be called first.
Then when you’re in the derived-class
constructor, all the members you can
access in the base class have been
initialized
2017/7/28
18
Inheritance and cleanup
• If you do have cleanup issues, you
must be diligent and create a dispose( )
method (the name I have chosen to
use here; you may come up with
something better) for your new class.
• And with inheritance, you must
override dispose( ) in the derived class
if you have any special cleanup that
must happen as part of garbage
collection.
2017/7/28
19
• When you override dispose( ) in an
inherited class, it’s important to
remember to call the base-class
version of dispose( ), since otherwise
the base-class cleanup will not
happen.
2017/7/28
20
Behavior of polymorphic
methods inside constructors
• If you call a dynamically-bound
method inside a constructor, the
overridden definition for that method
is used.
• However, the effect of this call can be
rather unexpected because the
overridden method will be called
before the object is fully constructed.
This can conceal some difficult-to-find
bugs
2017/7/28
21
Actual process of initialization
• 1. The storage allocated for the object is
initialized to binary zero before anything else
happens.
• 2. The base-class constructors are called as
described previously. At this point, the
overridden draw( ) method is called (yes,
before the RoundGlyph constructor is called),
which discovers a radius value of zero, due to
Step 1.
• 3. Member initializers are called in the order
of declaration.
• 4. The body of the derived-class constructor
is called.
2017/7/28
22
Covariant return types
• Java SE5 adds covariant return types,
which means that an overridden
method in a derived class can return a
type derived from the type returned
by the base-class method.
2017/7/28
23
Designing 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, especially when it’s
not obvious which one you should use.
2017/7/28
24
Substitution vs. extension
• t would seem that the cleanest way to
create an inheritance hierarchy is to
take the “pure” approach. That is,
only methods that have been
established in the base class are
overridden in the derived class.
2017/7/28
25
Pure substitution
2017/7/28
26
• This can be called a pure “is-a”
relationship because the interface of a
class establishes what it is. Inheritance
guarantees that any derived class will
have the interface of the base class and
nothing less. If you follow this diagram,
derived classes will also have no more
than the base-class interface.
• This can be thought of as pure
substitution
2017/7/28
27
• That is, the base class can receive any
message you can send to the derived
class because the two have exactly
the same interface.
2017/7/28
28
Extension
2017/7/28
29
• This can be termed an “is-like-a”
relationship, because the derived
class is like the base class—it has the
same fundamental interface—but it
has other features that require
additional methods to implement
2017/7/28
30
Downcasting and runtime
type information
• to move back down the inheritance
hierarchy—you use a downcast.
• There must be some way to
guarantee that a downcast is correct,
so that you won’t accidentally cast to
the wrong type and then send a
message that the object can’t accept.
This would be quite unsafe
2017/7/28
31
• in Java, every cast is checked. You get
a ClassCastException if error occurred.
• This act of checking types at run time
is called runtime type identification
(RTTI).
2017/7/28
32
Summary
• Polymorphism means “different forms.” In
object-oriented programming, you have the
same interface from the base class, and
different forms using that interface: the
different versions of the dynamically bound
methods.
• To use polymorphism—and thus objectoriented techniques—effectively in your
programs, you must expand your view of
programming to include not just members
and messages of an individual class, but also
the commonality among classes and their
relationships with each other.
2017/7/28
33