この第 1 版は Lua 5.0 で記述されています。引き続き後続バージョンでも大部分が関連していますが、一部違いがあります。
第 4 版は Lua 5.3 をターゲットにしており、Amazon およびその他の書店で購入できます。
本を購入することで、Lua プロジェクトをサポートすることにもなります。


16 – オブジェクト指向プログラミング

Lua のテーブルは、単なるオブジェクトではありません。オブジェクトと同様に、テーブルは状態を持ちます。オブジェクトと同様に、テーブルは、その値とは独立した ID (「自己」) を持ちます。具体的には、同じ値を持つ2つのオブジェクト (テーブル) は別のオブジェクトですが、1つのオブジェクトはさまざまな時点で異なる値を持つことができますが、常に同じオブジェクトです。オブジェクトと同様に、テーブルは作成者や作成場所とは無関係なライフサイクルを持ちます。

オブジェクトには独自の操作があります。テーブルも操作できます。

    Account = {balance = 0}
    function Account.withdraw (v)
      Account.balance = Account.balance - v
    end
この定義は新しい関数を作成し、それを Account オブジェクトの withdraw フィールドに格納します。その後、それを以下のように呼び出すことができます。
    Account.withdraw(100.00)

この種類の関数は、ほとんどメソッドと呼ばれます。ただし、関数内のグローバル名 Account を使用することは、悪いプログラミング手法です。まず、この関数は、この特定のオブジェクトに対してのみ機能します。第二に、この特定のオブジェクトの場合でも、オブジェクトがその特定のグローバル変数に格納されている場合にのみ関数は機能します。このオブジェクトの名前を変更すると、withdraw は機能しなくなります。

    a = Account; Account = nil
    a.withdraw(100.00)   -- ERROR!
このような動作は、オブジェクトに独立したライフサイクルがあるという前述の原則に違反しています。

より柔軟なアプローチとして、オペレーションの「レシーバー」でオペレーションを行う方法があります。そのためには、メソッドを定義する際に追加のパラメーターを追加する必要があります。このパラメーターは、メソッドがどのオブジェクトに対してオペレーションを行う必要があるかをメソッドに指示します。このパラメーターには通常、self または this という名前が付けられます。

    function Account.withdraw (self, v)
      self.balance = self.balance - v
    end
次に、メソッドを呼び出すときに、メソッドがどのオブジェクトに対してオペレーションを行う必要があるかを指定する必要があります。
    a1 = Account; Account = nil
    ...
    a1.withdraw(a1, 100.00)   -- OK
self パラメーターを使用すると、同じメソッドを多くのオブジェクトに使用できます。
    a2 = {balance=0, withdraw = Account.withdraw}
    ...
    a2.withdraw(a2, 260.00)

self パラメータの使用は、あらゆるオブジェクト指向言語において重要な点です。ほとんどの OO 言語において、このメカニズムはプログラマーからある程度隠されており、プログラマーはこのパラメータを宣言する必要はありません (ただし、メソッド内で self または this という名前を依然として使用できます)。Lua はコロン演算子を使用して、このパラメータを隠すこともできます。前のメソッド定義を書き換えると

    function Account:withdraw (v)
      self.balance = self.balance - v
    end
メソッド呼び出しは
    a:withdraw(100.00)
コロンの効果によって、メソッド定義に隠された追加パラメータが追加され、メソッド呼び出しに追加引数が追加されます。コロンは構文の便宜に過ぎず、新しいものではありません。ドット構文を使用して関数を定義し、コロン構文で呼び出すこともできますし、その逆もできます。追加パラメータを正しく処理すればよいからです。
    Account = { balance=0,
                withdraw = function (self, v)
                             self.balance = self.balance - v
                           end
              }
    
    function Account:deposit (v)
      self.balance = self.balance + v
    end
    
    Account.deposit(Account, 200.00)
    Account:withdraw(100.00)

オブジェクトは今や ID、ステータス、およびステータスに対する操作を持っています。クラスシステム、継承、プライバシーはまだありません。最初の問題に取り組みましょう: どうすれば類似した動作を持つ複数のオブジェクトを作成できますか? 具体的には、どうすれば複数の口座を作成できますか?