第一版是为 Lua 5.0 编写的。虽然在很大程度上仍然适用于后续版本,但有一些差异。
第四版针对 Lua 5.3,可在 亚马逊 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目


16.2 – 继承

由于类是对象,因此它们也可以从其他类获取方法。这使得在 Lua 中实现继承(以通常的面向对象含义)变得非常容易。

让我们假设我们有一个基类,如 Account

    Account = {balance = 0}
    
    function Account:new (o)
      o = o or {}
      setmetatable(o, self)
      self.__index = self
      return o
    end
    
    function Account:deposit (v)
      self.balance = self.balance + v
    end
    
    function Account:withdraw (v)
      if v > self.balance then error"insufficient funds" end
      self.balance = self.balance - v
    end

从该类中,我们希望派生出一个子类 SpecialAccount,该子类允许客户提取超过其余额的金额。我们从一个空类开始,该类仅从其基类继承所有操作

    SpecialAccount = Account:new()
到目前为止,SpecialAccount 只是一个 Account 实例。现在发生了好事
    s = SpecialAccount:new{limit=1000.00}
SpecialAccountAccount 继承 new,就像任何其他方法一样。然而,这次当 new 执行时,self 参数将引用 SpecialAccount。因此,s 的元表将是 SpecialAccount,其在索引 __index 处的值为 SpecialAccount。因此,sSpecialAccount 继承,而 SpecialAccountAccount 继承。当我们评估
    s:deposit(100.00)
Lua 无法在 s 中找到 deposit 字段,因此它会查找 SpecialAccount;它也无法在其中找到 deposit 字段,因此它会查找 Account,并在其中找到存款的原始实现。

SpecialAccount 变得特别的是,它可以重新定义从其超类继承的任何方法。我们所要做的就是编写新方法

    function SpecialAccount:withdraw (v)
      if v - self.balance >= self:getLimit() then
        error"insufficient funds"
      end
      self.balance = self.balance - v
    end
    
    function SpecialAccount:getLimit ()
      return self.limit or 0
    end
现在,当我们调用 s:withdraw(200.00) 时,Lua 不会转到 Account,因为它首先在 SpecialAccount 中找到了新的 withdraw 方法。因为 s.limit 是 1000.00(请记住,我们在创建 s 时设置了此字段),所以程序执行了取款操作,使 s 余额为负。

Lua 中面向对象编程的一个有趣方面是,您无需创建新类来指定新行为。如果只有一个对象需要特定行为,则可以直接在对象中实现该行为。例如,如果帐户 s 表示某个特殊客户,其限额始终为其余额的 10%,则您只能修改此单个帐户

    function s:getLimit ()
      return self.balance * 0.10
    end
在该声明之后,调用 s:withdraw(200.00) 将运行 SpecialAccount 中的 withdraw 方法,但当该方法调用 self:getLimit 时,它将调用此最后定义。