13.6 Inheritance
Three ideas:
- Class is a vector of classes
- Dispatch moves through class vector until it finds a defined method
- Method can delegate to another method via
NextMethod()
, which is indicated by->
as below:
13.6.1 NextMethod()
Consider secret
class that masks each character of the input with x
in output
new_secret <- function(x = double()) {
stopifnot(is.double(x))
structure(x, class = "secret")
}
print.secret <- function(x, ...) {
print(strrep("x", nchar(x)))
invisible(x)
}
y <- new_secret(c(15, 1, 456))
y
#> [1] "xx" "x" "xxx"
Notice that the [
method is problematic in that it does not preserve the secret
class. Additionally, it returns 15
as the first element instead of xx
.
Fix this with a [.secret
method:
The first fix (not run) is inefficient because it creates a copy of y
.
NextMethod()
is more efficient.
`[.secret` <- function(x, i) {
# first, dispatch to `[`
# then, coerce subset value to `secret` class
new_secret(NextMethod())
}
Notice that [.secret
is selected for dispatch, but that the method delegates to the internal [
.
13.6.2 Allowing subclassing
Continue the example above to have a supersecret
subclass that hides even the number of characters in the input (e.g., 123
-> xxxxx
, 12345678 -> xxxxx
, 1 -> xxxxx
).
To allow for this subclass, the constructor function needs to include two additional arguments:
...
for passing an arbitrary set of arguments to different subclassesclass
for defining the subclass
new_secret <- function(x, ..., class = character()) {
stopifnot(is.double(x))
structure(
x,
...,
class = c(class, "secret")
)
}
To create the subclass, simply invoke the parent class constructor inside of the subclass constructor:
new_supersecret <- function(x) {
new_secret(x, class = "supersecret")
}
print.supersecret <- function(x, ...) {
print(rep("xxxxx", length(x)))
invisible(x)
}
But this means the subclass inherits all parent methods and needs to overwrite all parent methods with subclass methods that return the sublclass rather than the parent class.
There’s no easy solution to this problem in base R.
There is a solution in the vectors package: vctrs::vec_restore()