Menu
Coddy logo textTech

Extending With contains

Part of the Object Oriented Programming section of Coddy's R journey. Lesson 32 of 57.

contains = "Animal" makes a new S4 class a subclass of Animal. The subclass has every slot of its parent plus its own, and new() takes them all:

setClass("Animal", slots = c(name = "character", age = "numeric"))
setClass("Dog", contains = "Animal", slots = c(breed = "character"))
rex <- new("Dog", name = "Rex", age = 3, breed = "collie")
print(slotNames("Dog"))
cat(rex@name, rex@breed)

Output:

[1] "breed" "name"  "age"  
Rex collie

Methods written for the parent work for the child too. When a generic has no method for Dog, S4 uses the one for Animal:

setClass("Animal", slots = c(name = "character"))
setClass("Dog", contains = "Animal")
invisible(setGeneric("intro", function(a) standardGeneric("intro")))
setMethod("intro", "Animal", function(a) paste("I am", a@name))
print(intro(new("Dog", name = "Rex")))

Output:

[1] "I am Rex"

A child overrides a parent method by defining its own for the same generic. The most specific class wins, and other subclasses keep the parent's method:

setClass("Animal", slots = c(name = "character"))
setClass("Dog", contains = "Animal")
setClass("Cat", contains = "Animal")
invisible(setGeneric("sound", function(a) standardGeneric("sound")))
setMethod("sound", "Animal", function(a) "...")
setMethod("sound", "Dog", function(a) "woof")
cat(sound(new("Dog", name = "Rex")), sound(new("Cat", name = "Tom")))

Output:

woof ...

is(x, "Animal") is TRUE for a dog, and a slot declared as "Animal" accepts a Dog. Code written for the parent class works with every subclass:

setClass("Animal", slots = c(name = "character"))
setClass("Dog", contains = "Animal")
setClass("Owner", slots = c(pet = "Animal"))
rex <- new("Dog", name = "Rex")
o <- new("Owner", pet = rex)
cat(is(rex, "Animal"), is(o@pet, "Dog"))

Output:

TRUE TRUE
challenge icon

Challenge

Easy

Employees and managers are S4 classes in two files. Employee.R declares Employee (slots name, salary) and the generic describe(), and needs a method for Employee that returns Bo earns 40000. In Manager.R declare Manager, which contains Employee and adds the slot reports (numeric), and override describe() so it returns Ada earns 60000 and manages 4.

The supplied code reads lines employee Bo 40000 or manager Ada 60000 4 and prints describe(), whether the object is an employee, and its slots.

Your code goes in Employee.R and Manager.R. main.R holds the supplied input/output code and cannot be edited.

Try it yourself

source("Employee.R")
source("Manager.R")

# Supplied input/output code: keep it as it is
input <- suppressWarnings(readLines(file("stdin")))
for (line in input) {
  p <- strsplit(line, " ")[[1]]
  x <- if (p[1] == "manager") {
    new("Manager", name = p[2], salary = as.numeric(p[3]), reports = as.numeric(p[4]))
  } else {
    new("Employee", name = p[2], salary = as.numeric(p[3]))
  }
  cat(describe(x), "\n", sep = "")
  cat("is an employee: ", is(x, "Employee"), ", slots: ", paste(slotNames(x), collapse = ", "), "\n", sep = "")
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online R compiler