Inheritance #
Subclass #
In most (class-based) object-oriented languages, a class $A$ can extend another class $B$. In this case, $A$ is called a subclass of $B$.
The intuitive meaning is inclusion ($\subseteq$) between their respective sets of instances, i.e. every instance of $A$ is also an instance of $B$ (but the converse may not hold).
Hint.
$\qquad$ “$A$ extends $B$”
can be paraphrased as
$\qquad$ “every $A$ is a $B$”.
Examples.
- every banana is a fruit,
- every square is a rectangle,
- every rectangle is a polygon.
In each of the following cases, could $A$ extend $B$ ?
| $A$ | $B$ |
|---|---|
| Student | Vegetarian |
| Polygon | Hexagon |
| Continent | Country |
| Country | Continent |
| City | Country |
| ZoomMeeting | Meeting |
| ZoomMeeting | Calendar |
| Chapter | Book |
| Minute | TimeInterval |
| Hour | TimeInterval |
| Minute | Hour |
| Integer | RationalNumber |
| RationalNumber | RealNumber |
| Integer | RealNumber |
| SetOfStudents | Student |
| Student | SetOfStudents |
| SetOfStudents | Set |
| SetOfStudents | SetOfPeople |
| ArrayOfIntegers | ArrayOfRealNumbers |
| ArrayOfIntegers | SetOfIntegers |
| UnionOfSets | Set |
| Set | UnionOfSets |
| Tree | Graph |
| Graph | Tree |
| AcyclicGraph | Tree |
| Object | Class |
| Subclass | Class |
| Class | Subclass |
Transitivity #
If $A$ extends $B$ and $B$ extends $C$, then $A$ extends $C$
Example. In the exercise above,
IntegerextendsRationalNumberandRationalNumberextendsRealNumber, thereforeIntegerextendsRealNumber.
Antisymmetry #
If $A$ extends $B$ and $B$ extends $A$, then they are the same class.
Example. In the exercise above,
SetandUnionOfSetsare (arguably) the same class.
Inheritance #
Naturally, if $A$ extends $B$, then it inherits the properties of $B$.
Example. A person has a birth date.
Since every student is a person, a student also has a birth date.
Example. A rectangle has four right angles.
Since every square is a rectangle, a square also has four right angles.
Factorizing code with a superclass (in Java) #
Inheritance can be used to avoid redundant code.
Running example #
Example. We will use an imaginary game, where each non-player character (NPC) has:
- a type: unicorn, butterfly, fairy, …,
- a certain amount of experience (XP),
- a certain number of remaining moves.
Besides, some properties and behaviors of an NPC are dictated by its type (e.g. its initial XP).
We will model the NPCs of this game as objects.
First, we can create a class
Unicornwhose instances are all NPCs of type unicorn.
In Java:public class Unicorn { int xp; int moves; public Unicorn(int moves) { xp = 5; this.moves = moves; } }Note. In this example, we use the prefix
this.for the attributemovesonly, because there is no ambiguity for the other attribute.We also create a class
Butterflyon the same model:public class Butterfly { int xp; int moves; public Butterfly(int moves) { xp = 2; this.moves = moves; } }
Implement a method
void meets(Unicorn u1, Unicorn u2)
that manages a meeting between two unicorns, with the effect that each unicorn gains the other’s XP.
For instance, if u1.xp has value 5 and u2.xp has value 7, then after the meeting,
u1.xp and u2.xp both have value 12.
Example (continued). Let us assume that we also want to manage meetings between:
- a unicorn and a butterfly and
- a butterfly and a butterfly,
with the same effect as a meeting between two unicorns.
Without inheritance, one would need to implement two additional (nearly identical) versions of the method
meets.More generally, if the game has $n$ types of NPCs, then the program would contain $\frac{n(n-1)}{2} + n$ nearly identical
meetsmethods.
Question. Can we use inheritance in this example to avoid duplicate code (and how)?
Example (continued). Observe that a unicorn and a butterfly (viewed as object) have identical attributes, namely
int xpandint moves. So we can create a superclassNPCofUnicornandButterflythat carries these attributes, and let the two subclasses inherit them.
Abstract class #
Example (continued). We may also force every NPC in the game to have a concrete type (like “unicorn” or “butterfly”), rather than being a generic “NPC”.
Syntax. In Java, the keyword
abstractensures that a class cannot be directly instantiated (even though it can have a constructor).
Notation. In the diagram above:
- the blue circled “A” indicates an abstract class,
- the green circled “C” indicates a regular (i.e. directly instantiated) class.
Example (continued). Here is a possible implementation of the abstract class
NPCpublic abstract class NPC { int xp; int moves; public NPC(int xp, int moves) { this.xp = xp; this.moves = moves; } }Because this class is abstract, the following code does not compile:
// Compilation error: the class is abstract, so it cannot be directly instantiated. NPC c = new NPC(2,3);
Subclass #
Syntax.
- the keyword
extendslets us declare that a class is a subclass of another- the keyword
superlets us use the constructor of the superclass inside the subclass.
Example (continued). We can now rewrite our class
Unicorn, as follows:
- declare that
Unicornis a subclass ofNPC, withextends, and- (optionally) use the constructor of
NPCwithin the constructor ofUnicorn, withsuper.This yields:
public class Unicorn extends NPC { public Unicorn(int moves) { super(5, moves); } }And we can proceed similarly for the class
Butterfly:public class Butterfly extends NPC { public Butterfly (int moves) { super(2, moves); } }
Restriction. In Java (as opposed to C++ for instance), a class can only have one immediate superclass.
So the keyword
superis never ambiguous.
Example (continued). Both attributes (
xpandmoves) are now carried by the superclassNPC.Because they are inherited, these attributes can still be accessed as if they were regular attributes of the subclass.
For instance,Unicorn u = new Unicorn(3); System.out.println(u.xp);outputs
5This allows us to write a generic method
meets, as follows:void meets(NPC c1, NPC c2) { int tmp = c1.xp; c1.xp += c2.xp; c2.xp += tmp; }And this method can be called with unicorns and/or butterflies as inputs.
For instance:Unicorn u = new Unicorn(1); Butterfly b = new Butterfly(4); meets(u, b);
Transitive inheritance #
Example (continued). In the example above, we assumed that all NPCs can move.
Let us now add a type of NPCs called
Flower, which cannot move. An instance of this class does not need the attributemoves.In order to model this, a quick solution consists in setting
movesto a special value (e.g.-1) for the classFlower.
This is unsatisfactory, because:
- unnecessary attributes make code harder to understand, and
- this is a potential source of bugs, when the attribute
movesfor a flower is accidentally accessed.
Modify our model to accommodate for the class Flower,
so that an instance of Flower only has the xp attribute, with (default) value 3.