Object-Oriented Programming

R has several OOP systems:

informal, generic-function dispatch most of base R & CRAN (print, summary, lm) formal classes, multiple dispatch Bioconductor, Matrix reference classes, mutable encapsulated, mutable, fast Shiny, many packages new formal system (successor to S3/S4)

32.1 S3

Operators as generics: define "+.money" <- function(e1, e2) ..., "==.money", "[.myclass", "$.myclass", length.myclass, as.character.myclass.

Best practice (Advanced R): write a constructor (new_x), a validator (validate_x), and a user-facing helper (x()).

# Constructor
new_dog <- function(name, age) {
  stopifnot(is.character(name), is.numeric(age))
  structure(list(name = name, age = age), class = c("dog", "animal"))
}

# Generic
speak <- function(x, ...) UseMethod("speak")

# Methods
speak.dog <- function(x, ...) cat(x$name, "says Woof!\n")
speak.animal <- function(x, ...) cat("Some generic animal sound\n")
speak.default <- function(x, ...) stop("Don't know how to speak")

print.dog <- function(x, ...) { cat("<dog:", x$name, ">\n"); invisible(x) }
summary.dog <- function(object, ...) cat("Dog aged", object$age, "\n")
format.dog <- function(x, ...) paste0("Dog(", x$name, ")")

d <- new_dog("Rex", 3)
speak(d); d; summary(d)
inherits(d, "animal")     # TRUE
methods(speak); methods(class = "dog")
NextMethod()              # call the next method in the class chain (inside a method)
unclass(d)

32.2 S4

Multiple dispatch: setMethod("combine", signature("Matrix", "numeric"), ...).

setClass("Person", representation(name = "character", age = "numeric"),
         validity = function(object) if (object@age < 0) "age must be >= 0" else TRUE)

setGeneric("greet", function(obj, ...) standardGeneric("greet"))
setMethod("greet", "Person", function(obj, ...) cat("Hello,", obj@name, "\n"))
setMethod("show", "Person", function(object) cat("<Person:", object@name, ">\n"))

setClass("Student", contains = "Person", representation(school = "character"))
setMethod("greet", "Student", function(obj, ...) { callNextMethod(); cat("I study at", obj@school, "\n") })

p <- new("Student", name = "Ann", age = 20, school = "MIT")
greet(p); p@name; slot(p, "age"); isVirtualClass("Person"); is(p, "Person")
validObject(p); showMethods("greet"); existsMethod("greet", "Person")

32.3 R6 — mutable, encapsulated objects

library(R6)
Account <- R6Class("Account",
  public = list(
    owner = NULL,
    initialize = function(owner, balance = 0) {
      self$owner <- owner
      private$balance <- balance
    },
    deposit = function(x) { private$balance <- private$balance + x; invisible(self) },
    withdraw = function(x) {
      if (x > private$balance) stop("Insufficient funds")
      private$balance <- private$balance - x
      invisible(self)
    },
    print = function(...) { cat("Account of", self$owner, ":", private$balance, "\n"); invisible(self) }
  ),
  private = list(balance = 0),
  active = list(
    current = function(value) { if (missing(value)) private$balance else stop("read-only") }
  )
)

acc <- Account$new("Ann", 100)
acc$deposit(50)$withdraw(30)    # method chaining
acc$current                      # 120
acc

Savings <- R6Class("Savings", inherit = Account,
  public = list(add_interest = function(rate) self$deposit(self$current * rate)))
acc2 <- acc$clone()              # R6 objects are references — clone to copy

32.4 S7 — the modern successor

library(S7)
Dog <- new_class("Dog", properties = list(name = class_character, age = class_numeric),
                 validator = function(self) if (self@age < 0) "@age must be non-negative")
speak <- new_generic("speak", "x")
method(speak, Dog) <- function(x) cat(x@name, "says Woof\n")
rex <- Dog(name = "Rex", age = 3)
speak(rex); rex@age <- 4

32.5 Reference classes (RC)

Person <- setRefClass("Person", fields = list(name = "character"),
                      methods = list(greet = function() cat("Hi,", name, "\n")))
Person$new(name = "Ann")$greet()

32.6 Choosing

S3 for most things (simple, idiomatic); R6 when you need mutable state (connections, simulations, Shiny modules); S4/S7 for large, formal class hierarchies or multiple dispatch.