・というところで、classに対するデコレータ。
・基本的には関数に対するそれと同じで、
@XXXXなんていうのをclassの定義の前に付けてあると、起動時にXXXX()という関数が自動的に呼ばれてこの関数が戻したclassによって元のclassが上書きされたようになる(すり替えが行われる)ということ。
・例によってGeminiくんが生成したサンプルにあれこれ手を加えてみたのがこれ。
----------
// 元のクラスを受け取って、「改造版の新しいクラス」を返すデコレータ
let log_instantiation = fn (OriginalClass) {
println("log_instantiation")
// 元のクラスを包み込む(ラップする)新しいクラスを定義する
class WrappedClass {
new(x, y) {
println("Wrapped Class x={x}, y={y}")
// 元クラス(OriginalClass)からオブジェクト(インスタンス)生成
self.inner = OriginalClass.new(x+1,y+1)
}
get_x() {
println("代替元のxを読み出します")
return self.inner.x
}
}
// 代替クラスを返す
return WrappedClass
}
@log_instantiation
class Point {
new(x, y) {
println("Point x={x},y={y}")
self.x = x
self.y = y
}
}
println("Start")
let p = Point(10, 20)
println(p.get_x())
-----
実行結果:
log_instantiation // ユーザコード実行前にデコレータが呼ばれる
Start // ここからユーザコード
Wrapped Class x=10, y=20 // 代替したWrappedClassのコンストラクタ
Point x=11,y=21 // WrappedClassからPointインスタンス(オブジェクト)生成
代替元のxを読み出します
11
----------
・というところで、functionのデコレータと大差はない。ただ、公式ドキュメント側の方ではこんな感じ。
----------
let mark = fn (cls) {
cls.marked = true
cls
}
@mark
class Point {
new(x, y) {
self.x = x
self.y = y
}
}
j = Point(4,5)
println("{j.class}")
println("{j.class.marked}")
println("{(j.class).marked}")
println("{Point.marked}") # true
-----
実行結果:
true
----------・こちらのサンプルのcls.markedって何だ?っていう話になる。
引数として渡されたのはPointクラス。これにmarkedとやらを何やら付加している。これはclassのメンバの追加?それをprintlnで・・・と思ったけど、これは単にmark関数が返したcls・・・これは代替元クラスそのもので、インタンス化されていないので、メンバなんてアクセスできるわけもない。
・ということで、しばし悩んだけど例によってGeminiくんとあれこれやってやっと納得したのは、これは金型の刻印みたいなものということ。
・鯛焼きを作る金型みたいなものを想像する。この金型がclassで金型を使ってパッコン!と作られる鯛焼きがインスタンスやらオブジェクトやらと言われるもの。
・で、このサンプルの.markedとか言って追加しているものは、この金型の外側につけられた金型メーカーの刻印みたいなものということ。
・なので、Point.markedはこの刻印を読んでいる。
・あくまでも”金型の外側の刻印”なので、このクラス(mark()が返したクラス:cls)から生成されたオブジェクトには.markedなんてメンバはない。
・この刻印のことはGeminiによるとメタデータと呼ぶらしい。
・ちょっと不思議なのは
j = Point(2,3)
println("{j.class}")
println("{j.class.marked}")
とすると、j.classは"Point"だと言うけど、”j.class.marked"はnilになってしまうこと。
・さて、これはどう解釈すると良いのかな。とりあえずの対処としては
class Point {
new(x, y) {
self.myclass = Point // 明示的にクラスオブジェクトを保持しておく
・・・・・・
なんて具合にクラスそのものをバックアップしておいて
j = Point(4, 5)
println("{j.myclass.marked}") // これなら true が取れる!
とすればtrueとなる。でも美しくないな。これはいずれ変更・修正されるのかな?