setOldClass             package:methods             R Documentation

Specify Names for Old-Style Classes

Description:

     Register an old-style (a.k.a. `S3') class as a formally defined
     class. The 'Classes' argument is the character vector used as the
     'class' attribute; in particular, if there is more than one
     string,  old-style class inheritance is mimiced.  Registering via
     'setOldClass' allows S3 classes to appear as slots or in method
     signatures.

Usage:

     setOldClass(Classes, where, test = FALSE)

Arguments:

 Classes: A character vector, giving the names for old-style classes,
          as they would appear on the right side of an assignment of
          the 'class' attribute. 

   where: Where to store the class definitions, the global or top-level
          environment by default.  (When either function is called in
          the source for a package, the class definitions will be
          included in the package's environment by default.)

    test: flag, if 'TRUE', inheritance must be tested explicitly for
          each object, needed if the S3 class can have a different set
          of class strings, with the same first string. See the details
          below.

Details:

     Each of the names will be defined as a virtual class, extending
     the remaining classes in 'Classes', and the class 'oldClass',
     which is the "root" of all old-style classes. See Methods for the
     details of method dispatch and inheritance. See the section
     *Register or Convert?* for comments on the alternative of defining
     "real" S4 classes rather than using 'setOldClass'.

     S3 classes have no formal definition, and some of them cannot be
     represented as an ordinary combination of S4 classes and
     superclasses.  It is still possible to register the classes as S4
     classes, but now the inheritance has to be verified for each
     object, and you must call 'setOldClass' with argument 'test=TRUE'.

     For example, ordered factors _always_ have the S3 class
     'c("ordered", "factor")'.  This is proper behavior, and maps
     simply into two S4 classes, with '"ordered"' extending '"factor"'.

     But objects whose class attribute has '"POSIXt"' as the first
     string may have either (or neither) of '"POSIXct"' or '"POSIXlt"'
     as the second string.  This behavior can be mapped into S4 classes
     but now to evaluate 'is(x, "POSIXlt")', for example, requires
     checking the S3 class attribute on each object. Supplying the
     'test=TRUE' argument to 'setOldClass' causes an explicit test to
     be included in the class definitions.  It's never wrong to have
     this test, but since it adds significant overhead to methods
     defined for the inherited classes, you should only supply this
     argument if it's known that object-specific tests are needed.

     The list '.OldClassesList' contains the old-style classes that are
     defined by the methods package.  Each element of the list is an
     old-style list, with multiple character strings if inheritance is
     included. Each element of the list was passed to 'setOldClass'
     when creating the 'methods' package; therefore, these classes can
     be used in 'setMethod' calls, with the inheritance as implied by
     the list.

Register or Convert?:

     A call to \section{setOldClass} creates formal classes
     corresponding to S3 classes, allows these to be used as slots in
     other classes or in a signature in 'setMethod', and mimics the S3
     inheritance.

     However, all such classes are created as virtual classes, meaning
     that you cannot generally create new objects from the class by
     calling 'new', and that objects cannot be coerced automatically
     from or to these classes.  All these restrictions just reflect the
     fact that nothing is inherently known about the "structure" of S3
     classes, or whether in fact they define a consistent set of
     attributes that can be mapped into slots in a formal class
     definition.

     _If_ your class does in fact have a consistent structure, so that
     every object from the class has the same structure, you may prefer
     to take some extra time to write down a specific definition in a
     call to 'setClass' to convert the class to a fully functional
     formal class.  On the other hand, if the actual contents of the
     class vary from one object to another, you may have to redesign
     most of the software using the class, in which case converting it
     may not be worth the effort.  You should still register the class
     via 'setOldClass', unless its class attribute is hopelessly
     unpredictable.

     An S3 class has consistent structure if each object has the same
     set of attributes, both the names and the classes of the
     attributes being the same for every object in the class.  In
     practice, you can convert classes that are slightly less well
     behaved.  If a few attributes appear in some but not all objects,
     you can include these optional attributes as slots that _always_
     appear in the objects, if you can supply a default value that is
     equivalent to the attribute being missing.  Sometimes 'NULL' can
     be that value: A slot (but not an attribute) can have the value
     'NULL'.  If 'version', for example, was an optional attribute, the
     old test 'is.null(attr(x,"version")' for a missing version
     attribute could turn into 'is.null(x@version)' for the formal
     class.

     The requirement that slots have a fixed class can be satisfied
     indirectly as well.  Slots _can_ be specified with class '"ANY"',
     allowing an arbitrary object.  However, this eliminates an
     important benefit of formal class definitions; namely, automatic
     validation of objects assigned to a slot.  If just a few different
     classes are possible, consider using 'setClassUnion' to define
     valid objects for a slot.

setOldClass:

See Also:

     'setClass', 'setMethod'

Examples:

     setOldClass(c("mlm", "lm"))
     setGeneric("dfResidual", function(model)standardGeneric("dfResidual"))
     setMethod("dfResidual", "lm", function(model)model$df.residual)

     ## dfResidual will work on mlm objects as well as lm objects
     myData <- data.frame(time = 1:10, y = (1:10)^.5)
     myLm <- lm(cbind(y, y^3)  ~ time, myData)



     rm(myData, myLm)
     removeGeneric("dfResidual")

