Luaで継承のような実装をする(テーブル+メタテーブル)

目次

概要

Luaにはクラスや継承といった仕組みはありませんが、テーブルとメタテーブルを使うことで、継承のような振る舞いを実装できます。

 

ポイント

子テーブルに存在しないキーを参照すると__indexを通じて親テーブルから探します。

これにより、親のメソッドを子から使えます。

  • Luaにはクラスや継承はない
  • __index による探索で継承風に実装できる
  • 子 → 親へメソッド探索が流れる

クラスの継承のような実装のサンプル

-- 親クラス
local Color1 = {}
Color1.__index = Color1

function Color1:new(name)
    local obj = setmetatable({ name = name }, self)
    return obj
end

function Color1:getName()
    return self.name
end

-- 子クラス(親を継承)
local Color2 = {}
setmetatable(Color2, { __index = Color1 })
Color2.__index = Color2

function Color2:getChildName()
    return "child:" .. self.name
end

-- 使用例
local c = Color2:new("red")

print(c:getName())       -- red(親のメソッド)
print(c:getChildName())  -- child:red(子のメソッド)

 

解説

① 親クラス

local Color1 = {}
Color1.__index = Color1

クラステーブルです。
__index に自分自身を設定することで、インスタンスからメソッドを参照できるようにします。

 

② 子クラス(継承のポイント)

setmetatable(Color2, { __index = Color1 })

ここが継承の本体です。
Color2 に存在しないキーは、メタテーブルの __index を通じて Color1 から探されます。

 

③ 子クラスの __index

Color2.__index = Color2

Color2:new() で作ったインスタンスのメタテーブルが Color2 になるため、この設定が必要です。
これにより「インスタンス → Color2 → Color1」という探索チェーンが成立します。

 

④ メソッド探索の流れ

function Color1:new(name)
    local obj = setmetatable({ name = name }, self)
    return obj
end

Color2:new("red")を呼ぶとき、selfはColor2になります。
そのためobjのメタテーブルはColor2になり、子クラスにnew()を別途定義する必要はありません。

 

⑤ メソッド探索の流れ

c:getName1()

内部では以下の順に探索されます。

1.c.getNameを探す→ない
2.cのメタテーブル(Color2)を見る→Color2.getNameはない
3.Color2のメタテーブルの__index(=Color1)を見る
4.Color1.getNameを発見→実行

これがLuaにおける継承風の仕組みです

→Color2:new("red")したが、親のColor1.getNameを使える

 

⑥ メソッドのオーバーライド

-- function Color2:getChildName()
function Color2:getName()
    -- return "child:" .. self.name
    return "override:" .. self.name
end

local c = Color2:new("red")
print(c:getName())  -- override:red(子のメソッドが呼ばれる)

仮に、子クラスで親と同名のメソッドを定義すると、子クラスの定義が優先されます。
探索順により Color2.getName が先に見つかるため、Color1.getName は呼ばれません。

関連の記事

Lua (__index) 存在しないキー参照時の動作
Luaでクラスのような実装をする(テーブル+メタテーブル)

△上に戻る