メタテーブルによるクラス
テーブルと__indexを使い、メソッド検索を備えたクラスを再現します。
「メタテーブルによるクラス」はCoddyKit上の無料Lua Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLua Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Lua Academyコースには全4レッスンが含まれています。
クラステーブルパターン
Luaでは、「クラス」はメソッドを格納するテーブルにすぎません。インスタンスは、メタテーブルの__indexがクラスのテーブルを指しているテーブルです。これにより、検索チェーンを通じてインスタンスからすべてのメソッドにアクセスできます。
local Animal = {}
Animal.__index = Animal
function Animal.new(name, sound)
return setmetatable({name=name, sound=sound}, Animal)
end
function Animal:speak()
print(self.name .. " says " .. self.sound)
end
local dog = Animal.new("Rex","Woof")
dog:speak() -- Rex says Woof__indexがクラスを指す
ClassName.__index = ClassNameを設定すると、「見つからないキーをクラスのテーブルで検索する」という意味になります。インスタンスメソッドが呼び出されると、Luaはまずインスタンス内を探し、メタテーブル(__index)を確認してクラスを見つけ、そこでメソッドを検索します。
local Point = {}
Point.__index = Point
Point.type = "2D Point"
function Point.new(x,y)
return setmetatable({x=x,y=y}, Point)
end
function Point:magnitude()
return math.sqrt(self.x^2 + self.y^2)
end
local p = Point.new(3,4)
print(p:magnitude()) -- 5.0
print(p.type) -- 2D Point (from class)コンストラクターパターン
new()関数はコンストラクターです。インスタンスデータを持つテーブルを作成し、クラスを指すメタテーブルでラップして返します。これはLuaでオブジェクトを作成する標準的な方法です。
local Circle = {}
Circle.__index = Circle
function Circle.new(cx, cy, radius)
assert(radius > 0, "radius must be positive")
return setmetatable({cx=cx, cy=cy, r=radius}, Circle)
end
function Circle:area()
return math.pi * self.r^2
end
function Circle:perimeter()
return 2 * math.pi * self.r
end
local c = Circle.new(0, 0, 5)
print(string.format("Area: %.2f", c:area()))メソッド構文と関数構文
obj:method()はobj.method(obj)の糖衣構文です。function Cls:method()のようにコロン構文でメソッドを定義すると、暗黙的にselfを受け取れます。インスタンスを渡すにはコロンで呼び出します。ドットとコロンを混在させると、気付きにくいバグの原因になります。
local Dog = {}
Dog.__index = Dog
-- Define with colon: self is implicit
function Dog:bark(times)
for i = 1, (times or 1) do
print(self.name .. ": Woof!")
end
end
function Dog.new(name)
return setmetatable({name=name}, Dog)
end
local d = Dog.new("Buddy")
d:bark(2) -- Buddy: Woof! (x2)
-- Dog.bark(d,1) -- same thing, explicit selfクラスレベルとインスタンスレベルのフィールド
クラスのテーブルにあるフィールドは、すべてのインスタンスで共有されます(クラス変数)。インスタンスのテーブルにあるフィールドは、インスタンスごとに固有です。インスタンスは、同じ名前の独自のフィールドを作成することで、クラスのフィールドを隠すことができます。
local Entity = {}
Entity.__index = Entity
Entity.count = 0 -- class-level counter
function Entity.new(name)
Entity.count = Entity.count + 1
return setmetatable({name=name, id=Entity.count}, Entity)
end
local a = Entity.new("alpha")
local b = Entity.new("beta")
local c = Entity.new("gamma")
print(Entity.count) -- 3
print(a.id, b.id, c.id) -- 1 2 3クラスのtostring
読みやすい出力を得るには、クラスのテーブルに__tostringを追加します。インスタンスのメタテーブルはクラスであり、そのクラスに__tostringがあるため、すべてのインスタンスで自動的に使用されます。
local Vec2 = {}
Vec2.__index = Vec2
Vec2.__tostring = function(v)
return string.format("Vec2(%g, %g)", v.x, v.y)
end
function Vec2.new(x,y)
return setmetatable({x=x,y=y}, Vec2)
end
function Vec2.__add(a, b)
return Vec2.new(a.x+b.x, a.y+b.y)
end
local v1 = Vec2.new(1,2)
local v2 = Vec2.new(3,4)
print(v1+v2) -- Vec2(4, 6)インスタンス型の確認
オブジェクトがクラスのインスタンスかどうかは、そのメタテーブルとクラスのテーブルを比較して確認できます。これはLuaにおけるinstanceof相当の機能です。
local Dog = {}
Dog.__index = Dog
function Dog.new(name)
return setmetatable({name=name}, Dog)
end
local function isDog(obj)
return getmetatable(obj) == Dog
end
local d = Dog.new("Rex")
local t = {}
print(isDog(d)) -- true
print(isDog(t)) -- falseプライベートメソッド
プライベートメソッドは、モジュールファイル内のローカル関数です。外部から呼び出すことはできませんが、モジュールのpublicメソッド内からはupvalueとしてアクセスできます。
local Account = {}
Account.__index = Account
-- Private helper
local function validate(amount)
return type(amount)=="number" and amount > 0
end
function Account.new(balance)
return setmetatable({balance=balance or 0}, Account)
end
function Account:deposit(amount)
assert(validate(amount), "invalid amount")
self.balance = self.balance + amount
end
function Account:getBalance() return self.balance endクラスの比較
独自の等価性と順序付けを実装するには、__eqと__ltをオーバーロードします。Luaがテーブルに対して__eqを呼び出すのは、両方のオペランドが同じメタテーブルを共有している場合だけです。
local Temp = {}
Temp.__index = Temp
Temp.__eq = function(a,b) return a.celsius == b.celsius end
Temp.__lt = function(a,b) return a.celsius < b.celsius end
Temp.__tostring = function(t)
return t.celsius.."°C"
end
function Temp.new(c)
return setmetatable({celsius=c}, Temp)
end
local t1 = Temp.new(100)
local t2 = Temp.new(0)
print(t1 == Temp.new(100)) -- true
print(t2 < t1) -- true複数のクラス
各クラスは、独自のメタテーブルを持つ独立したテーブルです。異なるクラスのオブジェクトは、異なるメタテーブルを持ちます。これがLuaでマルチクラスOOPシステムを構築する基盤になります。
local Cat = {}
Cat.__index = Cat
function Cat.new(name) return setmetatable({name=name,lives=9},Cat) end
function Cat:loseLife() self.lives=self.lives-1; return self.lives end
local Dog2 = {}
Dog2.__index = Dog2
function Dog2.new(name) return setmetatable({name=name,tricks={}},Dog2) end
function Dog2:learn(t) self.tricks[#self.tricks+1]=t end
local c = Cat.new("Whiskers")
local d = Dog2.new("Buddy")
d:learn("sit"); d:learn("stay")
print(c:loseLife(), #d.tricks) -- 8 2クイックチェック
なぜClassName.__index = ClassNameを設定するのでしょうか?
復習:メタテーブルによるクラス
まとめ:
- クラス = テーブル +
__index = self - コンストラクター:
function Cls.new(...) return setmetatable({}, Cls) end - メソッド:クラスのテーブルに定義し、コロン構文で呼び出す
- クラスとインスタンスのフィールド:クラスのフィールドは共有、インスタンスのフィールドはオブジェクトごとに固有
- 型チェック:
getmetatable(obj) == Cls
よくある質問
「メタテーブルによるクラス」レッスンは無料ですか?
はい。「メタテーブルによるクラス」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Lua Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Lua Academyコースには全4レッスンが含まれています。
「メタテーブルによるクラス」で何を学びますか?
テーブルと__indexを使い、メソッド検索を備えたクラスを再現します。 ブラウザで直接実行するハンズオンコードでLua Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。
Lua Academyを始めるのに経験は必要ですか?
事前経験は必要ありません。CoddyKitのLua Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。
「メタテーブルによるクラス」レッスンにはどのくらい時間がかかりますか?
ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。
このLua Academyレッスンでコードを書いて実行できますか?
はい。すべてのLua Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- メタテーブルによるクラス
- コンストラクターとnew()
- インスタンスメソッドとself
- クロージャによるカプセル化