setClass               package:methods               R Documentation

Create a Class Definition

Description:

     Functions to create ('setClass') and manipulate class definitions.

Usage:

     setClass(Class, representation, prototype, contains=character(),
              validity, access, where, version, sealed, package)

     removeClass(Class, where)

     isClass(Class, formal=TRUE, where)

     getClasses(where, inherits = missing(where))

     findClass(Class, where, unique = "")

     resetClass(Class, classDef, where)

     sealClass(Class, where)

Arguments:

   Class: character string name for the class.  Other than 'setClass',
          the functions will usually take a class definition instead of
          the string (allowing the caller to identify the class
          uniquely).

representation: the slots that the new class should have and/or other
          classes that this class extends.  Usually a call to the
          'representation' function. 

prototype: an object (usually a list) providing the default data for
          the slots specified in the representation. 

contains: what classes does this class extend?  (These are called
          _superclasses_ in some languages.)  When these classes have
          slots, all their slots will be contained in the new class as
          well. 

   where: For 'setClass' and 'removeClass', the environment in which to
          store or remove the definition.  Defaults to the top-level
          environment of the calling function (the global environment
          for ordinary computations, but the environment or name space
          of a package when loading that package).

          For other functions, 'where' defines where to do the search
          for the class definition, and the default is to search from
          the top-level environment or name space of the caller to this
          function. 

  unique: if 'findClass' expects a unique location for the class,
          'unique' is a character string explaining the purpose of the
          search (and is used in warning and error messages).  By
          default, multiple locations are possible and the function
          always returns a list. 

inherits: in a call to 'getClasses', should the value returned include
          all parent environments of 'where', or that environment only?
           Defaults to 'TRUE' if 'where' is omitted, and to 'FALSE'
          otherwise. 

validity: if supplied, should be a validity-checking method for objects
          from this class (a function that returns 'TRUE' if its
          argument is a valid object of this class and one or more
          strings describing the failures otherwise).  See
          'validObject' for details.

  access: Access list for the class.  Saved in the definition, but not
          currently used.

 version: A version indicator for this definition. Saved in the
          definition, but not currently used.

  sealed: If 'TRUE', the class definition will be sealed, so that
          another call to 'setClass' will fail on this class name. 

 package: An optional package name for the class.  By default (and
          usually) the package where the class definition is assigned
          will be used. 

  formal: Should a formal definition be required? 

classDef: For 'removeClass', the optional class definition (but usually
          it's better for 'Class' to be the class definition, and to
          omit 'classDef'). 

