この初版はLua 5.0向けに書かれました。後続バージョンでも大部分は関連性がありますが、いくつかの違いがあります。
第4版はLua 5.3を対象としており、Amazonおよびその他の書店で入手可能です。
本書を購入することで、Luaプロジェクトの支援にも貢献できます。


16.1 – クラス

クラスは、オブジェクト作成のための鋳型として機能します。多くのオブジェクト指向言語はクラスの概念を提供します。そのような言語では、各オブジェクトは特定のクラスのインスタンスです。Luaにはクラスの概念がありません。各オブジェクトは独自の動作を定義し、独自の形状を持っています。しかしながら、SelfやNewtonScriptなどのプロトタイプベースの言語を参考にすれば、Luaでクラスをエミュレートするのは困難ではありません。これらの言語では、オブジェクトはクラスを持ちません。代わりに、各オブジェクトはプロトタイプを持つ場合があります。プロトタイプは通常のオブジェクトであり、最初のオブジェクトは自分が知らない操作をそこで検索します。そのような言語でクラスを表すには、他のオブジェクト(そのインスタンス)のプロトタイプとして排他的に使用されるオブジェクトを作成するだけです。クラスとプロトタイプはどちらも、複数のオブジェクトで共有される動作を配置する場所として機能します。

Luaでは、前の章で見た継承のアイデアを使用して、プロトタイプを実装するのは簡単です。より具体的に言うと、2つのオブジェクトabがある場合、baのプロトタイプにするために必要なのは

    setmetatable(a, {__index = b})
これ以降、aは自分が持っていない操作をbで検索します。bをオブジェクトaのクラスと見なすのは、用語の変更以上のことはありません。

銀行口座の例に戻りましょう。Accountと同様の動作を持つ他の口座を作成するには、これらの新しいオブジェクトが__indexメタメソッドを使用してAccountから操作を継承するように配置します。口座オブジェクトのメタテーブルとして追加のテーブルを作成する必要がないという小さな最適化に注意してください。その目的のためにAccountテーブル自体を使用できます。

    function Account:new (o)
      o = o or {}   -- create object if user does not provide one
      setmetatable(o, self)
      self.__index = self
      return o
    end
(Account:newを呼び出すとき、selfAccountと等しくなります。そのため、selfの代わりにAccountを直接使用することもできました。ただし、次のセクションでクラス継承を紹介する際に、selfの使用が適切に適合します。)そのコードの後、新しい口座を作成してそのメソッドを呼び出すと何が起こるでしょうか?
    a = Account:new{balance = 0}
    a:deposit(100.00)
この新しい口座を作成すると、aはメタテーブルとしてAccountAccount:new呼び出しでのself)を持ちます。その後、a:deposit(100.00)を呼び出すと、実際にはa.deposit(a, 100.00)を呼び出しています(コロンは構文上の砂糖にすぎません)。しかし、Luaはテーブルa"deposit"エントリを見つけることができません。そのため、メタテーブルの__indexエントリを調べます。状況はほぼ次のようになります。
    getmetatable(a).__index.deposit(a, 100.00)
aのメタテーブルはAccountであり、Account.__indexAccountです(新しいメソッドがself.__index = selfを実行したため)。したがって、前の式を次のように書き直すことができます。
    Account.deposit(a, 100.00)
つまり、Luaは元のdeposit関数を呼び出しますが、selfパラメーターとしてaを渡します。そのため、新しい口座aAccountからdeposit関数を継承しました。同じメカニズムによって、Accountからすべてのフィールドを継承できます。

継承はメソッドだけでなく、新しい口座にない他のフィールドにも機能します。したがって、クラスはメソッドだけでなく、インスタンスフィールドのデフォルト値も提供します。最初のAccountの定義では、値0のbalanceフィールドを提供したことを思い出してください。したがって、初期残高なしで新しい口座を作成すると、このデフォルト値を継承します。

    b = Account:new()
    print(b.balance)    --> 0
bdepositメソッドを呼び出すと、次のものと同等の処理が実行されます。
    b.balance = b.balance + v
(selfbであるため)。式b.balanceは0と評価され、初期預金がb.balanceに割り当てられます。次にこの値を要求すると、インデックスメタメソッドは呼び出されません(bが独自のbalanceフィールドを持つようになったため)。