15.2 Basics

We’ll start with a quick overview of the main components of S4. You define an S4 class by calling setClass() with the class name and a definition of its slots, and the names and classes of the class data:

  slots = c(
    name = "character", 
    age = "numeric"

Once the class is defined, you can construct new objects from it by calling new() with the name of the class and a value for each slot:

john <- new("Person", name = "John Smith", age = NA_real_)

Given an S4 object you can see its class with is() and access slots with @ (equivalent to $) and slot() (equivalent to [[):

#> [1] "Person"
#> [1] "John Smith"
slot(john, "age")
#> [1] NA

Generally, you should only use @ in your methods. If you’re working with someone else’s class, look for accessor functions that allow you to safely set and get slot values. As the developer of a class, you should also provide your own accessor functions. Accessors are typically S4 generics allowing multiple classes to share the same external interface.

Here we’ll create a setter and getter for the age slot by first creating generics with setGeneric():

setGeneric("age", function(x) standardGeneric("age"))
setGeneric("age<-", function(x, value) standardGeneric("age<-"))

And then defining methods with setMethod():

setMethod("age", "Person", function(x) x@age)
setMethod("age<-", "Person", function(x, value) {
  x@age <- value

age(john) <- 50
#> [1] 50

If you’re using an S4 class defined in a package, you can get help on it with class?Person. To get help for a method, put ? in front of a call (e.g. ?age(john)) and ? will use the class of the arguments to figure out which help file you need.

Finally, you can use sloop functions to identify S4 objects and generics found in the wild:

#> [1] "S4"
#> [1] "S4"      "generic"

15.2.1 Exercises

  1. lubridate::period() returns an S4 class. What slots does it have? What class is each slot? What accessors does it provide?

  2. What other ways can you find help for a method? Read ?"?" and summarise the details.