Object-Oriented Programming (S3, S4, R6) in R

📘 Introduction

Object-Oriented Programming (OOP) is a programming paradigm that organizes code into objects, which combine data (attributes) and functions (methods). R supports multiple object-oriented systems, each designed for different programming needs. The three primary OOP systems in R are S3, S4, and R6.

Information

Unlike many programming languages that use a single OOP system, R supports multiple object systems. S3 is simple and flexible, S4 is formal and rigorous, while R6 provides reference-based object-oriented programming similar to languages such as Java and C++.

đŸŽ¯ Why Learn Object-Oriented Programming?

  • Organize code into reusable components.
  • Improve code readability and maintainability.
  • Model real-world entities as objects.
  • Build scalable applications and packages.
  • Create custom data structures and behaviors.

📚 OOP Systems in R

Object-Oriented Programming
S3 System
S4 System
R6 System

📋 Comparison of S3, S4, and R6

FeatureS3S4R6
ComplexitySimple.Moderate.Moderate.
Class DefinitionInformal.Formal.Formal.
Type CheckingNo.Yes.Yes.
Reference SemanticsNo.No.Yes.
Common UsageBase R objects.Bioconductor and complex packages.Applications and APIs.

đŸŸĸ S3 Object System

S3 is the simplest object-oriented system in R. Classes are assigned using the class() function, and methods follow a naming convention.

Creating an S3 Object

Creating an S3 Object

student <- list(
  name = "Alice",
  marks = 92
)

class(student) <- "Student"

print(student)

Creating an S3 Method

S3 Method

print.Student <- function(obj) {

  cat(
    "Student:",
    obj$name,
    "\nMarks:",
    obj$marks
  )

}

print(student)

🟡 S4 Object System

S4 provides a formal class system with explicit definitions for slots (attributes), inheritance, and method dispatch.

Creating an S4 Class

Creating an S4 Class

setClass(
  "Student",
  slots = list(
    name = "character",
    marks = "numeric"
  )
)

Creating an S4 Object

Creating an S4 Object

student <- new(
  "Student",
  name = "Alice",
  marks = 92
)

student

Accessing Slots

Accessing Slot Values

student@name

student@marks

Creating an S4 Method

S4 Method

setMethod(
  "show",
  "Student",
  function(object) {

    cat(
      "Name:",
      object@name,
      "\nMarks:",
      object@marks
    )

  }
)

student

đŸ”ĩ R6 Object System

R6 introduces reference-based objects. Unlike S3 and S4, modifying an R6 object changes the original object directly.

Installing and Loading R6

Install and Load R6

install.packages("R6")

library(R6)

Creating an R6 Class

Creating an R6 Class

library(R6)

Student <- R6Class(

  "Student",

  public = list(

    name = NULL,
    marks = NULL,

    initialize = function(
      name,
      marks
    ) {

      self$name <- name
      self$marks <- marks

    },

    display = function() {

      cat(
        "Name:",
        self$name,
        "\nMarks:",
        self$marks
      )

    }

  )

)

Creating an R6 Object

Creating an R6 Object

student <- Student$new(
  "Alice",
  92
)

student$display()

Updating Object Values

Modifying R6 Object

student$marks <- 95

student$display()

đŸ“Ļ Encapsulation in R6

R6 supports encapsulation by allowing both public and private members.

Private Members

Person <- R6Class(

  "Person",

  private = list(
    age = 25
  ),

  public = list(

    showAge = function() {

      print(
        private$age
      )

    }

  )

)

person <- Person$new()

person$showAge()

đŸ§Ŧ Inheritance in R6

R6 classes can inherit properties and methods from other classes.

R6 Inheritance

Employee <- R6Class(

  "Employee",

  inherit = Student,

  public = list(

    salary = NULL,

    initialize = function(
      name,
      marks,
      salary
    ) {

      super$initialize(
        name,
        marks
      )

      self$salary <- salary

    }

  )

)

employee <- Employee$new(
  "Bob",
  88,
  60000
)

employee$display()

📊 Generic Functions in S3

Generic functions call different methods depending on the object's class.

Generic Function

describe <- function(x) {

  UseMethod("describe")

}

describe.Student <- function(x) {

  cat(
    "Student:",
    x$name
  )

}

describe(student)

📋 S3 vs S4 vs R6 Example

SystemObject Creation
S3class(object) <- "Class"
S4new("Class")
R6Class$new()

🌍 Real-World Example

A university management system stores student information. An R6 class can represent each student with attributes such as name and marks, along with methods to display and update information.

University Student Class

Student <- R6Class(

  "Student",

  public = list(

    name = NULL,
    marks = NULL,

    initialize = function(
      name,
      marks
    ) {

      self$name <- name
      self$marks <- marks

    },

    updateMarks = function(
      newMarks
    ) {

      self$marks <- newMarks

    },

    display = function() {

      cat(
        "Student:",
        self$name,
        "\nMarks:",
        self$marks
      )

    }

  )

)

student <- Student$new(
  "Sophia",
  91
)

student$display()

student$updateMarks(95)

student$display()

🔄 OOP Workflow

Define Class
Create Object
Store Data
Add Methods
Use Object
Modify Object

📋 Common OOP Functions

FunctionPurpose
class()Assigns or retrieves an S3 class.
UseMethod()Creates S3 generic functions.
setClass()Defines an S4 class.
new()Creates an S4 object.
setMethod()Defines an S4 method.
R6Class()Creates an R6 class.
$new()Creates an R6 object.

âš ī¸ Common Mistakes

MistakeExplanationSolution
Using S3 when strict validation is requiredS3 does not enforce attribute types.Use S4 or R6 for formal class definitions.
Confusing S4 slot access with list accessS4 objects use @ instead of $.Access slots using the @ operator.
Expecting copy behavior from R6 objectsR6 objects use reference semantics.Remember that modifying one reference changes the original object.
Creating methods with incorrect naming in S3S3 dispatch depends on method names.Follow the generic.class naming convention.

💡 Best Practices

  • Use S3 for simple classes and lightweight extensions.
  • Choose S4 when strict validation and formal class definitions are required.
  • Use R6 for applications that require mutable objects and encapsulation.
  • Keep methods focused on a single responsibility.
  • Document class structures and methods for maintainability.

Best Practice

Each object-oriented system in R serves a different purpose. S3 emphasizes simplicity, S4 provides formal structure and validation, while R6 supports modern object-oriented programming with reference semantics. Selecting the appropriate system depends on the complexity and requirements of your project.

📝 Summary

R supports three major object-oriented programming systems: S3, S4, and R6. S3 offers a simple and flexible approach using informal classes and generic functions. S4 introduces formal class definitions, slots, type checking, and structured method dispatch. R6 provides encapsulation, inheritance, and reference semantics similar to traditional object-oriented languages. Understanding these systems enables you to build reusable, maintainable, and scalable R programs while choosing the most suitable object model for your applications.

>>"Object-oriented programming organizes data and behavior into reusable objects, making software easier to understand, extend, and maintain."