Inheritance and Polymorphism (p. 121)
A subclass: - inherits all the features from its superclass - can modify certain inherited features - achieved through method overriding
Polymorphism
The quality or state of existing in or assuming different forms. For this chapter: an object or reference may take multiple forms in different instances.
At run time, objects of a derived class may be treated as objects of a base class in places such as method parameters and collections or arrays. When this polymorphism occurs, the object's declared type is no longer identical to its run-time type.1
Dynamic Binding:
The process of linking a procedure call to a specific sequence of code (typically a class method) at runtime.
In static binding, the compiler determines fixed types for all variables and expression. In contrast, dynamic binding allows a method to be looked up by name at runtime.
In other words, dynamic binding allows the compiler to overlook the type discrepancy, making the assumption that an appropriate method will be found at runtime. Because instances of a Child class inherit their Parent class's methods, a runtime search for some Parent method can also be fulfilled by a Child's version of that method.
This is why any Child class can stand in for their Parent class in a method signature.
- Figure 5.4 in book has a visual representation!
Meanings of Inheritance: Module vs. Type View
Module View
A subclass describes an extension of its superclass from the perspective of reusing the superclass's code.
- Problems can arise when a designer
- uses the module view without a meaningful is-a relationship
- does not observe the polymorphism and dynamic binding semantics
Example: Stack<E> as a subclass of Vector<E> (p. 123)
Stack<E>inherits methods fromVector<E>that violate the Stack's LIFO principleadd(0, element)adds to bottom of stack- `remove(0) removes bottom of stack
- A better design would replace inheritance with composition (p. 123)
Type View
A subclass describes a subtype of the superclass type.
- based on "is-a" relation - see definition for "subtype" below
- For example,
intmight be a subtype oflongordouble. - The set of objects of a superclass subsumes the set of objects of a subclass.
- More aligned with the semantics of polymorphism than module view
- Generally preferred to module view
- Consistent with ADTs as the class foundation
- A subclass's ADT is a subtype of its superclass's ADT
Example: Square vs. Rectangle (p. 123, 125)
Square as a subclass of Rectangle
- in math, a square is a special type of rectangle
- if
Squareinherits fromRectangle, it must overrideRectangle'ssetWidth()andsetHeight()methods. - since the classes behave so differently, the is-a relationship isn't ideal.
- there should be no inheritance relation
Subtype
Data type \(t_2\) is a subtype of data type \(t_1\) if every value of \(t_2\) can be assigned to a variable of \(t_1\).
Consider inheritance's relationship with the Open-Closed Principle:
- a subclass may revise an inherited method through overriding while adding new methods.
- open for extension through inheritance
- closed for modification through overriding
Subcontracting
A subclass is a subcontractor of its parent class, meaning it must satisfy at least the same contract. If you subcontract, you must be willing to do the job under the original conditions, no less.
Subcontracting
Design by contract in the context of inheritance with overriding and dynamic binding.
Pre/Postcondition of Method Overriding
Consider a server \(A\) with a descendent \(A_1\). As a result of dynamic binding, a call from client \(C\) may be served by either \(A\) or \(A_1\) at runtime. This means that, to uphold an ancestor class's contract, the descendent must enforce either an equal or weaker precondition or an equal but stronger postcondition.
Subcontracting Rule of Inheritance
An overriding method that has either
- an equal or weaker precondition
- an equal or stronger postcondition
causes no harm to a client call that relies on the original.
So in essence, subcontracting is just a strategy that guarantees descendants of a server do not break a contract expected by a client.
Covariance/Contravariance for Method Overriding
class AnimalShelter {
Animal getAnimalForAdoption();
void putAnimal(Animal animal);
}
class CatShelter extends AnimalShelter {
Cat getAnimalForAdoption();
void putAnimal(Object animal);
}
getAnimalForAdoption()follows a covariant subtyping relationshipCat extends AnimalCatShelter extends AnimalShelter- considered overriding
putAnimal()follows a contravariant subtyping relationshipAnimal extends ObjectCatShelter.putAnimal()takes a more general argument than the class it extends from- considered overloading
Rules of Inheritance
Is-A Rule
Do not make
Binherit fromAunless you can somehow argue that everyBis anA.
The Change Rule of Inheritance
Do not use inheritance to describe a perceived “is-a” relation if the corresponding object components may have to be changed at run time.
Client relations usually permit change while inheritance relations do not.
- If an client of type
Bhas a component (server) of typeA, it is possible to change that component at runtime. - However, no component
Bthat inherits fromAcan change the fact of its own inheritance.
The Polymorphism Rule of Inheritance
Inheritance is appropriate to describe a perceived "is-a" relation if entities of the more general type may need to become attached to objects of the more specialized type through dynamic binding.
Premature Classification:
Inheritance shouldn't be used when a simple instance would suffice. (heirs vs. instances)