Learn R Programming

R6 (version 1.0.1)

R6Class: Create an R6 reference object generator

Description

Create a lightweight reference class: an environment containing functions.

Usage

R6Class(classname = NULL, public = list(), private = NULL,
  active = NULL, inherit = NULL, lock = TRUE, class = TRUE,
  parent_env = parent.frame())

Arguments

classname
Name of the class.
public
A list of public members, which can be functions (methods) and non-functions (fields).
private
An optional list of private members, which can be functions and non-functions.
active
An optional list of active binding functions.
inherit
A R6ClassGenerator object to inherit from; in other words, a superclass.
parent_env
An environment to use as the parent of newly-created objects.
class
Should a class attribute be added to the public environment? Default is TRUE.
lock
Should the environments of the generated objects be locked? If lcoked, new members can't be added to the objects.

S3 details

Normally the public environment will have two classes: the one supplied in the classname argument, and "R6Class". It is possible to get the public environment with no classes, by using class = FALSE. This will result in faster access speeds by avoiding class-based dispatch of $. The benefit is is negligible in most cases. With classes, accessing a member with $ takes around 2 microseconds on a modern machine; without classes, it takes around 0.3 microseconds. This will make a noticeable difference in performance only when there are hundreds of thousands or more iterations.

The primary difference in behavior when class=FALSE is that, without a class attribute, it won't be possible to use S3 methods with the objects, and so pretty printing (with print.R6Class) won't be used.

Details

Classes created by this generator have the following properties:
  • They have public members, and optionally have private members and active bindings.
  • Public members reside in an environment. This environment is what is returned by the generator, and is sometimes referred to as an R6 object.
  • If there are any private members, they are put in a private environment, which is the parent of the public environment. The parent of the private environment is set with theparent_envargument.
  • If there are no private members, then no private environment is created, and the parent of the public environment is set withparent_env.
  • If present, active bindings are put in the public environment.
  • The generator's$newmethod creates a new object and returns its public environment.
  • Methods can directly access the public and private environments, by usingprivate$xorself$x(for public). Assignment to either environment can be done with<<-< code="">, but it's more precise to explicitly specifyprivateorself.
  • The enclosing environment of all methods is set to the public environment, even for private methods. In other words, private methods are found in the private environment, but when they are called, the public environment is the parent environment.
  • Each instance of the class has its own copy of each method. The memory cost of this is small; it should be 56 bytes per method.
  • R6 objects have a class attribute so that thet may be used with S3 methods.

The active argument is a list of active binding functions. These functions take one argument. They look like regular variables, but when accessed, a function is called with an optional argument. For example, if obj$x2 is an active binding, then when accessed as obj$x2, it calls the x2() function that was in the active list, with no arguments. However, if a value is assigned to it, as in obj$x2 <- 50, then the function is called with the right-side value as its argument, as in x2(50).

If the public or private lists contain any items that have reference semantics (for example, an environment), those items will be shared across all instances of the class. To avoid this, add an entry for that item with a NULL initial value, and then in the intialize method, instantiate the object and assign it.

See Also

makeActiveBinding

Examples

Run this code
# A simple class
AnimalHerd <- R6Class("AnimalHerd",
  public = list(
    animal = "buffalo",
    count = 2,
    view = function() {
      paste(rep(animal, count), collapse = "")
    },
    reproduce = function(mult = 2) {
      count <<- count * mult
      invisible(self)
    }
  )
)

herd <- AnimalHerd$new()
herd$view()
# "buffalo buffalo"

herd$reproduce()
herd$view()
# "buffalo buffalo buffalo buffalo"

# Methods that return self are chainable
herd$reproduce()$view()
"buffalo buffalo buffalo buffalo buffalo buffalo buffalo buffalo"


# An example that demonstrates private members and active bindings
MyClass <- R6Class("MyClass",
  private = list(
    x = 2,
    # Private methods can access public members
    prod_xy = function() x * y
  ),
  public = list(
    y = 3,
    initialize = function(x, y) {
      if (!missing(x)) private$x <- x
      if (!missing(y)) self$y <- y
    },
    # Set a private variable
    set_x = function(value) private$x <- value,
    # Increment y, and return self
    inc_y = function(n = 1) {
      y <<- y + n
      invisible(self)
    },
    # Access private and public members
    sum_xy = function() x + y,
    # Access a private variable and private method
    sumprod = function() x + prod_xy()
  ),
  active = list(
    y2 = function(value) {
      if (missing(value)) return(y * 2)
      else self$y <- value/2
    }
  )
)

z <- MyClass$new(5)

z$sum_xy()   # 8
z$sumprod()  # 20
# z$x <- 20  # Error - can't access private member directly
z$set_x(20)
z$sum_xy()   # 23
z$y <- 100   # Can set public members directly
z$sum_xy()   # 120

z$y2         # An active binding that returns y*2
z$y2 <- 1000 # Setting an active binding
z$y          # 500

# Methods that return self allow chaining
z$inc_y()$inc_y()
z$y          # 502

# Print, using the print.R6Class method:
print(z)

Run the code above in your browser using DataLab