Luaにおける元表(metatable)は、テーブルの振る舞いをカスタマイズするための仕組みです。通常のテーブルでは定義されていない演算やアクセス方法を、元表を通じて再定義することで、より柔軟なデータ構造を実現できます。
元表の基本設定
元表はsetmetatable関数を用いてテーブルにアタッチします。元表自体も通常のテーブルであり、特定のキー(メタメソッド)を持つことで振る舞いを制御します。
local base_table = {}
local meta = {}
local instance = setmetatable(base_table, meta)
元表の保護:_metatableの利用
元表の外部からのアクセスを制限したい場合、元表内に_metatableキーを設定することで、getmetatableの返値を固定できます。このとき、元表そのものは隠蔽され、外部から変更できなくなります。
local protected = { _metatable = "hidden" }
local obj = setmetatable({}, protected)
print(getmetatable(obj)) -- "hidden" が返る
-- 元表の内容は直接参照・変更不可
__index:キー未定義時のフォールバック処理
__indexメタメソッドは、テーブルのキーが存在しない場合に呼び出されます。値として関数またはテーブルを指定できます。
関数による動的処理
local data = { "Lua", "Java", "C#" }
local fallback = {
__index = function(tbl, key)
print("アクセスされたキー: " .. tostring(key))
return "JavaScript"
end
}
data = setmetatable(data, fallback)
print(data[99]) -- 出力: アクセスされたキー: 99\nJavaScript
テーブルによる継承
local parent = { x = 100, y = 200 }
local child = { name = "point" }
local meta = { __index = parent }
child = setmetatable(child, meta)
print(child.x) -- 100(parentから継承)
print(child.name) -- "point"(child自身の値)
__newindex:新規代入のフック
__newindexは、存在しないキーへの代入時にのみ発火します。既存キーの更新には影響しません。これにより、ログ記録や値のバリデーションが可能です。
local storage = {}
local proxy = {
__newindex = function(tbl, key, value)
if type(value) ~= "string" then
error("文字列のみ許可されます")
end
print("新規設定: " .. key .. " → " .. value)
rawset(tbl, key, value) -- 実際の格納
end
}
storage = setmetatable(storage, proxy)
storage.id = "user_001" -- 出力: 新規設定: id → user_001
storage.id = "user_002" -- 何も出力されない(既存キー)
演算子のオーバーロード
元表では算術・比較演算子を再定義できます。以下は代表的なメタメソッドです。
__add:+__sub:-__mul:*__div:/__eq:==__lt:<__le:<=
例:二つのリストを連結する
local list1 = { "A", "B" }
local list2 = { "C", "D" }
local list_meta = {
__add = function(a, b)
local result = {}
for _, v in ipairs(a) do table.insert(result, v) end
for _, v in ipairs(b) do table.insert(result, v) end
return result
end
}
list1 = setmetatable(list1, list_meta)
local combined = list1 + list2
print(table.concat(combined, ",")) -- A,B,C,D
__call:テーブルの関数呼び出し
テーブルを関数のように呼び出すと、__callが実行されます。これにより、ファクトリーオブジェクトやコールバックオブジェクトを構築できます。
local counter = { value = 0 }
local meta = {
__call = function(tbl, increment)
tbl.value = tbl.value + (increment or 1)
return tbl.value
end
}
counter = setmetatable(counter, meta)
print(counter()) -- 1
print(counter(5)) -- 6
print(counter()) -- 7
__tostring:文字列表現のカスタマイズ
print()や文字列変換時に__tostringが呼び出され、テーブルの表示形式を制御できます。
local config = { host = "localhost", port = 8080 }
local meta = {
__tostring = function(tbl)
return "Config[host=" .. tbl.host .. ", port=" .. tbl.port .. "]"
end
}
config = setmetatable(config, meta)
print(config) -- Config[host=localhost, port=8080]