Lisp 如何将超类对象传递给子类构造函数?
假设我有一个带有两个插槽的类Lisp 如何将超类对象传递给子类构造函数?,lisp,common-lisp,clos,Lisp,Common Lisp,Clos,假设我有一个带有两个插槽的类A: (defclass a () ((a-1 :initarg :a-1) (a-2 :initarg :a-2))) 以及继承自A的类B: (defclass b (a) ((b-1 :initarg :b-1))) 如果我想实例化B,生成实例将为我提供插槽:a-1,:a-2和:B-1 这里有一个疯狂的想法:如果我想使用a的现有实例实例化B,并且只填充槽B-1,该怎么办 注:为什么有用:如果A实现了一些B直接继承的通用方法,而不添加任何新的内容。
A
:
(defclass a ()
((a-1 :initarg :a-1)
(a-2 :initarg :a-2)))
以及继承自A
的类B
:
(defclass b (a)
((b-1 :initarg :b-1)))
如果我想实例化B
,生成实例
将为我提供插槽:a-1
,:a-2
和:B-1
这里有一个疯狂的想法:如果我想使用a
的现有实例实例化B
,并且只填充槽B-1
,该怎么办
注:为什么有用:如果A
实现了一些B
直接继承的通用方法,而不添加任何新的内容。在另一种方法中,将A
的实例作为B
中的一个插槽,我需要编写简单的方法包装器来在该插槽上调用这些方法
我能想到的唯一方法是:在辅助构造函数中分解对象A
,并将相应的槽传递给为B
生成实例,即:
(defun make-b (b-1 a-obj)
(with-slots (a-1 a-2) a-obj
(make-instance 'b :b-1 b-1 :a-1 a-1 :a-2 a-2)))
有更好的方法吗?(或者,这种方法会导致非常糟糕的设计,我应该完全避免吗?我不认为有一个通用的解决方案。考虑:例如,如果类A
有一些插槽,这些插槽不是简单地从一些:initarg
初始化的,而是在初始化实例
或共享初始化
期间初始化的,那么应该发生什么
也就是说,只要你控制所有相关的类,你就可以尝试
- 制定一个由
a
实现的协议,类似于
(defgeneric initargs-for-copy (object)
(:method-combination append)
(:method append (object) nil))
(defmethod initargs-for-copy append ((object a))
(list :a-1 (slot-value object 'a-1) :a-2 (slot-value object 'a-2)))
(defun make-b (b-1 a-obj)
(apply #'make-instance 'b :b-1 b-1 (initargs-for-copy a-obj)))
- 在运行时使用MOP提取插槽(这可能需要了解您选择的Lisp实现,或者需要一些库的帮助,如
closer MOP
可通过获取)
- 用于将
A
实例转换为B
实例
破坏性的
不管怎样:我不确定您的用例是否真的保证了继承。这里的合成方法(从设计的角度)似乎更清晰。除了让B
通过A
继承一些通用方法实现之外:B
的实例在您的实际应用中是否真的被认为是A
的合适实例(即,是否存在is-A?
关系)?或者您只是想避免在这里提供包装器?您可以使用组合作为原型继承的一种形式,其中对象从另一个实例“继承”
(defclass prototype-mixin ()
((parent :initarg :parent :initform nil :accessor parent)))
(defmethod slot-unbound (c (p prototype-mixin) slot)
(declare (ignore c))
(let ((parent (parent p)))
(if parent
(slot-value parent slot)
(call-next-method))))
现在,您定义了两个类:
(defclass a ()
((slot :initarg :slot)))
(defclass b (a prototype-mixin)
((other :initarg :other)))
当您从a
的现有实例创建b
时,您将b
的parent
槽设置为a
。由于b
也是a
,因此b
中有一个未绑定的插槽。当您尝试访问此插槽时,您将访问“父”对象中存在的插槽,它是a
的一个实例。但如果需要,可以覆盖b
中的值
这种方法的灵感来自comp.lang.lisp上的Erik Naggum。您还可以使用A的实例,将其类更改为B,然后初始化添加的插槽。请注意,您确实需要摆脱“A实现方法”的想法。这是CLOS,类/槽和泛型函数/方法在这里更加独立。@RainerJoswig更改类的想法很有趣。但我不确定我是否完全理解类/槽和泛型函数/方法之间的关系。我意识到它们是相当独立的实体。“实现方法”思维的替代方法是什么?@mobiuseng在clos中,并不是“实现方法f”,而是“在通用函数f上定义了一个方法,专门用于A”。当你在多个参数上专门化方法时,这是一个更明显的区别。Joshua是对的。加上:“在泛型函数f上定义了一些方法,专门用于a”。它可能不止一个。@RainerJoswig好的,我明白了。只是在这个特殊情况下,我专门化了一个参数,所以看起来像是“实现方法”“.CLOS的方式转移了很多注意力。谢谢你的回答!我没有想到非平凡的初始化。。。我试图确定构建块之间的关系,以便正确地组合它们。于是,我想到了这个想法。到目前为止,组合似乎比继承更有效。
(defclass a ()
((slot :initarg :slot)))
(defclass b (a prototype-mixin)
((other :initarg :other)))