15.3 Writing S3 classes
Let’s now create our own S3 class! We will go back to the example from the introduction and create a class “student”. A student will have attributes name, age and field of study, as well as a vector of grades. A student can introduce himself and compute his average grade.
We start by creating an object that holds these attributes of a student. Then, we assign it to a class “student”.
> liam <- list(name = "Liam", age = 25, field = "biology", grades = c(4.5, 6, 5.5))
> class(liam) <- "student"
Now let’s write an S3 print method for the student class. This class needs to be named print.student()
; otherwise UseMethod()
will not find it. The method should also take the same arguments as print()
; otherwise, R will give an error when it tries to pass the arguments to print.student()
:
> print.student <- function(x, ...) {
+ output <- paste0("Hello there! I'm a student. My name is ", x$name, ", I'm ", x$age, " years old and I study ", x$field, ".")
+ print(output)
+ }
Does our method work? Yes, and not only that; R uses the print method to display the contents of liam
.
> print(liam)
[1] "Hello there! I'm a student. My name is Liam, I'm 25 years old and I study biology."
Note that we don’t need to write the generic function print()
itself. This method has already been implemented in base R.
Let’s now implement a generic function calculateMeanGrade()
. There is no generic R function that does this already, so we have to implement 1) the generic function calculateMeanGrade()
and 2) the specialized function calculateMeanGrade.student()
!
> # implement generic function
> calculateMeanGrade <- function(stud) {
+ UseMethod("calculateMeanGrade", stud)
+ }
>
> # implement S3 function
> calculateMeanGrade.student <- function(stud){
+ meanGrade <- mean(stud$grades)
+ return(meanGrade)
+ }
>
> # calculate mean grade
> calculateMeanGrade(liam)
[1] 5.333333
Note that you should never directly call the function calculateMeanGrade.student()
. Although this would give you exactly the same result, the whole point of S3 classes and generic functions would be lost. Maybe you have other classes - e.g. “secondaryStudent”, “universityStudent” - at some point that also need to calculate mean grades, but require a different implementation. The beauty of S3 programming is that you don’t need to remember the class of the object you’re dealing with. No matter if it is a secondaryStudent, a universityStudent or simply a student - you can just call calculateMeanGrade()
and R will do whatever it needs to do for you.