由于类也是对象,因此他们也可以从其他类获得方法。这种行为使得 继承
(即常见的面向对象的定义)可以很容易地在 Lua
语言中实现。
假如有一个类似于 Account
的基类,如下所示:
Account = { balance = 0 } function Account:new(o) o = o or {} self.__index = self setmetatable(o,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=100.00}
SpecialAccount
就像继承其他方法一样从 Account
继承了 new
。不过,现在执行 new
时,它的 self
参数指向的是 SpecialAccount
。因此, s
的元表会是 SpecialAccount
,其中字段 __index
的值也是 SpecialAccount
。因此, s
继承自SpecialAccount
,而 SpecialAccount
又继承自 Account
。之后,执行 s:deposit(100.00)
时, Lua
语言在 s
中找不到 deposit
字段,就会查找 SpecialAccount
,仍然找不到 deposit
字段,就查找 Account
并最终会在 Account
中找到 deposit
的最初实现。
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
语言会在 SpecialAccount
中先找到新的 withdraw
方法,所以不会再从 Account
中查找。由于 s.limit
为 100.00
(我们创建s时设置了这个值),所以程序会执行取款并使得 s
变成负的余额。
Lua
语言中的对象有一个有趣的特性,就是无须为了指定一种新行为而创建一个新类。如果只有单个对象需要某种特殊的行为,那么我们可以直接在该对象中实现这个行为。例如,假设账户 s
表示一个特殊的客户,这个客户的透支额度总是其余额的 10%
,那么可以只修改这个账户:
function s:getLimit() return self.balance * 0.10 end
在这段代码后,调用 s:withdraw(200.00)
还是会执行 SpecialAccount
的 withdraw
方法,但当 withdraw
调用 self:getLimit
时,调用的是上述的定义。