简体   繁体   中英

R R6 classes and UseMethod / generic methods

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)

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() .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM