R6 Classes in R Programming

R6 Classes and Their Types

In Object-Oriented Programming (OOP) within the R language, encapsulation refers to bundling data and methods within a class. The R6 package in R provides an encapsulated OOP framework, enabling the use of encapsulation effectively. The R6 package offers R6 classes, which function similarly to reference classes but are independent of S4 classes. In the R6 system, you define a class by creating a new R6Class object, specifying the class name, and including a list of properties and methods. Properties can be any R object, while methods are functions that interact with objects of the class.

To create an instance of an R6 class, you use the $new() method, passing any initial values for the properties. Once an object is instantiated, you can call its methods and access or modify its properties using the $ operator.

A key feature of R6 is its support for encapsulation and information hiding. This allows internal object details to remain hidden, simplifying the creation of complex and robust programs.

R6 classes allow for organizing code efficiently, enabling the creation of custom objects with their own properties and behaviors. Additionally, R6 supports inheritance, even across classes defined in different packages. Prominent R packages like dplyr and shiny utilize R6 classes.

Example: Basic R6 Class Implementation

library(R6)

# Define a Stack class
Stack <- R6Class("Stack",

  # Public members
  public = list(

    # Constructor/initializer
    initialize = function(...) {
      private$items <- list(...)
    },

    # Push an item onto the stack
    push = function(item) {
      private$items <- append(private$items, item)
    },

    # Pop an item from the stack
    pop = function() {
      if (self$size() == 0)
        stop("Stack is empty")
      item <- private$items[[length(private$items)]]
      private$items <- private$items[-length(private$items)]
      item
    },

    # Get the number of items in the stack
    size = function() {
      length(private$items)
    }
  ),

  # Private members
  private = list(
    items = list()
  )
)

# Create a Stack object
StackObject <- Stack$new()

# Push 10 onto the stack
StackObject$push(10)

# Push 20 onto the stack
StackObject$push(20)

# Pop the top item (20)
StackObject$pop()

# Pop the remaining item (10)
StackObject$pop()

Output:

[1] 20
[1] 10

In this example, the stack is implemented with private storage (items) that is hidden from external modification. The initialize method acts as the constructor, and public methods like push and pop provide controlled access to the stack.

Example: Inheritance in R6 Classes

# Define a subclass of Stack
ExtendedStack <- R6Class("ExtendedStack",

  # Inherit the Stack class
  inherit = Stack,

  public = list(

    # Override the size method to display a message
    size = function() {
      message("Calculating stack size...")
      super$size()  # Call the size method of the superclass
    }
  )
)

# Create an ExtendedStack object
ExtendedStackObject <- ExtendedStack$new()

# Push 5 onto the stack
ExtendedStackObject$push(5)

# Push 15 onto the stack
ExtendedStackObject$push(15)

# Check the stack size (with a message)
ExtendedStackObject$size()

# Pop the top item (15)
ExtendedStackObject$pop()

# Pop the remaining item (5)
ExtendedStackObject$pop()

Output:

[1] 20
[1] 10

In this example, the stack is implemented with private storage (items) that is hidden from external modification. The initialize method acts as the constructor, and public methods like push and pop provide controlled access to the stack.

Example: Inheritance in R6 Classes

# Define a subclass of Stack
ExtendedStack <- R6Class("ExtendedStack",

  # Inherit the Stack class
  inherit = Stack,

  public = list(

    # Override the size method to display a message
    size = function() {
      message("Calculating stack size...")
      super$size()  # Call the size method of the superclass
    }
  )
)

# Create an ExtendedStack object
ExtendedStackObject <- ExtendedStack$new()

# Push 5 onto the stack
ExtendedStackObject$push(5)

# Push 15 onto the stack
ExtendedStackObject$push(15)

# Check the stack size (with a message)
ExtendedStackObject$size()

# Pop the top item (15)
ExtendedStackObject$pop()

# Pop the remaining item (5)
ExtendedStackObject$pop()

Output:

Calculating stack size...
[1] 2
[1] 15
[1] 5

In this example, the ExtendedStack class inherits from the Stack class. It overrides the size method to include a message while still calling the original method using super. This demonstrates how methods from the parent class can be extended or customized in the subclass.

Key Features of R6 Classes
  • Encapsulation: Private members (e.g., private$items) ensure internal object details are protected from external modification.
  • Public and Private Members: Public members are accessible using $, while private members are accessible only within class methods.
  • Inheritance: Subclasses can inherit properties and methods from parent classes, and super allows access to parent methods.
  • Initialization: The initialize method acts as a constructor for setting up objects.

These features make R6 a robust and flexible system for implementing OOP in R.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *