You're wanting to use method dispatch, and every method needs to be associated with a generic, so yes, setGeneric
is required.
And for a little unasked-for advice... It's a bit weird to use a formal class system (presumably because the well-defined classes help in writing more complicated programs) and then to subvert the structure by adding arbitrary attributes; these should really be additional, well-defined slots in your class.
Let's make your example reproducible by defining Species
setClass("Species", representation(y="numeric"))
setClass("MySpecies", contains="Species", ##Species is an S4 class
representation(x="numeric"))
An implicit requirement for S4 classes is that new("MySpecies")
works; your initialize
method fails this test (because x
does not have a default value). In addition, it's common practice to expect that initializing MySpecies
calls the initialize
methods for the classes it contains. One could write
setMethod("initialize", "MySpecies", function(.Object, ..., x=numeric()) {
callNextMethod(.Object, x=x, ...)
})
Note callNextMethod
, so that the base class gets initialized properly. Using ...
and passing it to callNextMethod
means that slots that might be defined in Species
would also be initialized correctly. Also, x
needs to be after ...
, because initialize
is defined to take unnamed arguments that represent contained classes -- new("MySpecies", new("Species"))
is required to work, even if it is a way of constructing arguments that you do not use directly. The initialize
method above doesn't actually do anything more than the default initialize method, so in reality (and this is often the case) it makes sense not to write an initialize method at all.
And then in more recent R, setClass
returns a default constructor so
MySpecies <- setClass("MySpecies", contains="Species", ##Species is an S4 class
representation(x="numeric"))
and then
> MySpecies(x=1:5, y=5:1)
An object of class "MySpecies"
Slot "x":
[1] 1 2 3 4 5
Slot "y":
[1] 5 4 3 2 1