Search code examples
rr-s4

Is it possible to have an S3 slot in an S4 class?


I'm wondering how to include an S3 object as a data member in an S4 object, i.e. using composition instead of inheritance. Here's my code snippet.

library(randomForest)
set.seed(1337)

setClass("TestManager", slots = c(
    hp = "numeric",
    rfObj = "randomForest")
)

setGeneric("doIt", function(obj) standardGeneric("doIt"))
setMethod("doIt", "TestManager", function(obj) {
    response <- rep(c(0, 1), times = 50) # a vector of length 100
    predictors <- matrix(runif(200), nrow = 100, ncol = 2) # a matrix of dimension 100 x 2

    # package "randomForest" has a function "randomForest"
    # that returns an object of S3 class "randomForest"
    obj@rfObj <- randomForest::randomForest(predictors, response) # <- ERROR!

    return(obj)
})

obj <- new("TestManager", hp = 100)
obj <- doIt(obj)

This would lead to an error message:

Error in validObject(.Object) : 
  invalid class “TestManager” object: undefined class for slot "rfObj" ("randomForest")
In addition: Warning message:
 Error in validObject(.Object) : 
  invalid class “TestManager” object: undefined class for slot "rfObj" ("randomForest") 

Solution

  • One options is just to use "ANY" as the slot type. This would avoid the type checking

    setClass("TestManager", slots = c(
        hp = "numeric",
        rfObj = "ANY")
    )
    

    Otherwise you can use

    setOldClass("randomForest")
    

    to have S4 recognize the randomForest class type. But in your example you don't seem to be initializing that slot and there's no such thing as an "empty" random forest object so you would still get the error on initialization. If you want to allow NULL values, you can build a union class

    setOldClass("randomForest")
    setClassUnion("randomForestOrNULL", c("randomForest", "NULL"))
    setClass("TestManager", slots = c(
      hp = "numeric",
      rfObj = "randomForestOrNULL")
    )
    

    Then these will work

    obj <- new("TestManager", hp = 100)
    obj@rfObj # NULL
    obj <- doIt(obj)
    obj@rfObj