One the most useful feature of an object oriented programming language is inheritance.
Defining new classes out of existing ones. This is to say, we can derive new classes from existing base classes and adding new features. We don't have to write from scratch. Hence, inheritance provides reusability of code.
Inheritance forms a hierarchy of class just like a family tree. Important thing to note is that the attributes define for a base class will automatically be present in the derived class.
Moreover, the methods for the base class will work for the derived.
Below, we discuss how inheritance is carried out for the three different class systems in R programming language.
Inheritance in S3 Class
S3 classes do not have any fixed definition. Hence attributes of S3 objects can be arbitrary.
Derived class, however, inherit the methods defined for base class. Let us suppose we have a function that creates new objects of class student
as follows.
student <- function(n,a,g) {
value <- list(name=n, age=a, GPA=g)
attr(value, "class") <- "student"
value
}
Furthermore, we have a method defined for generic function print()
as follows.
print.student <- function(obj) {
cat(obj$name, "\n")
cat(obj$age, "years old\n")
cat("GPA:", obj$GPA, "\n")
}
Now we want to create an object of class InternationalStudent
which inherits from student
.
This is be done by assigning a character vector of class names like class(obj) <- c(child, parent)
.
> # create a list
> s <- list(name="John", age=21, GPA=3.5, country="France")
> # make it of the class InternationalStudent which is derived from the class student
> class(s) <- c("InternationalStudent","student")
> # print it out
> s
John
21 years old
GPA: 3.5
We can see above that, since we haven't defined any method of the form print.InternationalStudent()
, the method print.student()
got called. This method of class student
was inherited.
Now let us define print.InternationalStudent()
.
print.InternationalStudent <- function(obj) {
cat(obj$name, "is from", obj$country, "\n")
}
This will overwrite the method defined for class student
as shown below.
> s
John is from France
We can check for inheritance with functions like inherits()
or is()
.
> inherits(s,"student")
[1] TRUE
> is(s,"student")
[1] TRUE
Inheritance in S4 Class
Since S4 classes have proper definition, derived classes will inherit both attributes and methods of the parent class.
Let us define a class student
with a method for the generic function show()
.
# define a class called student
setClass("student",
slots=list(name="character", age="numeric", GPA="numeric")
)
# define class method for the show() generic function
setMethod("show",
"student",
function(object) {
cat(object@name, "\n")
cat(object@age, "years old\n")
cat("GPA:", object@GPA, "\n")
}
)
Inheritance is done during the derived class definition with the argument contains
as shown below.
# inherit from student
setClass("InternationalStudent",
slots=list(country="character"),
contains="student"
)
Here we have added an attribute country
, rest will be inherited from the parent.
> s <- new("InternationalStudent",name="John", age=21, GPA=3.5, country="France")
> show(s)
John
21 years old
GPA: 3.5
We see that method define for class student
got called when we did show(s)
.
We can define methods for the derived class which will overwrite methods of the base class, like in the case of S3 systems.
Inheritance in Reference Class
Inheritance in reference class is very much similar to that of the S4 class. We define in the contains
argument, from which base class to derive from.
Here is an example of student
reference class with two methods inc_age()
and dec_age()
.
student <- setRefClass("student",
fields=list(name="character", age="numeric", GPA="numeric"),
methods=list(
inc_age = function(x) {
age <<- age + x
},
dec_age = function(x) {
age <<- age - x
}
)
)
Now we will inherit from this class. We also overwrite dec_age()
method to add an integrity check to make sure age
is never negative.
InternationalStudent <- setRefClass("InternationalStudent",
fields=list(country="character"),
contains="student",
methods=list(
dec_age = function(x) {
if((age - x)<0) stop("Age cannot be negative")
age <<- age - x
}
)
)
Let us put it to test.
> s <- InternationalStudent(name="John", age=21, GPA=3.5, country="France")
> s$dec_age(5)
> s$age
[1] 16
> s$dec_age(20)
Error in s$dec_age(20) : Age cannot be negative
> s$age
[1] 16
In this way, we are able to inherit from the parent class.