5 Types and Classes
All slot access is performed by function calls.[2] The method that returns the value of a slot is called the getter method, and the method that sets the value of a slot is called the setter method. The getter and setter methods are added to generic functions. When defining a class, you specify slots by specifying the generic functions to which the getter and setter methods should be added.
For example, the class definition for <point>
might be
define class <point> (<object>) slot horizontal; slot vertical; end class;This definition indicates that instances of <point> should have two slots,
horizontal
and vertical
. The getter method for the first slot is added to the generic function horizontal
, and the getter method for the second slot is added to the generic function vertical
. The setter method for the first slot is added to the generic function horizontal-setter
, while the setter method for the second slot is added to the generic function vertical-setter
.The following two code fragments are equivalent. Each returns the horizontal coordinate of a point:
horizontal(a-point) a-point.horizontal;The following three code fragments each set the horizontal coordinate of a point to 10:
horizontal-setter(10, my-point) horizontal(my-point) := 10; my-point.horizontal := 10;A slot setter method returns its new value argument.
The collection of all the getter and setter generic functions for slots specified in a class or inherited from its superclasses must not contain any duplicates.
If a superclass is inherited through multiple paths, its slots are inherited once. For example, if class A has direct superclasses B and C, and both B and C have D as a direct superclass, A inherits from D both through B and through C, but the slots defined by D are only counted once. Because of this, multiple inheritance does not by itself create any duplicates among the getters and setters.
Note that two classes which specify a slot with the same getter or setter generic function are disjoint --they can never have a common subclass and no object can be an instance of both classes.
A slot specification must include the name of the getter of the slot (i.e. the name of the generic function to which the getter method will be added). This is how slots are identified. The specification may optionally include the name of the setter method. If it does not, a default name is generated by appending "-setter
" to the name of the getter.
A number of other options are available in slot specifications:
For the complete syntax of slot specifications, see the reference entry of define class
on page 364.
The following example defines a class with three slots, using a variety of slot options.
define class <window> (<view>) slot title :: <string> = "untitled"; slot position :: <point>, init-keyword: window-position:; slot color, init-keyword: color:, init-value: $blue-color; end class <window>;
There are three kinds of init specifications:
Only one init specification may be supplied in a given slot specification, inherited slot specification, or initialization argument specification.
In general, an init-function will only be called and an init-expression will only be executed if its value will actually be used.
make
when an instance is created. An init-keyword may be optional or required.
When the value of a slot is provided by a keyword in a call to make
, it is called an initialization argument.
If an init-keyword is specified, the slot is said to be keyword initializable.
instance
, class
, each-subclass
, and virtual
.
instance
allocation specifies that each instance gets its own storage for the slot. This is the default.
class
allocation specifies there is only one storage location used by all the general instances of the class. All the instances share a single value for the slot. If the value is changed in one instance, all the instances see the new value.
each-subclass
allocation specifies that the class gets one storage location for the slot, to be used by all the direct instances of the class. In addition, every subclass of the class gets a storage location for the slot, for use by its direct instances.
virtual
allocation specifies that no storage will be allocated for the slot. If allocation is virtual
, then it is up to the programmer to define methods on the getter and setter generic functions to retrieve and store the value of the slot. Dylan will ensure the existence of generic functions for any specified getter and setter but will not add any methods to them. A virtual slot cannot specify an init specification or init-keyword. Any required initialization for the slot must be performed in a method on initialize
.
setter: #f
. If the constant adjective is supplied, it is an error to supply an explicit value for the setter:
keyword in the slot specification. Such slots can only be given values at instance creation time (with an init specification or init-keyword).
define class <person> (<being>) constant slot birthplace, required-init-keyword: birthplace:; end class <person>;
define class <astronaut> (<person>) constant class slot employer = #"NASA"; end class <astronaut>;
define class <hair-trigger> (<object>) constant slot error-if-touched; end class <hair-trigger>;
The following example demonstrates how an explicitly defined setter method can be used to coerce a slot value of the wrong type (<sequence>
) to the right type (<simple-object-vector>
).
define class <person> (<object>) slot friends :: <simple-object-vector>, init-value: #[]; end class; define method friends-setter (f :: <sequence>, p :: <person>) p.friends := as(<simple-object-vector>, f); f; // return new-value argument end method tom.friends := list(dick, harry);The assignment expression invokes the method with the new-value parameter specialized on
<sequence>
, which reinvokes the function with a new-value argument that is a <simple-object-vector>
, which invokes the slot setter method.
In the following example, the class <view>
stores position
directly, while <displaced-view>
performs a transformation on the value of the slot when storing or retrieving it.
define class <view> (<object>) instance slot position; end class; define class <displaced-view> (<view>) end class; define method position (v :: <displaced-view>) // call the inherited method (the raw slot getter) // and transform the result displace-transform (next-method (v)); end method; define method position-setter (new-position, v :: <displaced-view>) // call the inherited method (the raw slot setter) // on the result of untransforming the position next-method (displace-untransform (new-position, v); new-position; // return the new position end method;In other situations, a programmer will want storage in an instance for a slot value, but will want to perform some auxiliary action whenever the slot is accessed. In this case, the programmer should define two slots: an instance slot to provide the storage and a virtual slot to provide the interface. In general, only the virtual slot will be documented. The instance slot will be an internal implementation used by the virtual slot for storage. An example of such use would be a slot that caches a value.
define class <shape> (<view>) virtual slot image; instance slot cached-image, init-value: #f; ... end class; define method image (shape :: <shape>) cached-image (shape) | (cached-image (shape) := compute-image (shape)); end method; define method image-setter (new-image, shape :: <shape>) cached-image (shape) := new-image; end method;
Generated with Harlequin WebMaker