Details:

     These are the functions that create and manipulate formal class
     definitions.  Brief documentation is provided below.  See the
     references for an introduction and for more details.

     'setClass': Define 'Class' to be an S-style class.  The effect is
          to create an object, of class '"classRepEnvironment"', and
          store this (hidden) in the specified environment or database.
           Objects can be created from the class (e.g., by calling
          'new'), manipulated (e.g., by accessing the object's slots),
          and methods may be defined including the class name in the
          signature (see 'setMethod').

     'removeClass': Remove the definition of this class, from the
          environment 'where' if this argument is supplied; if not,
          'removeClass' will search for a definition, starting in the
          top-level environment of the call to 'removeClass', and
          remove the (first) definition found.

     'isClass': Is this the name of a formally defined class? (Argument
          'formal' is for compatibility and is ignored.)

     'getClasses': The names of all the classes formally defined on
          'where'.  If called with no argument, all the classes visible
          from the calling function (if called from the top-level, all
          the classes in any of the environments on the search list). 
          The 'inherits' argument can be used to search a particular
          environment and all its parents, but usually the default
          setting is what you want.

     'findClass': The list of environments or positions on the search
          list in which a class definition of 'Class' is found.  If
          'where' is supplied, this is an environment (or name space)
          from which the search takes place; otherwise the top-level
          environment of the caller is used.  If 'unique' is supplied
          as a character string, 'findClass' returns a single
          environment or position.  By default, it always returns a
          list. The calling function should select, say, the first
          element as a position or environment for functions such as
          'get'.

          If 'unique' is supplied as a character string, 'findClass'
          will warn if there is more than one definition visible (using
          the string to identify the purpose of the call), and will
          generate an error if no definition can be found.

     'resetClass': Reset the internal definition of a class.  Causes
          the complete definition of the class to be re-computed, from
          the representation and superclasses specified in the original
          call to 'setClass'.

          This function is called when aspects of the class definition
          are changed.  You would need to call it explicitly if you
          changed the definition of a class that this class extends
          (but doing that in the  middle of a session is living
          dangerously, since it may invalidate existing objects).

     'sealClass': Seal the current definition of the specified class,
          to prevent further changes.  It is possible to seal a class
          in the call to 'setClass', but sometimes further changes have
          to be made (e.g., by calls to 'setIs').  If so, call
          'sealClass' after all the relevant changes have been made.

Inheritance and Prototypes:

     Defining new classes that inherit from ('extend') other classes is
     a powerful technique, but has to be used carefully and not
     over-used.  Otherwise, you will often get unintended results when
     you start to compute with objects from the new class.

     As shown in the examples below, the simplest and safest form of
     inheritance is to start with an explicit class, with some slots,
     that does not extend anything else.  It only does what we say it
     does.

     Then extensions will add some new slots and new behavior.

     Another variety of extension starts with one of the built-in data
     types, perhaps with the intention of modifying R's standard
     behavior for that class.  In this case, the new class inherits the
     built-in data type as its 'data' part.  See the '"numWithId"'
     example below.

     When such a class definition is printed, the data part shows up as
     a pseudo-slot named '.Data'.

S3 Classes:

     Earlier, informal classes of objects (usually referred to as 'S3'
     classes) are used by many R functions.  It is natural to consider
     including them as the class for a slot in a formal class, or even
     as a class to be extended by the new class.  This isn't prohibited
     but there are some disadvantages, and if you do want to include S3
     classes, they should be declared by including them in a call to
     'setOldClass'.  Here are some considerations:

        *  Using S3 classes somewhat defeats the purpose of defining a
           formal class:  An important advantage to your users is that
           a formal class provides guarantees of what the object
           contains (minimally, the classes of the slots and therfore
           what data they contain; optionally, any other requirements
           imposed by a validity method).

           But there is no guarantee whatever about the data in an
           object from an S3 class.  It's entirely up to the functions
           that create or modify such objects.  If you want to provide
           guarantees to your users, you will need a valdity method
           that explicitly checks the contents of S3-class objects.

        *  To get the minimal guarantee (that the object in a slot has,
           or extends, the class for the slot) you should ensure that
           the S3 classes are known to _be_ S3 classes, with the
           possible inheritance.  To do this, include a call to
           'setOldClass' for the S3 classes used.

           Otherwise, the S3 class is undefined (and the code used by
           'setClass' will issue a warning).  Slot assignments, for
           example, will not then check for possible errors.

        *  These caveats apply to S3 classes; that is, objects with a
           class assigned by some R function but without a formal class
           definition.  In contrast, the built-in data types
           ('numeric', 'list', etc.) are generally fine as slots or for
           'contains=' classes (see the previous section).  These data
           types don't have formal slots, but the base code in the
           system essentially forces them to contain the type of data
           they claim to have.

           The data types 'matrix' and 'array' are somewhat in between.
            They do not have an explicit S3 class, but do have one or
           two attributes.  There is no general problem in having these
           as slots, but because there is no guarantee of a dimnames
           slot, they don't work as formal classes.  The 'ts' class is
           treated as a formal class, extending class 'vector'.

Note:

     Certain slot names are not allowed in the current implementation,
     as they correspond to attributes which are treated specially. 
     These are 'class', 'comment', 'dim', 'dimnames', 'names',
     'row.names' and 'tsp'.

References:

     The R package 'methods' implements, with a few exceptions, the
     programming interface for classes and methods in the book
     _Programming with Data_ (John M. Chambers, Springer, 1998), in
     particular sections 1.6, 2.7, 2.8, and chapters 7 and 8.

     While the programming interface for the 'methods' package follows
     the reference, the R software is an original implementation, so
     details in the reference that reflect the S4 implementation may
     appear differently in R.  Also, there are extensions to the
     programming interface developed more recently than the reference. 
     For a discussion of details see '?Methods' and the links from that
     documentation.

See Also:

     'setClassUnion', 'Methods', 'makeClassRepresentation'

Examples:

     ## A simple class with two slots
     setClass("track",
              representation(x="numeric", y="numeric"))
     ## A class extending the previous, adding one more slot
     setClass("trackCurve",
              representation("track", smooth = "numeric"))
     ## A class similar to "trackCurve", but with different structure
     ## allowing matrices for the "y" and "smooth" slots
     setClass("trackMultiCurve",
              representation(x="numeric", y="matrix", smooth="matrix"),
              prototype = list(x=numeric(), y=matrix(0,0,0),
                               smooth= matrix(0,0,0)))
     ##
     ## Suppose we want trackMultiCurve to be like trackCurve when there's
     ## only one column.
     ## First, the wrong way.
     try(setIs("trackMultiCurve", "trackCurve",
         test = function(obj) {ncol(slot(obj, "y")) == 1}))

     ## Why didn't that work?  You can only override the slots "x", "y",
     ## and "smooth" if you provide an explicit coerce function to correct
     ## any inconsistencies:

     setIs("trackMultiCurve", "trackCurve",
       test = function(obj) {ncol(slot(obj, "y")) == 1},
       coerce = function(obj) {
          new("trackCurve",
              x = slot(obj, "x"),
              y = as.numeric(slot(obj,"y")),
              smooth = as.numeric(slot(obj, "smooth")))
       })

     ## A class that extends the built-in data type "numeric"

     setClass("numWithId", representation(id = "character"),
              contains = "numeric")

     new("numWithId", 1:3, id = "An Example")

