CLOS中构造函数的等价物是什么?

时间:2013-04-18 17:47:04

标签: lisp common-lisp clos

您如何在Lisp中表达以下Java代码?

class Foo {
    private String s;

    public Foo(String s) {
        this.s = s;
    }
}

class Bar extends Foo {
    public Bar(int i) {
        super(Integer.toString(i));
    }
}

在Lisp中,make-instanceinitialize-instance是构造函数的等价物吗?如果是,你如何调用超类构造函数?

2 个答案:

答案 0 :(得分:14)

在方法中使用CALL-NEXT-METHOD来调用下一个。

通常只需使用标准方法组合工具,并为:BEFORE编写:AFTER:AROUNDINITIALIZE-INSTANCE方法。

一种方式:

(defclass foo ()
  ((s :type string :initarg :s)))

(defclass bar (foo) ())

(defmethod initialize-instance :around ((b bar) &key i)
  (call-next-method b :s (princ-to-string i)))

示例:

CL-USER 17 > (make-instance 'bar :i 10)
#<BAR 402000B95B>

CL-USER 18 > (describe *)

#<BAR 4130439703> is a BAR
S      "10"

请注意,我已调用CALL-NEXT-METHOD而不更改其调度的参数。我刚刚更改了&key initarg参数。

答案 1 :(得分:3)

这是Rainier的一个例子,它增加了一个小小的变化: 通过约束槽值来进行特化(子类化),至少在 施工时间。考虑用于二维矩形的类 m-box

(defclass m-box ()
  ((left   :accessor m-box-left   :initform 0 :type integer :initarg :left  )
   (top    :accessor m-box-top    :initform 0 :type integer :initarg :top   )
   (width  :accessor m-box-width  :initform 0 :type integer :initarg :width )
   (height :accessor m-box-height :initform 0 :type integer :initarg :height)))

我们可以这样试试:

(describe (make-instance 'm-box :left 42 :top -39 :width 5 :height  11))

: #<M-BOX {10047A8F83}>
:   [standard-object]
: 
: Slots with :INSTANCE allocation:
:   LEFT    = 42
:   TOP     = -39
:   WIDTH   = 5
:   HEIGHT  = 11

现在考虑一个子类或专门化:让 m-block 成为一个m-box 单位宽度和高度。我们将 initialize-instance 方法设置为通过 顶部的值,但不是 width height

(defclass m-block (m-box) ())
(defmethod initialize-instance
    :around
    ((mb m-block)
     &key (left 0) (top 0))
  (call-next-method mb :left left :top top :width 1 :height 1))

我们可以按如下方式创建 m-block 的实例:

(describe (make-instance 'm-block :left 17 :top -34 :width 5 :height  11))

: #<M-BLOCK {10049C0CC3}>
:   [standard-object]
: 
: Slots with :INSTANCE allocation:
:   LEFT    = 17
:   TOP     = -34
:   WIDTH   = 1
:   HEIGHT  = 1

构造函数不阻止用户尝试设置 width height , 就像它对一些不存在的插槽一样:

(describe (make-instance 'm-block :left 17 :top -34 :plugh 2345))

Invalid initialization argument:
  :PLUGH
in call for class #<STANDARD-CLASS COMMON-LISP-USER::M-BLOCK>.
   [Condition of type SB-PCL::INITARG-ERROR]

但构造函数 用1来纠正用户的无效输入。

您可能希望通过调用error来使模型更加无懈可击 用户尝试输入无效的宽度高度

(defclass m-block (m-box) ())
(defmethod initialize-instance
  :around
  ((mb m-block)
   &key (left 0) (top 0) (width 1) (height 1))
  (when (or (/= 1 width) (/= 1 height))
    (error "an m-block must have unit width and height"))
  (call-next-method mb :left left :top top :width 1 :height 1))

拒绝用户的以下尝试:

(describe (make-instance 'm-block :left 17 :top -34 :width 5 :height  11))

an m-block must have unit width and height
   [Condition of type SIMPLE-ERROR]

但是,这个也证明了身高的违约行为,通过了:

(describe (make-instance 'm-block :left 17 :top -34 :width 1))

: #<M-BLOCK {1005377983}>
:   [standard-object]
: 
: Slots with :INSTANCE allocation:
:   LEFT    = 17
:   TOP     = -34
:   WIDTH   = 1
:   HEIGHT  = 1

此示例允许用户之后setf宽度或高度。我不 知道如何在子类的实例中创建宽度和高度只读 而不是超类的实例。