メタテーブル入門
setmetatable/getmetatableでメタテーブルを設定・取得します。
「メタテーブル入門」はCoddyKit上の無料Lua Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLua Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Lua Academyコースには全4レッスンが含まれています。
メタテーブルとは
メタテーブルは、別のテーブルの動作を制御する通常のLuaテーブルです。Luaがテーブルに対して実行できない操作(算術演算や、存在しないキーのインデックス参照など)に遭遇すると、メタテーブル内から対応するメタメソッドを探します。これは、演算子オーバーロードやOOPのためのLuaの拡張メカニズムです。
local t = {}
local mt = {__index = function(_, k) return "default_" .. k end}
setmetatable(t, mt)
print(t.foo) -- default_foo
print(t.bar) -- default_bar
print(t[1]) -- default_1setmetatable と getmetatable
setmetatable(t, mt)はtのメタテーブルをmtに設定し、tを返します。getmetatable(t)はメタテーブルを返し、存在しない場合はnilを返します。mt.__metatable = "protected"を設定すると、メタテーブルが変更されないように保護できます。
local t = {x = 10}
local mt = {}
setmetatable(t, mt)
print(getmetatable(t) == mt) -- true
-- Protect metatable
mt.__metatable = "locked"
print(getmetatable(t)) -- locked
-- setmetatable(t, {}) -- ERROR: cannot change protected metatableテーブルとしての __index
__indexがテーブルの場合、Luaはそのテーブルから存在しないキーを検索します。これはLuaのプロトタイプ継承の基盤です。__indexにもそのキーがない場合、Luaはそのテーブルのメタテーブルを調べ、さらにチェーンをたどって検索します。
local Animal = {sound = "...", legs = 4}
local Dog = setmetatable({sound = "Woof"}, {__index = Animal})
local Cat = setmetatable({sound = "Meow"}, {__index = Animal})
print(Dog.sound) -- Woof (own field)
print(Dog.legs) -- 4 (from Animal)
print(Cat.sound) -- Meow
print(Cat.legs) -- 4関数としての __index
__indexが関数の場合、Luaはその関数をテーブルと存在しないキーを引数にして呼び出します。関数は値を動的に計算したり取得したりできます。__indexからnilを返すと、「キーが見つからない」ことを示します。
local dynamic = setmetatable({}, {
__index = function(t, k)
if k:sub(1,4) == "get_" then
local field = k:sub(5)
return function(self) return self[field] end
end
return nil
end
})
dynamic.name = "Lua"
local getter = dynamic.get_name
print(getter(dynamic)) -- Luarawget と rawset
rawget(t, k)は__indexを発動させずにテーブルのキーを読み取ります。rawset(t, k, v)は__newindexを発動させずにキーを設定します。メタメソッド内で直接テーブルにアクセスし、無限再帰を避けたい場合に、これらを使ってメタメソッドを迂回します。
local logged = setmetatable({}, {
__newindex = function(t, k, v)
print("Setting", k, "=", v)
rawset(t, k, v) -- bypass __newindex
end
})
logged.x = 10 -- Setting x = 10
logged.x = 20 -- no log! key now exists in t
print(logged.x) -- 20メタテーブルを共有する複数のテーブル
1つのメタテーブルを多数のテーブルで共有できます。これはLuaのクラスシステムの仕組みです。クラスのすべてのインスタンスが、メソッドを保持する同じメタテーブルを共有します。メタテーブルへの変更は、すべてのインスタンスに影響します。
local Mt = {
__index = {
greet = function(self)
print("Hello from " .. (self.name or "?"))
end
}
}
local a = setmetatable({name="Alice"}, Mt)
local b = setmetatable({name="Bob"}, Mt)
a:greet() -- Hello from Alice
b:greet() -- Hello from Bob__metatableフィールド
mt.__metatableに任意の値を設定すると、getmetatableは実際のメタテーブルではなく、その値を返します。これにより、ユーザーによるメタテーブルの調査や置き換えを防げます。カプセル化やセキュリティに役立ちます。
local secret = setmetatable({}, {
__metatable = false, -- hide metatable
__index = {value = 42}
})
print(secret.value) -- 42
print(getmetatable(secret)) -- false (not the real mt)
-- setmetatable(secret, {}) -- ERRORメタテーブルの確認
getmetatable(t)を使うと、オブジェクトが特定のメタテーブルを持っているか確認できます(型チェックに使用します)。__metatableによって実際のメタテーブルが隠される場合があるため、信頼性の高いisinstanceチェックにはrawequalまたはマーカー用フィールドを使用してください。
local MyClass = {}
MyClass.__index = MyClass
MyClass.__metatable = MyClass -- use self as marker
function MyClass.new(x)
return setmetatable({x=x}, MyClass)
end
local obj = MyClass.new(5)
print(getmetatable(obj) == MyClass) -- true
print(obj.x) -- 5テーブル以外の値のメタテーブル
Luaの文字列には、すでにメタテーブル(stringモジュール)が設定されています。そのため、任意の文字列に対してs:upper()を呼び出せます。純粋なLuaから文字列、数値、ブール値にメタテーブルを設定することはできません。設定できるのはテーブルとuserdataだけです。
local s = "hello"
local mt = getmetatable(s)
-- String metatable has __index = string library
print(mt.__index == string) -- true
-- So all string functions are methods:
print(s:upper()) -- HELLO
print(s:rep(3, "-")) -- hello-hello-helloメタテーブルを使ったデバッグ
__tostringメタメソッドを追加すると、テーブルをtostring()やprint()で表示する方法を制御できます。複雑なオブジェクトのデバッグに非常に役立ちます。
local Point = {}
Point.__index = Point
Point.__tostring = function(p)
return string.format("Point(%g, %g)", p.x, p.y)
end
function Point.new(x, y)
return setmetatable({x=x,y=y}, Point)
end
local p = Point.new(3, 4)
print(tostring(p)) -- Point(3, 4)
print(p) -- Point(3, 4) (print calls tostring)確認問題
__indexが別のテーブルに設定されたメタテーブルを持つテーブルで、存在しないキーにアクセスすると何が起こりますか?
まとめ:メタテーブル
まとめ:
setmetatable(t, mt)/getmetatable(t)__index:テーブルの場合はプロトタイプ検索、関数の場合は動的ディスパッチrawget/rawsetはメタメソッドを回避します- メタテーブルを共有すると、クラスのような動作を実現できます
__metatableはメタテーブルを調査から保護します__tostringは出力方法を制御します
よくある質問
「メタテーブル入門」レッスンは無料ですか?
はい。「メタテーブル入門」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Lua Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Lua Academyコースには全4レッスンが含まれています。
「メタテーブル入門」で何を学びますか?
setmetatable/getmetatableでメタテーブルを設定・取得します。 ブラウザで直接実行するハンズオンコードでLua Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Lua Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのLua Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「メタテーブル入門」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このLua Academyレッスンでコードを書いて実行できますか?
はい。すべてのLua Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。