rr-s4

Can I overwrite the type of an S4 slot in R?


If I have an S4 object like this:

obj1 <- setClass(
  "obj1",
  slots = c(
    x = "numeric",
    y = "numeric"
  )
)

Is there a way to make a second object that inherits from obj1 but overwrites one of the data types of a slot? i.e.

obj2 <- setClass(
  "obj2",
  slots = c(
    x = "character"
  ),
  contains = "obj1"
)

Solution

  • The typical way to implement this would be to have both obj1 and obj2 inherit from a virtual class containing the common slots. This is particularly useful if you want to be able to call many of the same methods on both obj1 and obj2.

    Let's say we have the virtual class obj0:

    obj0 <- setClass(
      "obj0",
      slots = c(
        y = "numeric"
      ),
      contains = "VIRTUAL"
    )
    

    Then we can define the obj1 and obj2 classes with just the unique slots we want, while allowing them to inherit everything else from obj0

    obj1 <- setClass(
      "obj1",
      slots = c(
        x = "numeric"
      ),
      contains = "obj0"
    )
    
    obj2 <- setClass(
      "obj2",
      slots = c(
        x = "character"
      ),
      contains = "obj0"
    )
    

    Creating an instance of each object type, we get the desired result for obj1 and obj2

    obj1()
    #> An object of class "obj1"
    #> Slot "x":
    #> numeric(0)
    #> 
    #> Slot "y":
    #> numeric(0)
    obj2()
    #> An object of class "obj2"
    #> Slot "x":
    #> character(0)
    #> 
    #> Slot "y":
    #> numeric(0)
    

    And we don't need to worry about users trying to instantiate the incomplete obj0 class because they are disallowed:

    obj0()
    #> Error in new(structure("obj0", package = ".GlobalEnv"), ...): 
    #>  trying to generate an object from a virtual class ("obj0")
    

    Created on 2022-09-08 with reprex v2.0.2