0Pricing
Lua Academy · レッスン

メタテーブルによるクラス

テーブルと__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フィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. メタテーブルによるクラス
  2. コンストラクターとnew()
  3. インスタンスメソッドとself
  4. クロージャによるカプセル化
← Lua Academyに戻る