11.5 优先级 (Precedence)
我们已经看过类别是怎样能有多个基类了。当一个实例的方法同时属于这个实例所属的几个类时,Lisp 需要某种方式来决定要使用哪个方法。优先级的重点在于确保这一切是以一种直观的方式发生的。
每一个类别,都有一个优先级列表:一个将自身及自身的基类从最具体到最不具体所排序的列表。在目前看过的例子中,优先级还不是需要讨论的议题,但在更大的程序里,它会是一个需要考虑的议题。
以下是一个更复杂的类别层级:
(defclass sculpture () (height width depth))
(defclass statue (sclpture) (subject))
(defclass metalwork () (metal-type))
(defclass casting (metalwork) ())
(defclass cast-statue (statue casting) ())
图 11.3 包含了一个表示 cast-statue
类别及其基类的网络。
图 11.3: 类别层级
要替一个类别建构一个这样的网络,从最底层用一个节点表示该类别开始。接着替类别最近的基类画上节点,其顺序根据 defclass
调用里的顺序由左至右画,再来给每个节点重复这个过程,直到你抵达一个类别,这个类别最近的基类是 standard-object
── 即传给 defclass
的第二个参数为 ()
的类别。最后从这些类别往上建立链接,到表示 standard-object
节点为止,接着往上加一个表示类别 t
的节点与一个链接。结果会是一个网络,最顶与最下层各为一个点,如图 11.3 所示。
一个类别的优先级列表可以通过如下步骤,遍历对应的网络计算出来:
- 从网络的底部开始。
- 往上走,遇到未探索的分支永远选最左边。
- 如果你将进入一个节点,你发现此节点右边也有一条路同样进入该节点时,则从该节点退后,重走刚刚的老路,直到回到一个节点,这个节点上有尚未探索的路径。接着返回步骤 2。
- 当你抵达表示
t
的节点时,遍历就结束了。你第一次进入每个节点的顺序就决定了节点在优先级列表的顺序。
这个定义的结果之一(实际上讲的是规则 3)在优先级列表里,类别不会在其子类别出现前出现。
图 11.3 的箭头演示了一个网络是如何遍历的。由这个图所决定出的优先级列表为: cast-statue
, statue
, sculpture
, casting
, metalwork
, standard-object
, t
。有时候会用 specific 这个词,作为在一个给定的优先级列表中来引用类别的位置的速记法。优先级列表从最高优先级排序至最低优先级。
优先级的主要目的是,当一个通用函数 (generic function)被调用时,决定要用哪个方法。这个过程在下一节讲述。另一个优先级重要的地方是,当一个槽从多个基类继承时。408 页的备注解释了当这情况发生时的应用规则。 λ