15.3 Details on defining the class

15.3.1 Inheritance

setClass("Employee", 
  contains = "Person", 
  slots = c(
    boss = "Person"
  ),
  prototype = list(
    boss = new("Person")
  )
)

15.3.2 Instantiation

Create an instance of the class at two levels:

  • For developer (you): methods::new()
  • For user: constructor function
# how user constructs an instance
Person <- function(name, age = NA) {
  age <- as.double(age)
  
  # how the developer constructs an instance
  new("Person", name = name, age = age)
}

Person("Someone")
#> An object of class "Person"
#> Slot "name":
#> [1] "Someone"
#> 
#> Slot "age":
#> [1] NA

15.3.3 Validation

S4 objects

  • Check class of slot at creation
Person(mtcars)
#> Error in validObject(.Object): invalid class "Person" object: invalid object for slot "name" in class "Person": got class "data.frame", should be or extend class "character"
  • Do not check other things
Person("Hadley", age = c(30, 37))
#> An object of class "Person"
#> Slot "name":
#> [1] "Hadley"
#> 
#> Slot "age":
#> [1] 30 37

That’s where validation comes in–at two stages:

  1. At creation
  2. At modification

15.3.3.1 At creation

setValidity("Person", function(object) {
  if (length(object@name) != length(object@age)) {
    "@name and @age must be same length"
  } else {
    TRUE
  }
})
#> Class "Person" [in ".GlobalEnv"]
#> 
#> Slots:
#>                           
#> Name:       name       age
#> Class: character   numeric

Person("Hadley", age = c(30, 37))
#> Error in validObject(.Object): invalid class "Person" object: @name and @age must be same length

15.3.3.2 At modification

# get value
setGeneric("name", function(x) standardGeneric("name"))
#> [1] "name"
setMethod("name", "Person", function(x) x@name)

# set value--and assess whether resulting object is valid
setGeneric("name<-", function(x, value) standardGeneric("name<-"))
#> [1] "name<-"
setMethod("name<-", "Person", function(x, value) {
  x@name <- value
  validObject(x)
  x
})

# normal name; no problem
name(john) <- "Jon Smythe"
name(john)
#> [1] "Jon Smythe"

# invalid name; error thrown
name(john) <- letters
#> Error in validObject(x): invalid class "Person" object: @name and @age must be same length