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 mimicked.  Registering via
     'setOldClass' allows S3 classes to appear  in method signatures,
     and as a slot in an S4 class if a prototype is included.

Usage:

     setOldClass(Classes, prototype, 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. 

prototype: An optional object to use as the prototype.  This should be
          provided as the default S3 object for the class, if you plan
          to use the class as a slot in an S4 class.  See the details
          section. 

   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 an S4 class, extending the
     remaining classes in 'Classes', and the class 'oldClass', which is
     the 'root' of all old-style classes. S3 classes have no formal
     definition, and therefore no formally defined slots. If a
     'prototype' argument is supplied in the call to 'setOldClass()',
     objects from the class can be generated. If the S3 class is to be
     a slot in an S4 class, providing a prototype is recommended.
     Otherwise, the class will be created as a virtual S4 class; method
     dispatch will still work and inheritance will follow the S3 class
     hierarchy, but actions that require a prototype object from the
     class will not. For example, using the class as a slot in an S4
     class definition will set the corresponding slot to 'NULL' in the
     prototype for the S4 class.

     Providing a prototype allows the function 'new()' to be called for
     this class, but optional arguments in this call are not
     meaningful, since the class has no formal slots. Extending an S3
     class with an S4 class is formally legal, but discouraged. Since
     the S4 subclass will have a single character string in its
     'class()', S3 inheritance will not work. Also, there is no safe
     way for a general object from the S3 class to be inserted when an
     object is generated from the subclass.

     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'.

     Some S3 classes cannot be represented as an ordinary combination
     of S4 classes and superclasses, because objects from the S3 class
     can have a variable set of strings in the class. It is still
     possible to register such classes as S4 classes, but now the
     inheritance has to be verified for each object, and you must call
     'setOldClass' with argument 'test=TRUE' once for each superclass.

     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 '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.

     Supplying the 'prototype' and optionally the 'generator' arguments
     allows the S4 class created to be non-virtual, making it a
     candidate to be a slot in S4 class definitions and to be extended
     by  S4 classes. The class still does not have formally defined
     slots. Because R implements slots as attributes, an S3 class that
     uses attributes ('factor', for example) can in principle be
     defined as an S4 class with slots. However, a class such as 'lm'
     that uses components of a list in a similar role cannot have
     formal slots. The slots would  not be interpreted by S3 code
     written for 'lm' objects.

     _If_ your class does in fact have a consistent set of attributes,
     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, such a
     definition will not generally be possible.  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.

See Also:

     'setClass', 'setMethod'

Examples:

     require(stats)
     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")
     ## Not run: setOldClass("data.frame", prototoype = data.frame())

     ## End(Not run)

