15.3 Inheritance
Earlier we mentioned that in the real world an object may possess a property in virtue of being the particular kind of thing that it is, or it may possess that property in virtue of being a member of some more general class of things. Thus a dog can eat, not because it is a dog per se, but because it is an animal. You could say that dogs “inherit” the capacity to eat from the class Animal.
Most object-oriented frameworks allow for some type of inheritance between classes, and R6 is no exception. In the Wizard of Oz lions appear to be a special type of person: after all, they can speak, and they have desires. Accordingly, we should create a new class Lion that inherits from the class Person:
<- R6Class("Lion",
Lion inherit = Person,
public = list(
weight = NULL,
set_weight = function(val) {
$weight <- val
selfcat(self$name, " has weight: ", val, ".\n", sep = "")
},greet = function() {
cat("Grr! My name is ", self$name, "!", sep = "")
}
) )
The key to inheritance is the new argument in the code above:
inherit = Person
The impact of this additional argument is that any instance of the class Lion will possess by default all of the attributes and methods of class Person, in addition to the new attributes and methods (weight
, set_weight
, and greet
) specified explicitly in the definition of class Lion. Of these three explicitly-defined members:
weight
andset_weight
were not already in Person, so they are simply added on as members of Lion;37greet
was already a member of Person, but the new specification ofgreet
in the definition of Lion overrides the old one. Any instance of class Lion will greet us according to the code forgreet()
given in the definition of Lion. (Objects that are instances of the class Person, however, will go on greeting us just as they always have done.)
Let’s verify all of this by making an instance of class Lion:
<- Lion$new(
cowardlyLion name = "Cowardly Lion",
age = 18, desire = "courage"
)
## Grr! My name is Cowardly Lion!
Since the definition of class Lion did not specify a new method of initialization—we could have asked for this, but chose not to—the instantiation of a lion won’t set its weight. We will have to do that separately:
$set_weight(350) cowardlyLion
## Cowardly Lion has weight: 350.
Inheritance is an very effective way to re-use code and to keep code for a particular function in one place. Once we have written methods for some class X, those methods will be available to any objects that are instances of classes that inherit from class X. Hence there is no need to repeat the code in the definition of the inheriting classes. Later on if we need to modify one of of the class X methods, we need only make the change in class X: we don’t have to worry about changing it in all of the classes that inherit from X.
Let’s ask cowardlyLion
to greet us:
$greet() cowardlyLion
## Grr! My name is Cowardly Lion!
As expected, the greeting includes a growl, since the greet()
method for class Lion overrides the growl-less greet()
method that it would have otherwise inherited from class Person. We also see a fundamental aspect of message-passing OO at work:
The method used for determining how a particular task is performed is determined by the class of the object that is asked to perform the task.
Here we have the task of greeting, which is common to objects of class Person and to objects of class Lion. That task may be performed with or without a growl. Precisely how it is performed—the method that is selected for carrying out the task—depends upon the class of the object that performs the greeting: an object of class Person will greet you without a growl, whereas an object of class Lion will greet you with a growl.
The common task of greeting may be performed in different ways, depending upon the class of the object involved in the task. This is an example of what programmers call polymorphism. The term “polymorphism” comes from a Greek word that means “many forms,” and it refers to a task being performed differently depending on the class of the object involved. Polymorphism makes programming a bit easier: you only have to remember the name of the one common task. Invoke it on an object and you can rest assured that the task will be performed in the way that is right and proper to that object.
In the real world any person—not just any lion—has a weight. By making weight a feature specific to class Lion we are simply indicating that in our program weight is of particular importance to lions, but not to people in general.↩︎