Chapter 14 - Polymorphism
- Polymorphism in Programming Languages
- Polymorphic object is any entity (variable, function argument, etc.)
that is permitted to hold values of different types during its
lifetime.
- Polymorphic functions have polymorphic arguments.
- Polymorphic Functions in Dynamic Languages
- Easy to do in dynamic languages.
- Accomplished in static languages in several ways
- Overloading is most common form
- parametric overloading as defined here is the same as templates
in C++. C++ defines parametric overloading differently.
- Low-Level and High-Level Abstractions
- Low-level abstraction is a basic operation that is built on top
of only a few underlying mechanisms (swap())
- High-level abstraction - actual implementation of a full algorithm
such as quicksort(). In imperative (procedural) programming, you
have to rewrite the algorithm from scratch for each new data type.
Idea of OOP is to only have to change a couple of low-level
abstractions to get the full implemention. Library version of
qsort() in C does this. You only have to provide a comparison
function (low-level) to the qsort() (high-level) to do the job.
- Polymorphism make using the high-level abstractions without having
to rewrite the abstraction easy.
- Varieties of Polymorphism
- Polymorphism is a natural result of the is-a relationship.
- Pure polymorphism occurs when a single function can be applied to
arguments of a variety of types.
- Polymorphic Variables
- Polymorphic variable - can hold values of many different types during
its lifetime.
- Its dynamic type need not be the same as its static type.
- There is an "expected" type for variables but its "actual" type can
be from any value that is a subtype of the expected type.
- Dynamic languages - all variables are potentially polymorphic and
the expected type is defined as a set of behaviors.
- Static languages - polymorphism occurs through the difference between
the declared (static) type and the actual (dynamic) type. In many of
the static languages, the polymorphic variable can only hold values
of its declared type and any subtype of that declared type.
- Overloading
- Function NAME is overloaded if there are two or more function bodies
associated with that name. The NAME is polymorphic. Which body to
execute depends on the arguments given. (The compiler can determine
which body to execute in strongly typed languages.)
- Overloading Names in Real Life (and in general)
- Names of variables and functions can be reused with any given
implementation without any problem as long as they are completely
enclosed in different scopes. If they are in the same scope, then
problems can arise. Often the "local" version of the variable name
hides the variable that is in an outer scope from view.
- Overloading and coercion - if the types of the "actual" arguments
can be coerced into matching the types of the "formal" arguments,
then the appearance of overloading is present but may NOT be
actually present.
- Overloading does NOT imple similarity - it is up to the programmer
to make sure that all functions with the same name behave in a
similar and/or appropriate manner.
- Parametric overloading - one function name, one scope, multiple
bodies where the number and/or type of arguments differ. The
determination of which body to execute is made based on the type
and/or number of arguments.
- Overriding
- Overriding - in at least one subclass, there is a method with the same
name and parameters and return type as a method in the parent class.
This effectively hides this subclasses' access to the parent method.
- Refinement - the child class method executes the parent class's method
as part of its code. (Substitutibility guaranteed)
- Replacement - the child class method does whatever is appropriate for
it with no reliance on the parent method (Substitutibility not guaranteed)
- Deferred Methods
- Sometimes called an abstract method - called pure virtual method in C++
- Can be thought of as a generalization of overriding. The parent class
describes the method (prototype idea) but does NOT implement the
behavior.
- The child class MUST implement the behavior described in the parent by
overriding the parent method.
- Typically, the parent class is considered and cannot be instantiated.
- In static languages, all behavior that we want to be polymorphic must
be described in the parent class. Using deferred methods, we get just
this without having to implement the bahavior in the parent.
- This also guarantees that all children of the parent class will have
a similar interface.
- Pure Polymorphism
- One function can receive a variety of argument types
- In most cases, only languages that are dynamic are considered to have
the mechanisms to provide pure polymorphism.
- Should be able to implement the ideal. List class which can deal with
a list of anything including other lists, etc. No type considerations
are required.
- Generics and Templates
- C++ uses term template
- Idea is that we have a general purpose algorithm (often a container)
that is designed to work with a variety of types. However, we need to
specify which type to use THIS time.
- This idea is often called parameterized types. I.e. the actual type to
use for this instantiation is "passed" to the generic or template. Then
the "correct" type code is generated for use.
- This generating of the correct code for the instance is often done at
compile time.
- Polymorphism in the Various Languages
- Efficiency and Polymorphism
- Polymorphism use does reduce efficiency.
- However, using polymorphism does make it easier to develop projects,
make implementations easier to use, and make them easier to read.