I want to use R6 classes and generic methods (UseMethod
) to add small objects of different classes (Small1,MyClassA and Small2,MyClassB) to public lists (MyListA and MyListB) inside the Big instance of MyClass.
This works upon creation (Big is created with Small1 and -2), but it fails afterwards:
> Big$AddObj(Small3) #produces:
Error in UseMethod("AddObj", x) : no applicable method for 'AddObj'
applied to an object of class "c('MyClassA', 'R6')"
I'm not quite sure what's my error. How do I call the AddObj-Method later on for an additional object? Suggestions would be very appreciated.
require('R6')
MyClass <- R6Class("MyClass",
public = list(
initialize = function(...) {
for (x in list(...)) {AddObj(x)}
},
AddObj = function(x) {UseMethod("AddObj", x)},
AddObj.MyClassA = function(x) {
MyListA <<- c(MyListA, list(x))},
AddObj.MyClassB = function(x) {
MyListB <<- c(MyListB, list(x))},
AddObj.default = function(x) {
otherObjects <<- c(otherObjects, list(x))},
Show = function() {
print(methods(AddObj))
if (length(MyListA)>0) print(MyListA)
if (length(MyListB)>0) print(MyListB)
},
MyListA = list(),
MyListB = list(),
otherObjects = list()
)
)
MyClassA <- R6Class("MyClassA",
public = list(
name = NA,
initialize = function(input) {
if (!missing(input)) name <<- as.character(input)}
))
MyClassB <- R6Class("MyClassB",
public = list(
name = NA,
initialize = function(input) {
if (!missing(input)) name <<- as.character(input)}
))
Small1 <- MyClassA$new("MyName1")
Small2 <- MyClassB$new("MyName2")
Big <- MyClass$new(Small1, Small2)
Big$Show()
Small3 <- MyClassA$new("MyNewName")
Big$AddObj(Small3)
(See also the draft 'R Language Definition'.) UseMethod creates a new function call with arguments matched as they came in to the generic. Any local variables defined before the call to UseMethod are retained (unlike S). Any statements after the call to UseMethod will not be evaluated as UseMethod does not return.
R6 is an implemention of encapsulated object-oriented programming for R, and is a simpler, faster, lighter-weight alternative to R's built-in reference classes. This style of programming is also sometimes referred to as classical object-oriented programming.
There's no S3 dispatch within an R6 object, but you can use an if-else statement like so:
library('R6')
MyClass <- R6Class("MyClass",
portable = FALSE,
public = list(
initialize = function(...) {
for (x in list(...)) {AddObj(x)}
},
AddObj = function(x) {
if (inherits(x, "MyClassA"))
MyListA <<- c(MyListA, list(x))
else if (inherits(x, "MyClassB"))
MyListB <<- c(MyListB, list(x))
else
otherObjects <<- c(otherObjects, list(x))
},
Show = function() {
if (length(MyListA)>0) print(MyListA)
if (length(MyListB)>0) print(MyListB)
},
MyListA = list(),
MyListB = list(),
otherObjects = list()
)
)
MyClassA <- R6Class("MyClassA",
portable = FALSE,
public = list(
name = NA,
initialize = function(input) {
if (!missing(input)) name <<- as.character(input)}
))
MyClassB <- R6Class("MyClassB",
portable = FALSE,
public = list(
name = NA,
initialize = function(input) {
if (!missing(input)) name <<- as.character(input)}
))
Small1 <- MyClassA$new("MyName1")
Small2 <- MyClassB$new("MyName2")
Big <- MyClass$new(Small1, Small2)
Big$Show()
Small3 <- MyClassA$new("MyNewName")
Big$AddObj(Small3)
Big$Show()
Also note that I used the portable=FALSE
setting, which is something that was just added to the development version of R6. See https://github.com/wch/R6/issues/16 for more information about that.
UPDATE: It turns out I was slightly wrong -- there is S3 dispatch within an R6 object, but it's not used if the function is called with $
, as in Big$AddObj(Small3)
. You could make use of it with something like: eval(quote(AddObj(Small3)), envir = Big)
, but that's obviously not great. Better to go with the if-else and use inherits()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With