我有以下两个类:
(defclass person () ())
(defmethod speak ((s person) string)
(format t "-A" string))
(defmethod speak :before ((s person) string)
(print "Hello! "))
(defmethod speak :after ((s person) string)
(print "Have a nice day!"))
(defclass speaker (person) ())
(defmethod speak ((i speaker) string)
(print "Bonjour!"))
(speak (make-instance 'speaker) "Can I help yoU?")
这个的输出是:
"Hello! "
"Bonjour!"
"Have a nice day!"
我想弄清楚的是这些方法是如何按照“顺序”执行的。我似乎无法掌握正在发生的事情和原因。据说有一个规则优先于此,但我不知道在哪里找到它。例如,为什么"Hello!Can I help you"
在这种情况下不会发射?
答案 0 :(得分:9)
当你没有任何方法时,方法应用的顺序是:从最具体到最不具体的方法之前的所有方法,然后是最具体的主要方法,然后是来自之后的方法最具体到最具体的。在您的情况下,您有两个主要方法(方法没有:名称旁边的或之前):一个指定人,另一个指定扬声器。由于说话者比人更具体,因此只调用说话者主要方法。如果您想调用多个主要方法,请查看call-next-method。
答案 1 :(得分:3)
虽然我发现已经有一个已经接受的答案,但Common Lisp在HyperSpec中有一些非常好的文档,知道在哪里可以找到所发生的事情的完整描述是有用的。在这种情况下,它是 7.6.6.2 Standard Method Combination ,它说(缩写):
标准方法组合的语义如下:
如果有任何around方法,则调用最具体的around方法。它提供通用函数的值。
在around方法的主体内部,call-next-method可用于调用下一个方法。当下一个方法返回时,around方法 可以执行更多代码,可能基于返回的值或值。 如果call-next-method是,则调用泛型函数no-next-method 使用,没有适用的方法来调用。功能 next-method-p可用于确定是否存在下一个方法。
如果around方法调用call-next-method,则调用下一个最具体的around方法(如果适用)。如果周围没有 方法或者如果call-next-method被最不具体的方法调用 方法,其他方法调用如下:
以最具体的第一顺序调用所有之前的方法。他们的价值被忽略了。如果是,则发出错误信号 call-next-method用于before方法。
调用最具体的主要方法。在主要方法的主体内部,可以使用call-next-method来调用下一个方法 具体的主要方法。当该方法返回时,前一个 主方法可以执行更多代码,也许基于返回 价值或价值。如果是,则调用泛型函数no-next-method 使用call-next-method并且没有更多适用的主要方法 方法。函数next-method-p可用于确定是否a 存在下一种方法。如果不使用call-next-method,则只使用最多 调用特定的主要方法。
所有after方法都以最具体的最后一个顺序调用。他们的价值被忽略了。如果call-next-method为,则发出错误信号 用于后方法。
如果没有调用方法,则最具体的主方法提供泛型函数返回的值。该 调用call-next-method返回的值或值 最具体的方法是最具体的返回方法 主要方法。
在该页面的末尾有一个特别有用的插图,描述了行为及其动机:
之前的方法以最特定的第一顺序运行 方法以最少特定的第一顺序运行之后。该设计 可以用一个例子来说明这种差异的基本原理。 假设类C1修改其超类C2的行为 在方法之前和之后添加方法。是否行为 C2类直接由C2上的方法定义,或者从C2继承 超类不会影响调用的相对顺序 C1类实例的方法。方法运行之前的C1类 在所有C2类方法之前。 C1之后的方法运行 所有C2类的方法。
相比之下,所有周围的方法都在任何其他方法运行之前运行。从而 一个不太具体的around方法在更具体的主要方法之前运行 方法
如果仅使用主要方法且未使用call-next-method, 只调用最具体的方法;也就是说,更具体 方法影响更普遍的。
答案 2 :(得分:1)
除了其他答案,请注意您可以使用以下宏定义自定义方法组合:
DEFINE-METHOD-COMBINATION
。已经有10 existing method combinators,所以我认为定义自定义的不常见。当然,能够这样做有时非常有用(参见Joshua Taylor的评论)。
此外,调用方法的方式取决于类继承,默认情况下会考虑父子关系,以及超类之间的顺序。请阅读"Fundamentals of CLOS"。可以使用元对象协议更改类优先级列表:请参阅COMPUTE-CLASS-PRECEDENCE-LIST
。