11.3 槽的属性 (Slot Properties)
传给 defclass
的第三个参数必须是一个槽定义的列表。如上例所示,最简单的槽定义是一个表示其名称的符号。在一般情况下,一个槽定义可以是一个列表,第一个是槽的名称,伴随着一个或多个属性 (property)。属性像关键字参数那样指定。
通过替一个槽定义一个访问器 (accessor),我们隐式地定义了一个可以引用到槽的函数,使我们不需要再调用 slot-value
函数。如果我们如下更新我们的 circle
类定义,
(defclass circle ()
((radius :accessor circle-radius)
(center :accessor circle-center)))
那我们能够分别通过 circle-radius
及 circle-center
来引用槽:
> (setf c (make-instance 'circle))
#<CIRCLE #XC5C726>
> (setf (circle-radius c) 1)
1
> (circle-radius c)
1
通过指定一个 :writer
或是一个 :reader
,而不是 :accessor
,我们可以获得访问器的写入或读取行为。
要指定一个槽的缺省值,我们可以给入一个 :initform
参数。若我们想要在 make-instance
调用期间就将槽初始化,我们可以用 :initarg
定义一个参数名。 [1] 加入刚刚所说的两件事,现在我们的类定义变成:
(defclass circle ()
((radius :accessor circle-radius
:initarg :radius
:initform 1)
(center :accessor circle-center
:initarg :center
:initform (cons 0 0))))
现在当我们创建一个 circle
类的实例时,我们可以使用关键字参数 :initarg
给槽赋值,或是將槽的值设为 :initform
所指定的缺省值。
> (setf c (make-instance 'circle :radius 3))
#<CIRCLE #XC2DE0E>
> (circle-radius c)
3
> (circle-center c)
(0 . 0)
注意 initarg
的优先级比 initform
要高。
我们可以指定某些槽是共享的 ── 也就是每个产生出来的实例,共享槽的值都会是一样的。我们通过声明槽拥有 :allocation :class
来办到此事。(另一个办法是让一个槽有 :allocation :instance
,但由于这是缺省设置,不需要特别再声明一次。)当我们在一个实例中,改变了共享槽的值,则其它实例共享槽也会获得相同的值。所以我们会想要使用共享槽来保存所有实例都有的相同属性。
举例来说,假设我们想要模拟一群成人小报 (a flock of tabloids)的行为。(译注:可以看看什么是 tabloids。)在我们的模拟中,我们想要能够表示一个事实,也就是当一家小报采用一个头条时,其它小报也会跟进的这个行为。我们可以通过让所有的实例共享一个槽来实现。若 tabloid
类别像下面这样定义,
(defclass tabloid ()
((top-story :accessor tabloid-story
:allocation :class)))
那么如果我们创立两家小报,无论一家的头条是什么,另一家的头条也会是一样的:
> (setf daily-blab (make-instance 'tabloid)
unsolicited-mail (make-instance 'tabloid))
#<TABLOID #x302000EFE5BD>
> (setf (tabloid-story daily-blab) 'adultery-of-senator)
ADULTERY-OF-SENATOR
> (tabloid-story unsolicited-mail)
ADULTERY-OF-SENATOR
译注: ADULTERY-OF-SENATOR 参议员的性丑闻。
若有给入 :documentation
属性的话,用来作为 slot
的文档字符串。通过指定一个 :type
,你保证一个槽里只会有这种类型的元素。类型声明会在 13.3 节讲解。