{}const=>[]async()letfn</>var
Lua開発

Luaのメタテーブル:複雑なものをシンプルに

Luaのメタテーブルとは何か、なぜそれらが必要なのか、そしてそれらを使用してテーブルに「超能力」を追加する方法について説明します。

К

Kodik

著者

2分で読める

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

🧩 metatablesとは

Metatable は、setmetatable() を介して別のテーブルに添付された通常のテーブルであり、インデックス作成、追加、比較、連結、呼び出しなどの操作にオブジェクトがどのように反応するかを説明します。

メタテーブルを「ルールのレイヤー」と考えてください。Luaはキーと操作をチェックし、適切なメタメソッド(__indexなど)が見つかった場合はそれを使用します。

🧰 なぜ必要なのか

課題

メタメソッド

デフォルト/プロトタイプ

__index

記録の制御/禁止

__newindex

加算/乗算/...の再定義

__add, __mul, …

美しい結論

__tostring

比較/長さ/連結

__eq, __lt, __len, __concat

「呼び出し先」テーブルを作成します

__call

メタテーブルを保護する

__metatable

🔥 10万人以上の学生が参加中

理論を読むのに疲れた?
コーディングの時間だ!

Kodik — 実践でプログラミングを学ぶアプリ。AIメンター、インタラクティブなレッスン、実際のプロジェクト。

🤖 AI 24時間
🎓 修了証
💰 無料
🚀 始める
今日参加

最も短い例:ベクトルとしての表の追加

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の詳細なレッスン、ステップバイステップのエクササイズ、エラー解析、そして便利な練習を、携帯電話やブラウザで直接見つけることができます。

ニュース、新機能、役立つ資料を知りたい場合は、当社のニュースレターをご購読ください。 テレグラムチャンネル. そこは快適で、ビジネスに向いており、コードへの愛情が ❤️

🎯先延ばしをやめよう

記事は気に入った?
実践の時間だ!

Kodikでは読むだけでなく、すぐにコードを書く。理論 + 実践 = 本当のスキル。

即座に実践
🧠AIがコードを説明
🏆修了証

登録不要 • カード不要