実践Common lispを読み始めた-第16章 オブジェクト指向入門:総称関数(2)

UMPC欲しい。

標準メソッド結合

call-next-methodによってどのメソッドが順番に呼ばれるかはわかったが、lispには補助メソッドというのがあって、:before :after :aroundという修飾子を設定できる。色々出来るんだろうけど、何がうれしいのかよくわからない。単に複雑な印象。
前の日記に書いたコードに以下のコードを足してみて呼び出し順がどうかわるのか調べてみよう。

;;
;; foo.lispに追加
;;
(defmethod foo :before ((x B) (y B))
  (format t "Before B-B~%"))

(defmethod foo :before ((x D) (y D))
  (format t "Before D-D~%"))

(defmethod foo :after ((x A) (y A))
  (format t "After A-A~%"))

(defmethod foo :after ((x B) (y B))
  (format t "After B-B~%"))

CL-USER> (load "foo.lisp")
; Loading foo.lisp
A-A
After A-A
--
A-B
A-A
After A-A
--
aA1-aC2
aA1-C
A-aC2
A-C
A-A
After A-A
--
B-A
A-A
After A-A
--
Before B-B
B-B
B-A
A-B
A-A
After A-A
After B-B
--
B-C
B-A
A-aC2
A-C
A-A
After A-A
--
C-A
A-A
After A-A
--
C-B
C-A
A-B
A-A
After A-A
--
C-C
C-A
A-aC2
A-C
A-A
After A-A
--
Before D-D
Before B-B
D-D
B-B
B-C
B-A
C-B
C-C
C-A
A-B
A-C
A-A
After A-A
After B-B
T
CL-USER>  
  • beforeメソッドが定義されていると標準メソッドよりも先に呼び出される。beforeメソッド同士だとより具象クラスに近いものから順に呼ばれる。
  • afterメソッドが定義されているとbeforeメソッド、標準メソッドが呼び出された後で必ず呼び出される。afterメソッド同士ではより基底クラスに近いものから順に呼ばれる。
  • before, afterメソッドは必ず呼び出される。call-next-methodを明示的に呼んでなくてもOK。特にafterメソッドの場合、標準メソッドでcall-next-methodを呼び出さなくても必ず呼ばれる。
;;
;; foo.lispに追加
;;
(defmethod foo :around ((x B) (y B))
  (format t "Around B-B~%"))

CL-USER> (load "foo.lisp")
; Loading foo.lisp
A-A
After A-A
--
A-B
A-A
After A-A
--
aA1-aC2
aA1-C
A-aC2
A-C
A-A
After A-A
--
B-A
A-A
After A-A
--
around B-B
--
B-C
B-A
A-aC2
A-C
A-A
After A-A
--
C-A
A-A
After A-A
--
C-B
C-A
A-B
A-A
After A-A
--
C-C
C-A
A-aC2
A-C
A-A
After A-A
--
around B-B
T
CL-USER> 

-aroundメソッドで指定した引数の型に一致する場合には、何をおいても最初にaroundメソッドが実行される。
-before, afterメソッドのようにcall-next-methodを自動的に呼び出すことはしないようだ。x, yの型がBまたはそのサブクラスだとaroundメソッドだけ呼ばれている。
では、aroundメソッドのBodyでcall-next-methodを呼び出すとどうなるか。
>|lisp|

;;
;; さらにfoo.lispに追加
;;

(defmethod foo :around ((x B) (y B))
  (format t "Around B-B~%")
 (call-next-method))

(defmethod foo :around ((x D) (y D))
  (format t "Around D-D~%")
  (call-next-method))

CL-USER> (load "foo.lisp")
; Loading foo.lisp
A-A
After A-A
--
A-B
A-A
After A-A
--
aA1-aC2
aA1-C
A-aC2
A-C
A-A
After A-A
--
B-A
A-A
After A-A
--
around B-B
Before B-B
B-B
B-A
A-B
A-A
After A-A
After B-B
--
B-C
B-A
A-aC2
A-C
A-A
After A-A
--
C-A
A-A
After A-A
--
C-B
C-A
A-B
A-A
After A-A
--
C-C
C-A
A-aC2
A-C
A-A
After A-A
--
around D-D
around B-B
Before D-D
Before B-B
D-D
B-B
B-C
B-A
C-B
C-C
C-A
A-B
A-C
A-A
After A-A
After B-B
T
CL-USER> 
  • call-next-methodの呼び出し方はaround->before->標準メソッド->afterの順番になる。どの引数から先に呼ばれるかは標準メソッドの呼び方のルールと同じ。beforeメソッドからcall-next-method呼び出しを引いたものという扱いと思ってよいのかな。

その他のメソッド結合

まだあるのか。