rr-s4

R : setGeneric with two different S4 classes (B inherits A) with same method name and signature


There is two classes A & B.
B extends A.
There is a method doStuffs for A and one for B.
They have same name and signature except they work on different object and accordingly they do different stuffs.

setClass(
  Class = "A",
  slots = list(
    a = "character",
    b = "character"
  )
)

setGeneric("doStuffs", signature ="obj",
    function(obj,...) standardGeneric("doStuffs")
)
setClass(
  Class = "B",
  contains = "A",
  slots = list(
    c = "character"
  )
)

setGeneric("doStuffs", signature ="obj",
    function(obj,...) standardGeneric("doStuffs")
)

When I call :

obj <- new A(a="a",b="b")
showMethods("doStuffs")
doStuffs(obj)

Function: doStuffs (package blahblah)
obj="B"

unable to find an inherited method for function ‘doStuffs’ for signature ‘obj = "A"’

If I use the following code for both generics, it works.
Don't know why.
showMethods find both generics.

if (!isGeneric("doStuffs")) {
    if (is.function("doStuffs")) {
        fun <- doStuffs
    } else {
        fun <- function(obj, ...) standardGeneric("doStuffs")
    }

Function: doStuffs (package blahblah)
obj="A"
obj="B"

Why does this work ? How to code this the "good way" ?

Updated : I also have doStuffs method for each classes:

setMethod("doStuffs", "A", function(obj, x1,x2,x3) {}
setMethod("doStuffs", "B", function(obj, x4,x2,x3) {}

Solution

  • As discussed in comments, you are misusing setGeneric (and not using setMethod).

    Based on the class definitions you provide, first define the generic:

    setGeneric(
      "doStuffs",
      function(obj,...) standardGeneric("doStuffs")
    )
    "doStuffs"
    

    Now define a couple of objects

    obj1 <- new ("A", a = "A", b = "B")
    obj2 <- new ("B", a = "C", b = "D")
    

    Next, define a method for the superclass ...

    setMethod(
      "doStuffs",
      signature = signature(obj = "A"),
      def = function(obj) {
        "Hello from A"
      }
    )
    

    ... and see what happens:

    doStuffs(obj1)
    [1] "Hello from A"
    doStuffs(obj2)
    [1] "Hello from A"
    

    This is correct: there is no doStuffs method for the subclass, so objects of the subclass use the method of the superclass.

    Now define a method for class B ...

    setMethod(
      "doStuffs",
      signature = signature(obj = "B"),
      def = function(obj) {
        "Hello from B"
      }
    )
    

    ... and repeat.

    doStuffs(obj1)
    [1] "Hello from A"
    doStuffs(obj2)
    [1] "Hello from B"
    

    All good.

    I think the S4 Chapter of Hadley's Advanced R is well worth a read. And several re-reads.