Chapter 5
Types and Classes
Instance Creation and Initialization
The creation and initialization of instances is controlled by the generic
functions initialize
and make
, using
initialization information supplied by the class definition and by keyword arguments in the
call to make
. Much of this behavior is supplied by the
default make
method defined on <class>
.
Overview
Instance creation and initialization proceeds through the following steps:
- The program calls
make
specifying a class and a set of keyword arguments. - Optionally, the default
make
method may be shadowed by a user-supplied method specialized with a singleton specializer. This enables the user method to get at all the arguments tomake
, and to provide actual instantiation and initializations based on them. For example, a singleton method on an abstract class can reinvokemake
on a concrete subclass of the abstract class, passing along the same or augmented initialization arguments. - The default
make
method examines its keyword arguments, which are known as the supplied initialization arguments. It then produces a set of defaulted initialization arguments by augmenting the supplied initialization arguments with any additional initialization arguments for which default values are defined by the class or any of its superclasses.
If the supplied initialization arguments contains duplicate keywords,make
will use the leftmost occurrence. This is consistent with keyword argument conventions used in function calls. - The default
make
method signals an error if any required init-keyword is absent from the defaulted initialization arguments, or if any of the defaulted initialization arguments are not valid for initialization of that class. An initialization argument is valid if it is specified as an init-keyword in a slot specification or initialization argument specification, or if it is permitted by one or more of theinitialize
methods applicable to an instance of the class. - The default
make
method allocates an instance and initializes all the slots for which it can provide values, as follows:- If the slot is keyword initializable and its keyword is present in the defaulted initialization arguments, then the slot is initialized from the defaulted initialization arguments.
- If the slot is not initialized by a keyword but has an init specification, it is initialized from the init specification.
- In either case, an error of type
<type-error>
is signaled if the value is not of the type declared for the slot. - Regardless of the source, the initial value is stored into the slot using an unspecified built-in mechanism. In particular, the setter generic function is not called to store the value into the slot.
- The default
make
method then callsinitialize
on the initialized instance and the defaulted initialization arguments. Methods on initialize can access these arguments by accepting them as keyword parameters or in a rest parameter. If they are accepted in a rest parameter and the defaulted initialization arguments contained duplicate keywords, it is undefined whether any entries other than the leftmost for that keyword will be present. - Each
initialize
method typically callsnext-method
, and then performs its own initializations. (Note that it won't have to initialize slots that were initialized by the defaultmake
method.) - The default
make
method ignores the value of the call toinitialize
and returns the instance.
The values of virtual slots are not automatically initialized when a
new instance is created. The programmer must perform any necessary initialization. This
would usually be done inside a method on initialize
. Because the values of
virtual slots are often computed from other values at run-time, many virtual slots will not
require any explicit initialization.
Additional Behavior of Make and Initialize
The object returned by make
is guaranteed to be a general instance of the
first argument to make
, but not necessarily a direct instance. This liberality
allows make
to be called on an abstract class; it can instantiate and return a
direct instance of one of the concrete subclasses of the abstract class.
define abstract class <dog> (<object>) end class define class <yorkshire-terrier> (<dog>) end class define method make (the-class == <dog>, #rest init-args, #key) apply(make, <yorkshire-terrier>, init-args) end make(<dog>) ⇒ {instance of <yorkshire-terrier>}
make
is not required to return a newly allocated instance. It may return a
previously created instance if that is appropriate. If a new instance is
allocated, make
will call initialize
on the instance before
returning it.
The make
method on <class>
returns a newly allocated direct
instance of its first argument.
Programmers may customize make
for particular classes by defining methods
specialized on singletons of classes. These methods may reinvoke make
on a
subtype of the class, or they may obtain the default make
behavior by calling
next-method.
The default make
method signals an error if its first argument is an abstract
class. An instantiable abstract class must override this method with its own method
for make
.
Initialization of Class Allocated Slots
The initalization of slots with allocation class or each-subclass is performed in the following way:
- If the slot is not keyword initializable and the class definition does not include an init specification for the slot, the slot remains uninitialized until it is explicitly assigned by the program.
- If the slot is not keyword initializable and the class definition does include an init specification for the slot, the slot is initialized from the init specification before or during the creation of the first instance of the class.
- If the slot is keyword initializable and the class definition also
includes an init specification for the slot, the slot may be initialized or assigned by
the default method of
make
whenever an instance is created, as follows:- If the corresponding initialization argument is absent from the defaulted
initialization arguments of the call to
make
and the slot has not yet been initialized, then the slot is initialized from the init specification. If the slot has already been initialized, no action is taken. - If the corresponding initialization argument is present in the defaulted
initialization arguments of the call to
make
, then the slot is set to the value of that initialization argument, regardless of whether the slot was previously initialized.
- If the corresponding initialization argument is absent from the defaulted
initialization arguments of the call to
Testing the Initialization of a Slot
A program can test to see whether a slot has been initialized, using
the slot-initialized?
function, described
on page 261. There is no
portable mechanism for resetting a slot to the uninitialized state once it has been
initialized.
To support the slot-initialized?
protocol in a virtual slot, programmers must
define a method for slot-initialized?
that specializes on the getter of the
slot and the class.
Inherited Slot Specifications
An inherited slot specification is used to provide an init specification for a slot inherited from a superclass. It can add an init specification if one was not already present, or it can override an existing init specification.
Inherited slot specifications identify the slot to be modified by the getter name. The inherited slot specification is only allowed if the class does indeed inherit a slot with that getter.
(An inherited slot specification is not required to include an init specification. If it does not, its only purpose is to ensure that the slot is present in a superclass. Because init specifications are not allowed for virtual slots, this is the only valid form of inherited slot specification for virtual slots.)
If an inherited slot specification supplies an init specification, it overrides any init specification inherited from a superclass. This allows the init specification of an inherited slot to be replaced in a subclass, thereby changing the default initial value of the slot.
define class <animal> (<object>) slot n-legs, init-value: 4; end class; define class <spider> (<animal>) inherited slot n-legs, init-value: 8; end class;
Initialization Argument Specifications
Initialization argument specifications provide options for the handling of initialization
arguments. They appear in define class
forms, and have a syntax similar to that
of slot specifications.
Initialization argument specifications allow the type of an initialization argument to be restricted, they allow an initialization argument to be declared to be required, and they allow the specification of a default value for an initialization argument.
Note that an initialization argument will only be used if it is specified to be the
init-keyword of a slot, or if it is used as a keyword argument in an applicable method
on initialize
. An initialization argument specification can supply a default
value for an initialization argument, and it can restrict the type of the argument or make
it required, but it does not by itself cause the argument to be used when initializing an
instance.
There are two kinds of initialization argument specifications: required initialization argument specifications, and optional initialization argument specifications.
A required initialization argument specification asserts that the initialization argument
must be present in the defaulted initialization arguments. The default make
method will signal an error if no such initialization argument is present.
An optional initialization argument specification can be used to specify
a default value for the initialization argument, using an init
specification. When a call to make
does not specify the initialization
argument, the default make
method will add it to the defaulted initialization
arguments with the value of the init specification.
The type argument has the same meaning in both kinds of initialization argument specification: it restricts the type of that initialization argument. Note that this is not the same thing as restricting the type of the slot.
The following example shows how initialization argument specifications can be used to override the behavior of a superclass:
define class <person> (<object>) slot favorite-beverage, init-value: #"milk", init-keyword: favorite-beverage:; slot name, required-init-keyword: name:; end class <person>; define class <astronaut> (<person>) keyword favorite-beverage:, init-value: #"tang"; keyword name:, init-value: "Bud"; end class <astronaut>;
In this example, the <astronaut>
class provides default values for
the favorite-beverage:
and name:
init-keywords. In addition to
indirectly supplying default values for these slots, this also has the effect of making
the name:
argument optional in calls to make
on <astronaut>
. If the call to make
does not specify
a name:
, the name:
will be added to the defaulted initialization
arguments by the default make
method before the defaulted initialization
arguments are checked for completeness.
More than one keyword initializable slot may be initialized from the same initialization
argument (that is, more than one keyword initializable slot may specify the same
init-keyword). However, an error is signaled if a single define-class
form has
more than one initialization argument specification for the same keyword. An error will also
be signaled if a single define-class
form has a keyword initializable slot that
includes an init specification and also includes an initialization argument specification
for the same keyword that is either required or provides a default value. These error
situations are all indications of code that can never be
reached.
Errata: In the published
book, the definition of class <person>
is missing a comma
between slot name
and required-init-keyword:
. The definition
of class <astronaut>
is missing commas in its keyword descriptors,
between the keyword and init-value:
.
Initialization Argument Inheritance
The inheritance of initialization argument specifications is defined as follows.
- A slot specification that supplies an init-keyword K by
using
required-init-keyword:
is treated as if the initialization argument specificationrequired keyword
K had been specified in the class definition. - A slot specification that supplies both an init-keyword and also an init specification is not equivalent to an initialization argument specification that includes both the init-keyword and an init specification. In the former case the init specification is used to default the value of the slot directly, but does not affect the defaulted initialization arguments; in the latter case the init specification is used to default the value of the slot indirectly, by affecting the defaulted initialization arguments.
- If the initialization argument is being specified for the first time (it is not
inherited from any superclass) there are three factors to consider:
- The
type:
argument, which defaults to<object>
, specifies the required type of the initialization argument. (This does not specify the type of the slot.) - If the initialization argument is specified with
required keyword
then it is required, otherwise it is optional. - If the initialization argument is specified
with
keyword
, then it can provide an init specification that is used by the defaultmake
method to provide a default value for the initialization argument in the defaulted initialization arguments.
- The
- If an initialization argument specification is being specified for an initialization
argument that is inherited from a single superclass, the following factors hold:
- The type must be a subtype of the type of the inherited initialization
argument. This implies that the type must be specified unless the type of the
inherited initialization argument is
<object>
. - The initialization argument is required if the overriding initialization argument
specification uses
required keyword
, or if the inherited initialization argument specification is required and the overriding initialization argument specification does not provide an init specification. When the overriding initialization argument specification usesrequired keyword
, any init specification in the inherited initialization argument specification is discarded. This means that a subclass can make an initialization argument used by a superclass become required; it can also make a required initialization argument become optional by specifying a default value for it. - Otherwise, the initialization argument is optional. If the overriding specification provides an init specification, then that is used to compute the defaulted initialization argument when the class is instantiated. Otherwise, the inherited initial value specification is used.
- The type must be a subtype of the type of the inherited initialization
argument. This implies that the type must be specified unless the type of the
inherited initialization argument is
- When an initialization argument specification is being inherited from multiple superclasses, if the superclasses have exactly the same definition for the initialization argument, then that definition can simply be inherited. If the definitions differ, then the class that combines these other classes must provide an initialization argument specification that is compatible with all of the inherited ones, as described above.