When the class is used by the developer (who knows the design of the class), using the assignment operator @<-
instead of a setter method as setFirstSlot
defined in the question may be better. The reason is that the former avoids returning the whole object.
However, setter methods are desirable to prevent users from trying assignments that do not match the definition of the slot in the class. I know that if we use @<-
to assign a character to the slot x
(which was defined as numeric
) an error is returned.
setClass('foo', representation(x = 'numeric', y = 'numeric'))
f <- new('foo')
f@x <- 1 # this is ok
f@y <- 2 # this is ok
f@x <- "a"
#Error in checkAtAssignment("foo", "x", "character") :
# assignment of an object of class “character” is not valid for @‘x’ in an object of class “foo”; is(value, "numeric") is not TRUE
But imagine a situation where the slot should contain only one element. This requirement in the length of the slot is not caught by @<-
:
# this assignment is allowed
f@x <- c(1, 2, 3, 4)
f@x
#[1] 1 2 3 4
In this situation we would like to define a setter method in order to inform the user about further restrictions in the definition of the slot. But then, we have to return the entire object and this may be an extra burden if the object is big.
As far as I know there is no way to define the length of a slot in its definition. The method setValidity
could be defined in order to check this or other requirements in the slots, but it seems that @<-
does not rely on validObject
and the assignment f@x <- c(1, 2, 3, 4)
would be allowed even if we define setValidity
:
valid.foo <- function(object)
{
if (length(object@x) > 1)
stop("slot ", sQuote("x"), " must be of length 1")
}
setValidity("foo", valid.foo)
# no error is detected and the assignment is allowed
f@x <- c(1, 4, 6)
f@x
#[1] 1 4 6
# we need to call "validObject" to check if everything is correct
validObject(f)
#Error in validityMethod(object) : slot ‘x’ must be of length 1
A possible solution is to modify the object in-place. The method set.x.inplace
below is based on this approach.
setGeneric("set.x.inplace", function(object, val){ standardGeneric("set.x.inplace") })
setMethod("set.x.inplace", "foo", function(object, val)
{
if (length(val) == 1) {
eval(eval(substitute(expression(object@x <<- val))))
} else
stop("slot ", sQuote("x"), " must be of length 1")
#return(object) # not necessary
})
set.x.inplace(f, 6)
f
#An object of class "foo"
#Slot "x":
#[1] 6
#Slot "y":
#[1] 2
# the assignment is not allowed
set.x.inplace(f, c(1,2,3))
#Error in set.x.inplace(f, c(1, 2, 3)) : slot ‘x’ must be of length 1
As this method does not perform a return operation, it can be a good alternative with objects of large size.