7

How would you express the following Java code in Lisp?

class Foo {
    private String s;

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

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

In Lisp, is make-instance or initialize-instance the equivalent of a constructor? If yes, how do you call the super class constructor(s)?

4

2 回答 2

15

在一个方法中使用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时没有更改它分派的参数。我只是更改了&keyinitarg 参数。

于 2013-04-18T20:02:00.413 回答
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方法设置为传递lefttop的值,但不传递widthheight

(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

构造函数不会阻止用户设置widthheight的尝试,就像对一些不存在的插槽所做的那样:

(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]

但是这个也演示了height的默认设置,它经历了:

(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之后调整宽度或高度。我不知道如何在子类的实例中而不是在超类的实例中使宽度和高度只读。

于 2016-02-14T02:55:47.127 回答