The majority of applications using methods and classes will be in R packages implementing new computations for an application, using new classes of objects that represent the data and results. Computations will be implemented using methods that implement functional computations when one or more of the arguments is an object from these classes.
Calls to the functions setClass()
define the new classes;
calls to setMethod
define the methods.
These, along with ordinary R computations, are sufficient to get
started for most applications.
Classes are defined in terms of the data in them and what other classes of data they inherit from. Section ‘Defining Classes’ outlines the basic design of new classes.
Methods are R functions, often implementing basic computations as they apply to the new classes of objects. Section ‘Defining Methods’ discusses basic requirements and special tools for defining methods.
The classes discussed here are the original functional classes. R also supports formal classes and methods similar to those in other languages such as Python, in which methods are part of class definitions and invoked on an object. These are more appropriate when computations expect references to objects that are persistent, making changes to the object over time. See ReferenceClasses and Chapter 9 of the reference for the choice between these and S4 classes.
All objects in R belong to a class; ordinary vectors and other basic
objects are built-in (builtin-class).
A new class is defined in terms of the named slots that is has
and/or in terms of existing classes that it inherits from, or
contains (discussed in ‘Class Inheritance’ below).
A call to setClass()
names a new class and uses the corresponding arguments to
define it.
For example, suppose we want a class of objects to represent a collection of positions, perhaps from GPS readings. A natural way to think of these in R would have vectors of numeric values for latitude, longitude and altitude. A class with three corresponding slots could be defined by:
Pos <- setClass("Pos", slots = c(latitude = "numeric",
longitude = "numeric", altitude = "numeric"))
The value returned is a function, typically assigned as here with the name of the class. Calling this function returns an object from the class; its arguments are named with the slot names. If a function in the class had read the corresponding data, perhaps from a CSV file or from a data base, it could return an object from the class by:
Pos(latitude = x, longitude = y, altitude = z)
The slots are accessed by the
@
operator; for example, if g
is an object from
the class, g@latitude
.
In addition to returning a generator function the call to
setClass()
assigns a definition of the class in a
special metadata object in the package's namespace.
When the package is loaded into an R session, the class definition is
added to a table of known classes.
To make the class and the generating function publicly available, the
package should include POS
in exportClasses()
and
export()
directives in its NAMESPACE
file:
exportClasses(Pos); export(Pos)
Defining methods for an R function makes that function generic. Instead of a call to the function always being carried out by the same method, there will be several alternatives. These are selected by matching the classes of the arguments in the call to a table in the generic function, indexed by classes for one or more formal arguments to the function, known as the signatures for the methods.
A method definition then specifies three things: the name of the function, the signature and the method definition itself. The definition must be a function with the same formal arguments as the generic.
For example, a method to make a plot of an object from class
"Pos"
could be defined by:
setMethod("plot", c("Pos", "missing"), function(x, y, ...) {
plotPos(x, y) })
This method will match a call to plot()
if the first
argument is from class "Pos"
or a subclass of that.
The second argument must be missing; only a missing argument matches
that class in the signature.
Any object will match class "ANY"
in the corresponding position
of the signature.
A class may inherit all the slots and methods of one or more existing
classes by specifying the names of the inherited classes in the contains =
argument to
setClass()
.
To define a class that extends class "Pos"
to a class
"GPS"
with a slot for the observation times:
GPS <- setClass("GPS", slots = c(time = "POSIXt"), contains = "Pos")
The inherited classes may be S4 classes, S3
classes or basic data types.
S3 classes need to be identified as such by a call to
setOldClass()
; most S3 classes in the base package and
many in the other built-in packages are already declared, as is
"POSIXt"
.
If it had not been, the application package should contain:
setOldClass("POSIXt")
Inheriting from one of the R types is special. Objects from the new
class will have the same type. A class
Currency
that contains numeric data plus a slot "unit"
would be created by
Currency <- setClass("Currency", slots = c(unit = "character"),
contains = "numeric")
Objects created from this class will have type "numeric"
and
inherit all the builtin arithmetic and other computations for that
type.
Classes can only inherit from at most one such type; if the class does
not inherit from a type, objects from the class will have type
"S4"
.
Chambers, John M. (2016) Extending R, Chapman & Hall. (Chapters 9 and 10.)