我有两个包,每个包都定义了一个类。第二个类继承自第一个类,但有一个同名的插槽。意图确实是要覆盖这个插槽。
(defpackage :foo
(:use :cl)
(:export foo))
(in-package :foo)
(defclass foo () ((s)))
(defpackage :bar
(:use :cl :foo)
(:export bar))
(in-package :bar)
(defclass bar (foo) ((s)))
当我创建bar
的实例时,sbcl会发出有用的警告
(make-instance 'bar)
STYLE-WARNING:
slot names with the same SYMBOL-NAME but different SYMBOL-PACKAGE (possible
package problem) for class #<STANDARD-CLASS BAR:BAR>:
(FOO::S BAR::S)
由于这是预期的行为,我可以像这样抑制警告:
(handler-bind (#+SBCL (style-warning #'muffle-warning))
(make-instance 'bar))
但是我希望bar
类的用户能够在不收到警告的情况下创建实例。
我可以编写一个包含上一个代码块中代码的包装函数,但是可以在调用(make-instance 'bar)
之前抑制警告,而不会消除所有样式警告吗?
答案 0 :(得分:4)
Common Lisp中的符号通常属于一个包。当Lisp阅读器遇到新符号时,它将把它放在当前包中(由IN-PACKAGE
设置),除非从其他地方导入了具有相同名称的符号。通过在包和符号名称之间放置一个冒号(或内部符号的两个冒号),可以使用包名写入符号。
如果我们在您的代码中添加包前缀,就像Lisp读者看到它们一样,错误就变得容易看到:
(cl:defpackage keyword:foo
(keyword:use keyword:cl)
(keyword:export cl-user::foo)) ;Makes FOO:FOO an external symbol, which will be
; imported with the (:USE ... :FOO) below.
(cl:in-package keyword:foo)
(cl:defclass foo:foo () ((foo::s))) ;FOO::S is not exported, so it
; is an internal symbol. It will
; not be imported by the :USE-clause.
(cl:defpackage keyword:bar
(keyword:use keyword:cl keyword:foo) ;Import all external symbols from CL and FOO.
(keyword:export cl-user::bar))
(cl:in-package keyword:bar)
(cl:defclass bar:bar (foo:foo) ;FOO:FOO was imported from the other package.
((bar::s))) ;FOO::S (an internal symbol) wasn't imported, so a new
; symbol was interned in BAR.
班级BAR:BAR
实际上有两个广告位FOO::S
和BAR::S
。插槽名称具有相同的SYMBOL-NAME
,但具有不同的SYMBOL-PACKAGE
。这很少是程序员的意图,因此SBCL会对此发出警告。要解决此问题,您应导出FOO::S
,以便在包S
中编写不合格的BAR
将引用相同的符号。或者,您可以使用包限定形式编写插槽名称,但通常不建议使用其他包的内部符号。
答案 1 :(得分:2)
这似乎警告在这里很有用,但是如果你发现自己处于一种必须扼杀它的情况,那么适当的地方就是在课程定稿时:
#+sbcl
(defmethod sb-mop:finalize-inheritance :around ((c (eql (find-class 'bar))))
(handler-bind ((sb-int:simple-style-warning #'muffle-warning))
(call-next-method)))
创建新实例时出错,但是当我测试代码时,在REPL中定义类时会发出错误信号。这是一个线索,即在类完成期间发出错误信号,必须在defclass
之后(但不一定紧接在之后)和分配第一个实例之前完成。见Class finalization protocol:
调用finalize-inheritance的确切位置取决于类metaobject的类;对于标准类,在定义了类的所有超类之后的某个时间调用它,但不迟于分配类的第一个实例时(通过allocate-instance)。