メタブルとは
具体的には、数値の加算、減算、乗算、除算、文字列の連結、関数の呼び出し、テーブル・エントリへの値の代入などです。 これらは全てデフォルトのロジックに従います。 たとえば、2 つのテーブルを加算する方法を定義できます。
最も簡単な例では、2つのテーブルの加算を再定義しています。 この例では、cの__addフィールドを書き換え、aのMetatableをcに設定しています。加算操作が実行されると、LuaはまずaにMetatableがあるかどうか、そしてそのMetatableに__addフィールドが存在するかどうかをチェックし、存在する場合はそれを呼び出し、そうでない場合はbの条件をチェックし、どちらも存在しない場合はデフォルトの加算操作を呼び出し、テーブルに__addフィールドが存在しない場合はそれを呼び出します。どちらも存在しない場合、デフォルトの加算演算が呼び出され、テーブルがデフォルトの加算演算を定義していない場合、エラーがスローされます。
--つのテーブルを定義する&n bsp;
a = {5, 6} b = {7, 8}
--cをメタテーブルとして使う
c = {}
--加算の再定義
c.__a dd = function(op1, op2)
for _, item in ipairs(op2) do
table.insert(op1, item) 終了
return op1 終了
--aのMetatableをcに設定する。
setmetatable(a, c)
--d現在はこのようになっている。{5,6,7,8} d = a + b
何が起こっているのかがわかったら、Metatableの具体的なプロパティを見てみましょう。
Metatableは不思議なものではなく、ただの古いテーブルです。Luaはテーブルのデータ構造内でこれらの操作を再定義するためのエントリーポイントをいくつか定義しています。 これらはすべて、テーブルのフィールドを表すダブルアンダースコア(上の例では__add)で始まります。 ある値に対してMetatableを設定し、Metatable内の対応する操作フィールドの書き換えを設定すると、その値が操作を実行したときに書き換えられたカスタム操作がトリガーされます。 例えば、__add は、プラス記号の両側にある 2 つのオペランドをパラメータとして渡し、戻り値を要求します。 この振る舞いをイベントに例える人もいます。xxの振る舞いがトリガーされると、イベントカスタムのアクションがトリガーされます。
Luaではどのような値もメタテーブルを持ち、異なる値は異なるメタテーブルを持つことも、同じメタテーブルを共有することもできますが、Lua自体が提供する機能では、C拡張や他のライブラリを使用しない限り、テーブル型以外の値のメタテーブルを変更することはできません。 setmetatableとgetmetatableは、テーブル型のMetatableを操作するためのメソッド群です。
メタテーブルとオブジェクト指向
Luaは手続き型言語ですが、メタテーブルを使ってオブジェクト指向をシミュレートすることができます。 その鍵となるのが__indexフィールドです。 これはテーブルのインデックス値へのアクセスを提供します。 テーブルがtable[key]のような値でインデックスされている場合、Luaはまずテーブル自体でkeyの値を調べ、もしそれがなく、__index属性を持つメタテーブルがあれば、__indexで定義された関数のロジックに従ってそれを調べます。 考えてみれば、これはオブジェクト指向の核心である継承を実装する方法です。 Luaでオブジェクト指向を実装する方法はたくさんありますが、__indexを使う方法はありません。
この例では、ProgrammingInLuaのOO実装を使って、飛べるというプロパティを持ち、他の鳥オブジェクトのベースとなるBirdオブジェクトと、鳥の一種であるが飛べないOstrichオブジェクトを作成しました。 結果は明らかです:BirdとOstrichは別々の状態を持っています。
local Bird = {CanFly = true}
function Bird:New()
local b = {}
setmetatable(b, self)
selfself.__index = self
return b
終了
local ダチョウ = Bird:New() --Bird.CanFly is true, Ostrich.CanFly is true
Ostrich.CanFly = false --Bird.CanFly is true, Ostrich.CanFly is false
__indexとは対照的に、__newindexはテーブルのキーが更新された時に発生します。 これらのイベントは、テーブルのキーにrawsetとrawgetを使用することでスキップすることができます。
通話と傍受
JavaやC#では動的プロキシやAOPを実装するのに大変な手間がかかりますが、Luaではこのようなことはとても簡単で、限定的ではありますが、Luaの柔軟性を感じることができます。 これが__call操作で、値が呼び出された時に発動します。
a = {}
function a:Func()
print("simonw")
終了
c = {}
c.__call = function(t, )
print("Start")
t.Func()
print("End") end
setmetatable(a, c)
a()
--[[
Start
simonw
End ]]





