Here is an updated fast rundown of the numerous R object systems according to "Advanced R, 2nd edition" (CRC Press, 2019) by Hadley Wickham (Chief Scientist at RStudio), which has a web representation here, based on the chapter about Object-Oriented Programming.

The first edition from 2015 has a web representation here, with the corresponding chapter on OO here.
Approaches to OO systems
Hadley defines the following to distinguish two distinct approaches to OO programming:
Functional OOP: methods (callable code pieces) belong to generic functions (not to be confused with Java/C# generic methods). Think of the methods as being located in a global lookup table. The method to execute is found by the runtime system based on the name of the function and the type (or object class) of one or more arguments passed to that function (this is called "method dispatch"). Syntax-wise, method calls may look like ordinary function calls: myfunc(object, arg1, arg2)
. This call would lead the runtime to look for the method associated to the pair ("myfunc", typeof(object)) or possibly ("myfunc", typeof(object), typeof(arg1), typeof(arg2)) if the language supports that. In R's S3, the full name of the generic function gives the (function-name, class) pair. For example: mean.Date
is the method to compute the mean of Dates. Try methods("mean")
to list the generic methods with function name mean
. The Functional OOP approach is found for example in the OO pioneer Smalltalk, the Common Lisp Object System and Julia. Hadley notes that "Compared to R, Julia’s implementation is fully developed and extremely performant."
Encapsulated OOP: methods belong to objects or classes, and method calls typically look like object.method(arg1, arg2)
. This is called encapsulated because the object encapsulates both data (fields) and behaviour (methods). Think of the method as being located in a lookup table attached to the object or the object's class description. The runtime looks the method up based on method name and possibly the type of one or more arguments. This is the approach found in "popular" OO languages like C++, Java, C#.
In both cases, if inheritance is supported (it probably is), the runtime may traverse the class hierarchy upwards until it has found a match for the call lookup key.
How to find out what system an R object belongs to
library(sloop) # formerly, "pryr"
otype(mtcars)
#> [1] "S3"
The R object systems
S3
- Functional OOP approach.
- Most important system according to Hadley.
- Simplest, most common. First OO system used by R.
- Comes with base R, used throughout base R.
- Relies on conventions rather than enforced guarantees.
- See Chambers, John M, and Trevor J Hastie. 1992. "Statistical Models in S." Wadsworth & Brooks/Cole Advanced Books & Software.
- Details in "Advanced R, 2nd edition" here.
S4
- Functional OOP approach.
- Third most important system according to Hadley.
- Rewrite of S3, therefore similar to S3, but more formal and more strict: it forces you to think carefully about program design. Suited for building large systems (e.g. the Bioconductor project).
- Implemented in the base "methods" package.
- See: Chambers, John M. 1998. "Programming with Data: A Guide to the S Language." Springer.
- Details in "Advanced R, 2nd edition" here.
RC aka "Reference Classes"
- Encapsulated OOP approach.
- Comes with base R.
- Based on S4.
- RC objects are special type of S4 objects that are also "mutable". i.e. instead of using R's usual copy-on-modify semantics, they can be modified in-place. Note that mutable state is hard to reason about and a source of ugly bugs but can lead to more efficient code in certain applications.
R6
- Encapsulated OOP approach.
- Second most important system according to Hadley.
- Can be found in the R6 package (install with
library(R6)
)
- Similar to RC, but lighter & much faster: it does not depend on S4 or the methods package. Built on top of R environments. Also has:
- public and private methods
- active bindings (fields, that, when accessed, actually call a method)
- class inhertance which works across packages
- both class methods (code that belongs to class and can access an instance via
self
, private
, super
) and member functions (functions assigned to fields, but which are not methods, just functions)
- Provides a standardised way to escape R's "copy-on-modify" semantics
- See the package site: "R6: Encapsulated object-oriented programming for R".
- Details in "Advanced R, 2nd edition" here.
Others
There are others, like R.oo (similar to RC), proto (prototype-based, think JavaScript) and Mutatr. However, "Advanced R" says:
Apart from R6, which is widely used, these systems are primarily of
theoretical interest. They do have their strengths, but few R users
know and understand them, so it is hard for others to read and
contribute to your code.
Be sure to read the chapter on trade-offs in "Advanced R, 2nd edition", too.