defclass的第三个参数必须是一个槽定义的列表。最简单的槽定义,就像前一篇文章所看到的,就是一个标识名字的符号(symbol)。通常情况下,一个槽可以定义为一个列表,这个列表是由名字,和它所带有的一个或多个属性组成。属性以关键字参数的形式给出。
通过定义一个槽的 :accessor ,我们就隐式地创建了一个引用那个槽的函数,这样就无需调用slot-value了。如果我们把circle类的定义更新成如下:
[1]>
(defclass circle ()
((radius :accessor circle-radius)
(center :accessor circle-center)))
#<STANDARD-CLASS CIRCLE>
然后我们就可以通过circle-radius和circle-center来引用槽了:
[2]>
(setf c (make-instance 'circle))
#<CIRCLE #x00033540CE78>
[3]> (setf (circle-radius c) 1)
1
[4]> (circle-radius c)
1
当然我们也还可以通过通用的方式来引用:
[7]> (setf (slot-value c 'radius) 100)
100
[8]> (circle-radius c)
100
通过指定一个: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))))
#<STANDARD-CLASS CIRCLE>
[2]> (setf c (make-instance 'circle :radius 3))
#<CIRCLE #x00033E435198>
[3]> (circle-radius c)
3
[4]> (circle-center c)
(0 . 0)
注意,:initarg的优先级要高于:initform。
我们也可以指定某些槽是被共享的(静态变量-译者注)——也就是说,它们的值对于每个实例都是一样的。我们通过给这个槽指定:allocation :class来达到这个目的。(另一个可选的项是:allocation :instance,这是默认值,也就没有必要显式地写出来了)当我们在一个实例中改变这个槽的值的时候,其它每个实例也会得到改变后的相同的值。所以,我们通过使用共享槽来存储所有的实例公用的属性。
比如,假设我们想要模拟一群小报的行为。在我们的模拟中,我们希望能够模拟这样的事实:当一个小报获得一个主题后,它们都会获得。我们可以通过让所有的实例共享一个槽来达成目的。如果tabloid类被定义成如下:
[5]> (defclass tabloid ()
((top-story :accessor tabloid-story
:allocation :class)))
#<STANDARD-CLASS TABLOID>
然后,我们创建tabloid的两个实例,不论什么变成了其中一个的首页新闻,也会变成其它的首页新闻:
[7]> (setf (tabloid-story daily-blab) 'adult-of-senator)
ADULT-OF-SENATOR
[8]> (tabloid-story unsolicited-mail)
ADULT-OF-SENATOR
如果:document属性被给出的话,它的值应该是作为描述这个槽的文档字符串。通过指定:type,可以保证那个槽只包含那种类型的元素。