14.3 Controlling access
R6Class()
has two other arguments that work similarly to public
:
private
allows you to create fields and methods that are only available from within the class, not outside of it.active
allows you to use accessor functions to define dynamic, or active, fields.
These are described in the following sections.
14.3.1 Privacy
With R6 you can define private fields and methods, elements that can only be accessed from within the class, not from the outside53. There are two things that you need to know to take advantage of private elements:
The
private
argument toR6Class
works in the same way as thepublic
argument: you give it a named list of methods (functions) and fields (everything else).Fields and methods defined in
private
are available within the methods usingprivate$
instead ofself$
. You cannot access private fields or methods outside of the class.
To make this concrete, we could make $age
and $name
fields of the Person class private. With this definition of Person
we can only set $age
and $name
during object creation, and we cannot access their values from outside of the class.
R6Class("Person",
Person <-public = list(
initialize = function(name, age = NA) {
$name <- name
private$age <- age
private
},print = function(...) {
cat("Person: \n")
cat(" Name: ", private$name, "\n", sep = "")
cat(" Age: ", private$age, "\n", sep = "")
}
),private = list(
age = NA,
name = NULL
)
)
Person$new("Hadley")
hadley3 <-
hadley3#> Person:
#> Name: Hadley
#> Age: NA
$name
hadley3#> NULL
The distinction between public and private fields is important when you create complex networks of classes, and you want to make it as clear as possible what it’s ok for others to access. Anything that’s private can be more easily refactored because you know others aren’t relying on it. Private methods tend to be less important in R compared to other programming languages because the object hierarchies in R tend to be simpler.
14.3.2 Active fields
Active fields allow you to define components that look like fields from the outside, but are defined with functions, like methods. Active fields are implemented using active bindings (Section 7.2.6). Each active binding is a function that takes a single argument: value
. If the argument is missing()
, the value is being retrieved; otherwise it’s being modified.
For example, you could make an active field random
that returns a different value every time you access it:
R6::R6Class("Rando", active = list(
Rando <-random = function(value) {
if (missing(value)) {
runif(1)
else {
} stop("Can't set `$random`", call. = FALSE)
}
}
)) Rando$new()
x <-$random
x#> [1] 0.0808
$random
x#> [1] 0.834
$random
x#> [1] 0.601
Active fields are particularly useful in conjunction with private fields, because they make it possible to implement components that look like fields from the outside but provide additional checks. For example, we can use them to make a read-only age
field, and to ensure that name
is a length 1 character vector.
R6Class("Person",
Person <-private = list(
.age = NA,
.name = NULL
),active = list(
age = function(value) {
if (missing(value)) {
$.age
privateelse {
} stop("`$age` is read only", call. = FALSE)
}
},name = function(value) {
if (missing(value)) {
$.name
privateelse {
} stopifnot(is.character(value), length(value) == 1)
$.name <- value
private
self
}
}
),public = list(
initialize = function(name, age = NA) {
$.name <- name
private$.age <- age
private
}
)
)
Person$new("Hadley", age = 38)
hadley4 <-$name
hadley4#> [1] "Hadley"
$name <- 10
hadley4#> Error in (function (value) : is.character(value) is not TRUE
$age <- 20
hadley4#> Error: `$age` is read only
14.3.3 Exercises
Create a bank account class that prevents you from directly setting the account balance, but you can still withdraw from and deposit to. Throw an error if you attempt to go into overdraft.
Create a class with a write-only
$password
field. It should have$check_password(password)
method that returnsTRUE
orFALSE
, but there should be no way to view the complete password.Extend the
Rando
class with another active binding that allows you to access the previous random value. Ensure that active binding is the only way to access the value.Can subclasses access private fields/methods from their parent? Perform an experiment to find out.
Because R is such a flexible language, it’s technically still possible to access private values, but you’ll have to try much harder, spelunking in to the details of R6’s implementation.↩︎