この初版は Lua 5.0 用に書かれました。より新しいバージョンでも依然として大まかに関連がありますが、いくつかの相違があります。
第 4 版は Lua 5.3 を対象としたもので、Amazon およびその他の書店で入手できます。
この書籍を購入することで、Lua プロジェクトをサポートできます。


16.3 – 多重継承

Lua ではオブジェクトがプリミティブではないため、Lua でオブジェクト指向プログラミングを行う方法はいくつかあります。インデックスメタメソッドを使用した私たちが以前に見た方法は、おそらく簡潔性、パフォーマンス、柔軟性の最高の組み合わせです。それにも関わらず、特定のケースにさらに適している可能性のある他の実装があります。ここでは Lua で多重継承を可能にする代替の実装を見ていきます。

この実装の鍵は、メタフィールド __index に関数をで使用することです。テーブルのメタテーブルの __index フィールドに関数がある場合は、元のテーブルでキーが見つからない場合に常に Lua がその関数を呼び出すことを覚えておいてください。その後、__index は、必要な数の親で欠落しているキーを検索できます。

多重継承とは、クラスが複数のスーパークラスを持つ可能性があることを意味します。そのため、クラスメソッドを使用してサブクラスを作成することはできません。その代わり、その目的に特化した関数 createClass を定義します。この関数は、新しいクラスのスーパークラスを議論として持ちます。この関数は、新しいクラスを表すテーブルを作成し、多重継承を行う __index メタメソッドを使用してメタテーブルを設定します。多重継承にもかかわらず、各インスタンスは 1 つのクラスに属し、それらすべてのメソッドを探します。したがって、クラスとスーパークラスの間の関係は、クラスとインスタンスの間の関係とは異なります。特に、クラスはインスタンスのメタテーブルとそれ自身のメタテーブルの両方を同時に持つことはできません。この実装では、インスタンスのメタテーブルとしてクラスを保持し、クラスのメタテーブルとなる別のテーブルを作成します。

    -- look up for `k' in list of tables `plist'
    local function search (k, plist)
      for i=1, table.getn(plist) do
        local v = plist[i][k]     -- try `i'-th superclass
        if v then return v end
      end
    end
    
    function createClass (...)
      local c = {}        -- new class
    
      -- class will search for each method in the list of its
      -- parents (`arg' is the list of parents)
      setmetatable(c, {__index = function (t, k)
        return search(k, arg)
      end})
    
      -- prepare `c' to be the metatable of its instances
      c.__index = c
    
      -- define a new constructor for this new class
      function c:new (o)
        o = o or {}
        setmetatable(o, c)
        return o
      end
    
      -- return new class
      return c
    end

小さい例を使用して createClass の使用について説明しましょう。以前の Account クラスと、setnamegetname という 2 つのメソッドのみを持つ、Named という別のクラスがあると仮定します。

    Named = {}
    function Named:getname ()
      return self.name
    end
    
    function Named:setname (n)
      self.name = n
    end
AccountNamed の両方のサブクラスである新しいクラス NamedAccount を作成するには、createClass を呼び出すだけです。
    NamedAccount = createClass(Account, Named)
インスタンスを作成して使用する方法は通常どおりです。
    account = NamedAccount:new{name = "Paul"}
    print(account:getname())     --> Paul
最後のステートメントで何が起こるかを見てみましょう。Lua は account でフィールド getname を見つけることができません。そこで、account のメタテーブル、つまり NamedAccount__index フィールドを探します。しかし、NamedAccountgetname フィールドを提供できないため、Lua は NamedAccount のメタテーブルの __index フィールドを探します。このフィールドには関数が含まれているため、Lua はそれを呼び出します。この関数は最初に Accountgetname を検索しますが、見つからず、次に Named で検索します。ここで非 nil の値が見つかり、検索の最終結果となります。

もちろん、この検索の根本的な複雑さにより、多重継承のパフォーマンスは単一継承と同じではありません。このパフォーマンスを向上させる簡単な方法は、継承したメソッドをサブクラスにコピーすることです。この手法を使用すると、クラスのインデックスメタメソッドは次のように記述できます。

      ...
      setmetatable(c, {__index = function (t, k)
        local v = search(k, arg)
        t[k] = v       -- save for next access
        return v
      end})
      ...
このテクニックにより、継承されたメソッドへのアクセスはローカルメソッドへのアクセスと同じくらい高速になります(最初のアクセスを除く)。欠点は、システム実行後にメソッド定義を変更するのが難しいことです。これらの変更は階層チェーンのどこにも伝播しないためです。