Call more then one slot or fields in S4 or Reference Classes

359 views Asked by At

Is it possible to call or set values for more then one slot?

A<-setClass(Class="A",slot=c(name="character",type="character"))
a<-A()
slot(object,c("name","type"),check=T)

Do I have to write own getSlot and setSlot methods? And how to that in R5?

AB <- setRefClass("AB", fields=c(name="character"),
                  methods=list(getName=AB.getName)
                  )

AB.getName<-function(object){
  object$name
}

a<-AB(name="abc")
AB.getName(a)
2

There are 2 answers

3
Richie Cotton On BEST ANSWER

This answer applies to reference classes.

Let's start with the simplest definition of AB, without any methods.

AB <- setRefClass(
  "AB", 
  fields = list(
    name = "character"
  )
)

You can retrieve the value of the name field in the same way you would a list.

ab <- AB$new(name = "ABC")
ab$name
## [1] "ABC"
(ab$name <- "ABCD")
## [1] "ABCD"

It is possible to autogenerate accessor methods to get and set the name field.

AB$accessors("name")
ab$getName()
ab$setName("ABCDE")

This is really pointless though since it has the exactly same behaviour as before, but with more typing. What can be useful is to do input checking (or other custom behaviour) when you set a field. To do this, you can add a setName method that you write yourself.

AB$methods(
  setName = function(x) 
  {
    if(length(x) > 1)
    {
      warning("Only using the first string.")
      x <- x[1]
    }
    name <<- x
  }
)
ab$setName(letters)
## Warning message:
## In ab$setName(letters) : Only using the first string.

It is also possible (and usually more useful) to define this method when you assign the reference class template.

AB <- setRefClass(
  "AB", 
  fields = list(
    name = "character"
  ), 
  methods = list(
    setName = function(x) 
    {
      if(length(x) > 1)
      {
        warning("Only using the first string.")
        x <- x[1]
      }
      name <<- x
    }
  )
)

Response to comment:

Yes that works, but:

getFieldNames is more maintainable if implemented as names(AB$fields()).

When defining fields in setRefClass, use a list. For example, list(name="character", var2="character").

When assigning an instance of a reference class, use new. For example, AB$new(name="abc",var2="abc")

2
Martin Morgan On

In S4, the default initialize method allows one to write

A <- setClass(Class="A", slot=c(name="character",type="character"))
a <- A(name="abc", type="def")
initialize(a, name="cde", type="fgh")

Your own initialize methods (if any -- I think it's usually best to avoid them) have to be written to allow for this use. There is no default way to convert an S4 representation to a list.

You could incorporate these ideas into your own generics / methods with something like

setGeneric("values", function(x, ...) standardGeneric("values"))
setMethod("values", "A", function(x, ...) {
    slts = slotNames(x)
    lapply(setNames(slts, slts), slot, object=x)
})

setGeneric("values<-", function(x, ..., value) standardGeneric("values<-"))
setReplaceMethod("values", c(x="A", value="list"), function(x, ..., value) {
    do.call("initialize", c(x, value))
})

with

> a <- A(name="abc", type="def")
> values(a)  = list(name="cde", type="fgh")
> values(a)
$name
[1] "cde"

$type
[1] "fgh"