如何使用 metaclass 使 class 的实例继承自特定的 superclass

How to make the instances of a class using a metaclass inherit from a specific superclass

我正在尝试为普通 lisp 实现 json 序列化 API。为此,我定义了一个名为 json-class 的元 class。这个 metaclass 定义了插槽选项 :ignore 用于忽略对象的特定插槽。由于我在序列化过程中使用 yason,我想将方法​​ yason:encode-slots 专门用于 classes 中使用元 class json-class 的每个对象。我能想到的实现此目的的唯一方法是使所有对象从 json-object 类型的 json-class 实例化,可用于专门化方法。 我试图复制的行为与 MOP 已经实现的行为相同,包括每个 class 使用 standard-class 生成类型为 standard-object.[=19= 的对象]

使用Closer to Mop:

(defpackage :so (:use :closer-common-lisp))
(in-package :so)

;; Define a metaclass named json-class.
;; It inherits from a standard-class
(defclass json-class (standard-class) ())

专精 VALIDATE-SUPERCLASS (see also VALIDATE-SUPERCLASS explained).

(defmethod validate-superclass ((j json-class) (s standard-class)) t)

然后定义基数json-object

(defclass json-object () () (:metaclass json-class))

例如:

(class-of (make-instance 'json-object))
#<JSON-CLASS SO::JSON-OBJECT>

返回的对象是 json-objectjson-class 的实例,standard-class 的子类。

如果你查看 SBCL 的源代码,你会发现它在 shared-initialize:[=24 的直接超级class 列表中添加了 standard-object =]

         (setq direct-superclasses
               (or direct-superclasses
                   (list (if (funcallable-standard-class-p class)
                             *the-class-funcallable-standard-object*
                             *the-class-standard-object*))))

如果您希望将 class 隐式添加到直接 superclasses 列表中,最好在 shared-initialize 上的 :around 方法中执行此操作或 initialize-instance.

另一种选择是将 compute-class-precedence-list 特化为,例如:

(cons my-superclass (call-next-method))

这就是 An existing JSON MOP library 所做的。

编辑:

我认为乱用 compute-class-precedence-list 来添加 superclasses 是未定义的行为,尽管它通常有效。我认为最好的解决方案是为 shared-initializeinitialize-instance 编写一个 :around 方法,首先检查(使用 subclassp)您的 class 是否已经在继承链,没有则添加。