テーブルに「特別な」動作をさせたい場合がよくあります。ベクトルのように追加したり、デフォルト値を代入したり、きれいに印刷したりします。✨このために、Luaには metatables — 表の動作を隠すスクリプト。以下は、簡潔で実用的で痛みのない分析です。

🧩 metatablesとは
Metatable は、setmetatable() を介して別のテーブルに添付された通常のテーブルであり、インデックス作成、追加、比較、連結、呼び出しなどの操作にオブジェクトがどのように反応するかを説明します。
メタテーブルを「ルールのレイヤー」と考えてください。Luaはキーと操作をチェックし、適切なメタメソッド(__indexなど)が見つかった場合はそれを使用します。
🧰 なぜ必要なのか
課題 | メタメソッド |
|---|---|
デフォルト/プロトタイプ |
|
記録の制御/禁止 |
|
加算/乗算/...の再定義 |
|
美しい結論 |
|
比較/長さ/連結 |
|
「呼び出し先」テーブルを作成します |
|
メタテーブルを保護する |
|
最も短い例:ベクトルとしての表の追加
local mt = {
__add = function(a, b)
return { a[1] + b[1], a[2] + b[2] }
end
}
local v1 = setmetatable({1, 2}, mt)
local v2 = setmetatable({3, 4}, mt)
local sum = v1 + v2
print(sum[1], sum[2]) --> 4 6
メタテーブルなしで v1 + v2 はエラーを引き起こします。Lua はテーブルの追加方法を知りません。
🧭 __index: デフォルト値と「継承」
__indexは、テーブルにキーがないときにトリガーされます。プロトタイプテーブルまたは関数への参照として設定できます。
オプション1:プロトタイプ
local defaults = { speed = 10, hp = 100 }
local player = setmetatable({}, { __index = defaults })
print(player.speed) --> 10 (берётся из defaults)
player.speed = 20 -- теперь свой speed у player
print(player.speed) --> 20
オプション2:値の自動生成(遅延初期化)
local counts = setmetatable({}, {
__index = function(t, key)
local v = 0
rawset(t, key, v) -- записываем сразу, чтобы дальше было «как будто существовало всегда»
return v
end
})
counts["apples"] = counts["apples"] + 1
print(counts["apples"]) --> 1
rawsetを使用して、__newindexを呼び出さず、再帰に陥らないようにします。
🛡️ __newindex:記録の管理
local guarded = setmetatable({ x = 0, y = 0 }, {
__newindex = function(t, k, v)
if k == "x" or k == "y" then
rawset(t, k, v) -- безопасная запись без повторного вызова __newindex
else
error("You cannot add a new field: " .. tostring(k), 2)
end
end
})
guarded.x = 10 -- ок
guarded.title = "oops" -- ошибка
よくある間違いは、__newindex 内に t[k] = v を書くことです。これにより、__newindexが再度呼び出され、再帰が起こります。rawsetを使用してください。
⚠️落とし穴
メタテーブル コピーされません 表の表面コピーの場合は、再度ハングアップします。
__indexというチェーンは複雑すぎて、デバッグが難しくなります。モデルはシンプルにしてください。rawget/rawsetメタメソッドをバイパスすることは、強みでもありリスクでもあります。慎重に使用してください。
🚀高度なメタテーブルテクニック
📞 __call:オブジェクトの「ファクトリ」を作成する
メタメソッド __call を使用すると、テーブルを関数として呼び出すことができます。これは、ファクトリやDSLに便利です。
local greeter = setmetatable({}, {
__call = function(_, name) print("Hi, " .. name .. "!") end
})
greeter("Code") --> Привет, Кодик!
🔒 __metatable:変更からの保護
実際のメタメソッドを非表示にして変更を禁止するには、次のようにします。
local obj = {}
local mt = { __metatable = "Access to the metatable is denied" }
setmetatable(obj, mt)
print(getmetatable(obj)) --> Доступ к метатаблице запрещён
-- getmetatable(obj).__index -- уже не получить
🎨 美しい出力と「魔法」の演算子
local mt = {
__tostring = function(t) return ("Point(%d, %d)"):format(t.x, t.y) end,
__len = function(t) return math.sqrt(t.x*t.x + t.y*t.y) end, -- длина вектора через #
__eq = function(a,b) return a.x==b.x and a.y==b.y end,
__add = function(a,b) return setmetatable({x=a.x+b.x, y=a.y+b.y}, mt) end,
}
local p = setmetatable({x=3, y=4}, mt)
print(p) --> Point(3, 4)
print(#p) --> 5
💬 Roblox/Love2Dゲーム、設定システム、またはミニDSLのどこにメタテーブルを適用しますか?
📚 このテーマについてもっと詳しく知りたいですか?
アプリで コディック Luaの詳細なレッスン、ステップバイステップのエクササイズ、エラー解析、そして便利な練習を、携帯電話やブラウザで直接見つけることができます。
ニュース、新機能、役立つ資料を知りたい場合は、当社のニュースレターをご購読ください。 テレグラムチャンネル. そこは快適で、ビジネスに向いており、コードへの愛情が ❤️
