__index によるリンク
CoddyのLuaジャーニー「Object Oriented Programming」セクションの一部 — レッスン 7/70。
プロパティとメソッドを持つプロトタイプテーブルがありますが、新しいオブジェクトに実際にそれを使用させるにはどうすればよいでしょうか?その答えは __index メタメソッドにあります。
テーブルに存在しないキーにアクセスした際、Luaは通常nilを返します。しかし、そのテーブルに__indexフィールドを持つメタテーブルが設定されている場合、Luaは代わりにそのフィールドを参照します。__indexがプロトタイプを指すように設定することで、見つからないキーは自動的にプロトタイプから検索されるようになります:
local Dog = {
species = "Canis familiaris",
legs = 4
}
function Dog:bark()
print("Woof!")
end
-- 空のインスタンスを作成
local myDog = {}
-- プロトタイプにリンク
setmetatable(myDog, { __index = Dog })
print(myDog.species) -- 出力: Canis familiaris
myDog:bark() -- 出力: Woof!テーブル myDog は完全に空ですが、myDog.species へのアクセスは動作します。Lua が myDog の中に species を見つけられないとき、メタテーブルの __index をチェックし、Dog の中にそれを見つけます。
これはLuaにおけるオブジェクト指向プログラミングの基礎です。インスタンスは独自のデータを持つことができ、共有される振る舞いはプロトタイプに存在します。次のレッスンでは、このパターンをクリーンなコンストラクタ関数にまとめる方法を学びます。
チャレンジ
簡単それでは、__index メタメソッドを実際に使ってみましょう!プロトタイプを作成し、インスタンスをそれにリンクさせることで、インスタンスが直接持っていないプロパティやメソッドにアクセスできるようにします。
コードは2つのファイルに分けて構成します:
Robot.lua: 以下の内容を持つRobotプロトタイプテーブルを定義します:modelフィールドを"RX-100"に設定batteryLevelフィールドを100に設定- ロボットの現在の状態を表示する
:status()メソッド
main.lua: Robot モジュールを読み込み(require)、空のインスタンステーブルを作成して、setmetatableと__indexを使用して Robot プロトタイプにリンクさせます。これにより、空のインスタンスがプロトタイプからすべてにアクセスできるようになります。
1つの入力を受け取ります:ロボットのインスタンスに割り当てる名前です。
インスタンスをプロトタイプにリンクさせた後:
- 入力された名前をインスタンスの
nameフィールドに割り当てます(これはインスタンス固有のものになります) - インスタンスの
:status()メソッドを呼び出します
:status() メソッドは、以下の正確な形式で出力する必要があります:
[name] ([model]) - Battery: [batteryLevel]%例えば、入力が Sparky の場合、出力は以下のようになります:
Sparky (RX-100) - Battery: 100%インスタンスが独自の name を持ちながら、__index リンクを通じてプロトタイプから model や batteryLevel にアクセスしている点に注目してください。
チートシート
__index メタメソッドを使用すると、オブジェクトはプロトタイプテーブルからプロパティやメソッドを継承できます。
テーブルに存在しないキーにアクセスすると、Luaはそのテーブルに __index フィールドを持つメタテーブルがあるかどうかを確認します。もしあれば、Luaは __index で指定されたテーブル内でそのキーを探します。
インスタンスをプロトタイプにリンクするには:
local Prototype = {
property = "value"
}
function Prototype:method()
print("Method called")
end
-- インスタンスを作成する
local instance = {}
-- __index を使用してプロトタイプにリンクする
setmetatable(instance, { __index = Prototype })
-- プロトタイプのプロパティとメソッドにアクセスする
print(instance.property) -- 出力: value
instance:method() -- 出力: Method calledインスタンステーブルは空にすることもできますが、プロトタイプからすべてのプロパティやメソッドにアクセスできます。また、インスタンスはプロトタイプをオーバーライドしたり拡張したりする独自のプロパティを持つこともできます。
このパターンはLuaにおけるオブジェクト指向プログラミングの基礎であり、インスタンスは独自のデータを保持しながら、プロトタイプから振る舞いを共有します。
自分で試してみよう
-- 入力を読み込む
local name = io.read()
-- Robotモジュールを読み込む
local Robot = require('Robot')
-- TODO: 空のインスタンス用テーブルを作成する
-- TODO: __indexを使用してsetmetatableでインスタンスをRobotプロトタイプにリンクする
-- TODO: 入力された名前をインスタンスのnameフィールドに代入する
-- TODO: インスタンスの :status() メソッドを呼び出す
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。