retList
can be used as a shortcut to:
vs.
It will simply find all objects in the environment in which it is called and returns those in a list. This is one way to think about and implement features associated with object orientation in R.
Every value bound to a symbol will be public, objects with a leading dot in the name will be private. This can be changed by stating which names to make public explicitely.
Here is a simple class definition:
library("aoos")
Person <- function(name) {
print <- function(x, ...) {
cat(paste0("Hello, my name is ", .self$name, ".\n"))
}
retList(c("Person", "Print"))
}
retList
is used as a generic constructor function. It
allways returns a list containing all visible values from the
environment where it is called. There is a print method
print.Print
in the package which will look for a member
function called print for printing. Thus the print
method
in the class Person will be used as S3 printing
method.
## Hello, my name is Ann.
Inside the class definition - i.e. the body of the constructor - you
can use .self
but you do not have to; It just might be more
explicit. You can also use the super-assignment operator
<<-
. Note that .self
is not a reference
to the instance itself, it is only referencing to the environment in
which the methods will live in.
You should keep in mind that a public field is a value in a list and
not a reference to the value in .self
. Thus, if your object
should have public fields you have to define get/set methods. In this
example you can think of name
as an attribute since
changing it in an instance won’t change the bahavior of
greet
. Here is something you can do instead:
Person <- function(.name) {
print <- function(x, ...) {
cat(paste0("Hello, my name is ", .self$.name, ".\n"))
}
name <- function(x) {
if (!missing(x)) .name <<- x
.name
}
retList(c("Person", "Print"))
}
p <- Person("Ann")
p
## Hello, my name is Ann.
## [1] "Ann"
## [1] "Paul"
## Hello, my name is Paul.
Although I don’t understand why ‘Ann’ can change to be ‘Paul’, this is one way of adding something like reference semantics.
An object can inherit from another object. The constructer then extends the instance of the super class/object. For that you can supply a call to the super classes constructor function, where you also have to supply arguments for initialization.
Person <- function(name) {
print <- function(x, ...) {
cat(paste0("Hello, my name is ", .self$name, ".\n"))
}
retList(c("Person", "Print"))
}
Employee <- function(id, ...) {
print <- function(x, ...) {
cat("Name: ", name, "\nID: ", id)
}
retList("Employee", super = Person(...))
}
kalle <- Employee("1", "Kalle")
str(kalle)
## List of 3
## $ id : chr "1"
## $ print:function (x, ...)
## $ name : chr "Kalle"
## - attr(*, ".self")=<environment: 0x555888a61c80>
## - attr(*, "class")= chr [1:4] "Employee" "Person" "Print" "list"
## Name: Kalle
## ID: 1