Search code examples
rconstructorr-s4

With R S4 classes, is it possible to have non-optional constructor parameters


Suppose I have an S4 class Test that has a single slot name. A valid name must be at least one character long, so Test(name = "Bob") should work but Test(name = "") should throw an error. An undefined name should also give an error: Test().

My class is defined as:

Test <- setClass(
    "Test", 
    slots = c(name = "character"), 
    validity = function(object) {

        if (nchar(object@name) == 0) {

            return("name must at least one character long")
        }

        T
    })

Testing the class in the console, I find that my validity function is not executed for the unassigned case:

> Test(name = "Bob")
An object of class "Test"
Slot "name":
[1] "Bob"

> Test(name = "")
Error in validObject(.Object) : 
  invalid class “Test” object: name must at least one character long

> Test()
An object of class "Test"
Slot "name":
character(0)

How can I ensure that an error is always thrown when an invalid object is created?


Solution

  • One way to ensure the validity of an S4 is the use of prototype to initialise the slots like this

    Test <- setClass(
        "Test",
        slots = c(name = "character"),
        prototype = prototype(name = 'name_default'),
        validity = function(object) {
            if (nchar(object@name) == 0) {
                return("name must at least one character long")
            }
        })
    Test(name = "Bob")
    ## An object of class "Test"
    ## Slot "name":
    ## [1] "Bob"
    Test(name = '')
    ## Error in validObject(.Object) : 
    ##   invalid class "Test" object: name must at least one character long
    Test()
    ## An object of class "Test"
    ## Slot "name":
    ## [1] "name_default"
    

    another way would be to create a constructor to test the presence of name:

    consTest <- function(name) {
        if (missing(name)) {
            stop("name is missing")
        } else {
            new(Class = "Test", name = name)
        }
    }
    consTest(name = "Bob") # similar to Test(...)
    consTest(name = '') # similar to Test(...)
    consTest()
    ## Error in consTest() (from Retest.R@13#3) : name is missing