The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
The S7 package provides a new OOP system designed to be a successor to S3 and S4. It has been designed and implemented collaboratively by the RConsortium Object-Oriented Programming Working Group, which includes representatives from R-Core, BioConductor, RStudio/tidyverse, and the wider R community.
This vignette gives an overview of the most important parts of S7: classes and objects, generics and methods, and the basics of method dispatch and inheritance.
S7 classes have a formal definition that you create with
new_class()
. There are two arguments that you’ll use with
almost every class:
name
of the class, supplied in the first
argument.properties
, the data associated with each
instance of the class. The easiest way to define properties is to supply
a named list where the values define the valid types of the
property.The following code defines a simple dog
class with two
properties: a character name
and a numeric
age
.
Dog <- new_class("Dog", properties = list(
name = class_character,
age = class_numeric
))
Dog
#> <Dog> class
#> @ parent : <S7_object>
#> @ constructor: function(name, age) {...}
#> @ validator : <NULL>
#> @ properties :
#> $ name: <character>
#> $ age : <integer> or <double>
S7 provides a number of built-in definitions that allow you to refer
to existing base types that are not S7 classes. You can recognize these
definitions because they all start with class_
.
Note that I’ve assigned the return value of new_class()
to an object with the same name as the class. This is important! That
object represents the class and is what you use to construct instances
of the class:
Once you have an S7 object, you can get and set properties using
@
:
S7 automatically validates the type of the property using the type
supplied in new_class()
:
Given an object, you can retrieves its class
S7_class()
:
S7_class(lola)
#> <Dog> class
#> @ parent : <S7_object>
#> @ constructor: function(name, age) {...}
#> @ validator : <NULL>
#> @ properties :
#> $ name: <character>
#> $ age : <integer> or <double>
S7 objects also have an S3 class()
. This is used for
compatibility with existing S3 generics and you can learn more about it
in vignette("compatibility")
.
If you want to learn more about the details of S7 classes and
objects, including validation methods and more details of properties,
please see vignette("classes-objects")
.
S7, like S3 and S4, is built around the idea of generic functions, or generics for short. A generic defines an interface, which uses a different implementation depending on the class of one or more arguments. The implementation for a specific class is called a method, and the generic finds that appropriate method by performing method dispatch.
Use new_generic()
to create a S7 generic. In its
simplest form, it only needs two arguments: the name of the generic
(used in error messages) and the name of the argument used for method
dispatch:
Like with new_class()
, you should always assign the
result of new_generic()
to a variable with the same name as
the first argument.
Once you have a generic, you can register methods for specific
classes with
method(generic, class) <- implementation
.
Once the method is registered, the generic will use it when appropriate:
Let’s define another class, this one for cats, and define another
method for speak()
:
Cat <- new_class("Cat", properties = list(
name = class_character,
age = class_double
))
method(speak, Cat) <- function(x) {
"Meow"
}
fluffy <- Cat(name = "Fluffy", age = 5)
speak(fluffy)
#> [1] "Meow"
You get an error if you call the generic with a class that doesn’t have a method:
The cat
and dog
classes share the same
properties, so we could use a common parent class to extract out the
duplicated specification. We first define the parent class:
Then use the parent
argument to
new_class:
Cat <- new_class("Cat", parent = Pet)
Dog <- new_class("Dog", parent = Pet)
Cat
#> <Cat> class
#> @ parent : <Pet>
#> @ constructor: function(name, age) {...}
#> @ validator : <NULL>
#> @ properties :
#> $ name: <character>
#> $ age : <integer> or <double>
Dog
#> <Dog> class
#> @ parent : <Pet>
#> @ constructor: function(name, age) {...}
#> @ validator : <NULL>
#> @ properties :
#> $ name: <character>
#> $ age : <integer> or <double>
Because we have created new classes, we need to recreate the existing
lola
and fluffy
objects:
Method dispatch takes advantage of the hierarchy of parent classes: if a method is not defined for a class, it will try the method for the parent class, and so on until it finds a method or gives up with an error. This inheritance is a powerful mechanism for sharing code across classes.
describe <- new_generic("describe", "x")
method(describe, Pet) <- function(x) {
paste0(x@name, " is ", x@age, " years old")
}
describe(lola)
#> [1] "Lola is 11 years old"
describe(fluffy)
#> [1] "Fluffy is 5 years old"
method(describe, Dog) <- function(x) {
paste0(x@name, " is a ", x@age, " year old dog")
}
describe(lola)
#> [1] "Lola is a 11 year old dog"
describe(fluffy)
#> [1] "Fluffy is 5 years old"
You can define a fallback method for any S7 object by registering a
method for S7_object
:
method(describe, S7_object) <- function(x) {
"An S7 object"
}
Cocktail <- new_class("Cocktail",
properties = list(
ingredients = class_character
)
)
martini <- Cocktail(ingredients = c("gin", "vermouth"))
describe(martini)
#> [1] "An S7 object"
Printing a generic will show you which methods are currently defined:
describe
#> <S7_generic> describe(x, ...) with 3 methods:
#> 1: method(describe, Dog)
#> 2: method(describe, S7_object)
#> 3: method(describe, Pet)
And you can use method()
to retrieve the implementation
of one of those methods:
method(describe, Pet)
#> <S7_method> method(describe, Pet)
#> function (x)
#> {
#> paste0(x@name, " is ", x@age, " years old")
#> }
#> <bytecode: 0x11631e0b0>
Learn more about method dispatch in
vignette("generics-methods")
.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.