The r has at least 4 object-oriented systems (OO): S3
, S4
, Reference Classes (also known as R5
or RC
) and R6
. The latest system is not part of r-base , but has been widely used by the community. R6
comes from a package with the same name.
But attention !!! You should even use OO in R?
It is not a matter of coming to defend this or that programming paradigm. It occurs that "R is a functional programming language; embrace it, do not fight it" 1 .
That is, if you really want to "understand the logic of programming in R
", you should start to address the problems in a more functional way. When your problem requires , leave the r and use object orientation.
See, S3
system is an object-oriented system subordinated to functional programming logic, since its purpose is to dispatch the object correctly to the appropriate function (method).
Back to answer
Already exists a question about the difference between the native systems of the r . I'll focus on the constructor and inheritance response on S3
and R6
systems since they are the most used.
constructors
S3
In% w / o there is no formal definition of the class. Any object can be "transformed" into the desired class.
x <- "lala"
class(x) <- "lm"
print(x)
# Error: $ operator is invalid for atomic vectors
To create or instantiate a class simply add the class to the object, as in the example above, or add it by means of the function S3
. Good practice, however, is to create a constructor to create objects of that class.
pergunta <- function(x) {
structure(x, class = c("pergunta", class(x)))
}
p1 <- pergunta("Acabei de criar uma classe?")
p1
# [1] "Acabei de criar uma classe?"
# attr(,"class")
# [1] "pergunta" "character"
R6
In structure()
you need to first formally define the class. This is where you define a constructor (item R6
in initialize
).
library(R6)
Pergunta<- R6Class(
"Pergunta",
public = list(
initialize = function(pergunta) {
self$pergunta <- pergunta
self$responder()
},
pergunta = NULL,
responder = function() {
print(sample(c("Sim!", "Não!"), 1))
}
)
)
Then you can instantiate it (which is when the constructor is "enabled")
set.seed(1)
P1 <- Pergunta$new("Acabei de criar uma classe?")
# [1] "Sim!"
On constructor utility in public
: is the same as in any other language in which OO is used, but this paradigm has restricted use in R
, as commented by Daniel.
inheritance
S3
Inheritance in the R
method works by adding classes ahead of the "parent class". This is because the way to dispatch the object to the methods goes through the class vector until it finds a method for that function.
class(dplyr::starwars)
# [1] "tbl_df" "tbl" "data.frame"
Thus, S3
s can work for both methods ( tibble
), which are preferred, as well as methods of print()
( data.frame
). This is also why, even though we have not defined a method to print our class, summary()
has turned around. When no method is found, the object is thrown to the "default method" ( R
)
R6
In the case of nome_da_funcao.default()
, the function that defines the class has an argument to identify inheritance ( R6
). Attention to the fact that it should be passed its own class as object, not just its name.
PerguntaRetorica <- R6Class(
"PerguntaRetorica",
inherit = Pergunta,
public = list(responder = function() print("!?!"))
)
After the class has been defined, we can instantiate it.
retorica <- PerguntaRetorica$new("Ser ou não ser?")
[1] "!?!"
The importance of knowing the ascendancy of a given object is knowing how it will behave with the methods. And the way to do this is to pass the object to inherit
.
class(retorica)
[1] "PerguntaRetorica" "Pergunta" "R6"
Here we see that our daughter-class is heiress of class()
which in turn descends from Pergunta
References
1: The tidy tools manifesto
2: Advanced R - Object oriented programming