DEFCLASS


Basic Syntax

(defclass Class-Name (Superclass*)
  (Slot-Definition*)
  Class-Option*)[1]

Examples

(defclass Graphical-Object ()
  ())
(defclass Rectangle (Graphical-Object)
  ((Height :accessor Height :initarg :Height :initform 3)
   (Width  :accessor Width  :initarg :Width  :initform 5)))
(defclass Square (Rectangle)
  ()
  (:documentation "The SQUARE class"))

make-instance and slot-value

Given a class definition, make-instance will make an actual object of that class. The slot-value function is the most primitive function for retrieving and setting values of slots.

(defclass Submarine ()
  ((Depth)
   (Speed)))

(setq Sub-1 (make-instance 'Submarine))
(setf (slot-value Sub-1 'Depth) 100)
(slot-value Sub-1 'Depth) ==> 100
(slot-value Sub-1 'Speed) ==> [Error: unbound slot SPEED]

Unlike with the methods defined by :reader, :writer, and :accessor, you cannot put :before or :after methods on slot-value or (setf slot-value).

There are also functions slot-exists-p and slot-boundp for checking if a given instance has a certain slot and if that slot has a value, respectively.

Slot Definitions

Slot definitions are lists composed of the slot name followed optionally by pairs of the following slot option keywords and associated value.

:initform a default value for the slot (unbound otherwise)

:reader the name of a function to lookup the slot

:writer the name of a function to set the slot

:accessor the name of a function to read, and (with setf) set the slot

:initarg the name of a keyword for make-instance

:allocation whether slot is shared or specific to each instance

:type a Common Lisp type for use in compiler optimizations

:documentation documentation for the very rarely-seen slot object

Values supplied by :initform, :allocation, and :documentation override any previously supplied value in a superclass. Values supplied via :reader, :writer, :accessor and :initarg give values that apply to the current class in addition to values supplied for superclasses. A :type restriction imposed on a slot is intersected with the restrictions on that slot imposed by superclasses. Similarly, you can only supply one :initform, :allocation, :documentation, or :type entry for a given slot, but you can supply multiple :reader, :writer, :accessor or :initarg entries.

:initform

Specifies a default initial value for the slot. This value is evaluated each time make-instance is called. Supplying an explicit initial value at make-instance time (see :initarg) will override this.

(defclass Submarine ()
  ((Depth :initform 100)
   (Speed :initform 5)))

(setq Sub-1 (make-instance 'Submarine))
(slot-value 'Sub-1 'Speed) ==> 5
(slot-value 'Sub-1 'Depth) ==> 100

:reader

Specifies the name of a function that will read the slot (in lieu of using slot-value). This value is NOT evaluated, and thus should not be quoted. Using :accessor obviates the need for :reader or :writer.

(defclass Submarine ()
  ((Depth :reader Depth :initform 100)
   (Speed :reader Speed :initform 5)))

(setq Sub-1 (make-instance 'Submarine))
(Depth Sub-1) ==> 100
(Speed Sub-1) ==> 5

:writer

Specifies the name of a function that will set the slot (in lieu of using setf on slot-value). Not evaluated. Using :accessor obviates the need for :reader or :writer.

(defclass Submarine ()
  ((Depth :reader Depth :writer Set-Depth :initform 100)
   (Speed :reader Speed :writer Set-Speed :initform 5)))

(setq Sub-1 (make-instance 'Submarine))
(Depth Sub-1) ==> 100
(Speed Sub-1) ==> 5
(Set-Depth 200 Sub-1)
(Set-Speed 4 Sub-1)
(Depth Sub-1) ==> 200
(Speed Sub-1) ==> 4

:accessor

Specifies the name of a function that will both read and (with setf) set the slot. If you have a :accessor, you don't need to specify either :reader or :writer. Not evaluated.

(defclass Submarine ()
  ((Depth :accessor Depth :initform 100)
   (Speed :accessor Speed :initform 5)))

(setq Sub-1 (make-instance 'Submarine))
(Depth Sub-1) ==> 100
(Speed Sub-1) ==> 5
(setf (Depth Sub-1) 200)
(setf (Speed Sub-1) 4)
(Depth Sub-1) ==> 200
(Speed Sub-1) ==> 4

:initarg

Specifies a keyword that can be passed to make-instance to give a slot an initial value. If this keyword is used at make-instance time, it overrides any default initial value that :initform provided. Unlike with defstruct, defclass does not make any of these keywords automatically available.

(defclass Submarine ()
  ((Depth :accessor Depth :initarg :Depth :initform 100)
   (Speed :accessor Speed :initarg :Speed :initform 5)))

(setq Sub-1 (make-instance 'Submarine :Depth 200))
(Depth Sub-1) ==> 200
(Speed Sub-1) ==> 5

:allocation

Specifies whether a slot's value is specific to each instance (:instance), or has a single value that is shared by all instances of that class (:class). The default is :instance.

(defclass Submarine ()
  ((Depth :accessor Depth :initform 100)
   (Speed :accessor Speed :initform 5)))

(defclass Los-Angeles (Submarine)
  ((Max-Speed :accessor Max-Speed :initform 10 :allocation :class)))

(setq SSN-1 (make-instance 'Los-Angeles))
(setq SSN-2 (make-instance 'Los-Angeles))
(Max-Speed SSN-1) ==> 10
(Max-Speed SSN-2) ==> 10
(setf (Max-Speed SSN-1) 15)
(Max-Speed SSN-1) ==> 15
(Max-Speed SSN-2) ==> 15

:type

Specifies a Common Lisp type for the value of the slot. This type information may be used by the compiler to provide optimizations, or might be used to do error checking. However, error checking is not required, and probably won't be done except when compiled with high safety settings. It is similar to a type declaration in C: putting a value of the wrong type into a slot may result in a relatively arbitrary error next time the slot value is used. Also recall that, in Common Lisp in general, type integer doesn't let the compiler do many optimizations, since integers can be of arbitrary size. For fixed-size integers, like int's in C, use the fixnum type declaration.

The :type entry gets inherited by combining the type restrictions of the current class and all superclasses using and. This means that a class cannot relax a type restriction imposed by a superclass, only make it more stringent.

(defclass Submarine ()    ((Depth :type integer)))
(defclass SSN (Submarine) ((Depth :type fixnum)))

This means that a legal value of the Depth slot must satisfy (typep value '(and integer fixnum)).

:documentation

This option, rarely used, specifies documentation for the slot. The only way to retrieve this documentation is to call the documentation function on the slot object. However, the slot object can only be gotten by using a function in the CLOS MOP (Meta-Object Protocol). Since the MOP did not get accepted as part of the first ANSI standard for Lisp, this means that there is no way in standard Lisp to retrieve the slot documentation! Now, the simplest MOP functions are in fact in most implementations (including Lucid, Symbolics, Harlequin, and Franz), but since this is not guaranteed to be portable, slot documentation is generally not used.

Class Options

Class options are a list of one of the following keywords, along with an associated value.

:documentation documentation for the class object

:default-initargs default values for the previously specified :initarg's

:metaclass the superclass of the class object itself. Leave this for the CLOS wizards.

Each class option can be specified at most once for a given class.

:documentation

Specifies a documentation string for the class object. Passing the class object to the documentation function will retrieve it. The class object is returned from a call to defclass, or can be found by passing an instance to class-of or supplying a class name to find-class.

(setq Submarine-Class
  (defclass Submarine ()
    ((Depth :accessor Depth :initform 100)
     (Speed :accessor Speed :initform 5))
    (:documentation "The basic submarine class")) )

(setq Sub-1 (make-instance 'Submarine))

(documentation Submarine-Class)               ==> "The basic submarine class"
(documentation (class-of Sub-1))             ==> "The basic submarine class"
(documentation (find-class 'Submarine)) ==> "The basic submarine class"

:default-initargs

Supplies default values for :initarg entries. These entries are almost always ones that were provided by a superclass, since if they were defined in the current class it would be easier to just put in a :initform for them. This is an alternative to repeating the slot name and supplying a new :initform, but is preferred by some people as being more concise. In addition, in some situations the slot name is kept private, and only the :initarg's are advertized as the official interface. The :default-initargs entry overrides any local or inherited :initform. The values are evaluated every time make-instance is called.

(defclass Submarine ()
  ((Depth :accessor Depth :initarg :Depth :initform 100)
   (Speed :accessor Speed :initarg :Speed :initform 5)))

(defclass Slow-Submarine (Submarine)
  ()
  (:default-initargs 
     :Speed 1))

(setq Sub-1 (make-instance 'Slow-Submarine))
(Speed Sub-1) ==> 1

:metaclass

Can be used to change the general behavior of classes themselves, by changing the built-in superclass of user-defined classes from the usual default of standard-class. Not for the faint of heart.


[1] I'm using regular expression notation, where a "*" means "0 or more occurrences of". Thus
(Superclass*) means "0 or more superclasses, inside parens".

Table of contents

Basic Syntax
Examples
make-instance and slot-value
Slot Definitions
:initform
:reader
:writer
:accessor
:initarg
:allocation
:type
:documentation
Class Options
:documentation
:default-initargs
:metaclass