第一版是为 Lua 5.0 撰写的。虽然在很大程度上仍然适用于后续版本,但有一些区别。
第四版针对 Lua 5.3,可在 亚马逊 和其他书店购买。
通过购买这本书,您还可以帮助 支持 Lua 项目。
用 Lua 编程 | ||
第二部分。表和对象 第 16 章。面向对象编程 |
类用作创建对象的模具。多种面向对象语言提供类的概念。在这些语言中,每个对象都是特定类的实例。Lua 没有类的概念;每个对象都定义自己的行为并具有自己的形状。尽管如此,在基于原型的语言(如 Self 和 NewtonScript)的指导下,在 Lua 中模拟类并不困难。在这些语言中,对象没有类。相反,每个对象可能有一个原型,这是一个常规对象,第一个对象会在其中查找它不知道的任何操作。为了在这些语言中表示一个类,我们只需创建一个对象,专门用作其他对象(其实例)的原型。类和原型都用作放置多种对象共享的行为的地方。
在 Lua 中,使用我们在上一章中看到的继承思想来实现原型非常简单。更具体地说,如果我们有两个对象 a
和 b
,我们所要做的就是让 b
成为 a
的原型,即
setmetatable(a, {__index = b})之后,
a
会在 b
中查找它不具备的任何操作。将 b
视为对象 a
的类,这只不过是术语上的改变。
让我们回到银行账户的示例。为了创建行为类似于 Account
的其他账户,我们安排这些新对象从 Account
继承其操作,使用 __index
元方法。请注意一个小优化,我们不需要创建一个额外的表作为账户对象的元表;我们可以将 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
时,self
等于 Account
;因此我们可以直接使用 Account
,而不是 self
。但是,在下一节中引入类继承时,使用 self
会非常合适。) 在这段代码之后,当我们创建一个新账户并在其上调用一个方法时,会发生什么?
a = Account:new{balance = 0} a:deposit(100.00)当我们创建这个新账户时,
a
将拥有 Account
(调用 Account: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.__index
也是 Account
(因为新方法执行了 self.__index = self
)。因此,我们可以将之前的表达式重写为
Account.deposit(a, 100.00)也就是说,Lua 调用原始的
deposit
函数,但将 a
作为 self 参数传递。因此,新账户 a
从 Account
继承了 deposit
函数。通过相同的机制,它可以从 Account
继承所有字段。
继承不仅适用于方法,还适用于新账户中不存在的其他字段。因此,类不仅提供方法,还为其实例字段提供默认值。请记住,在我们对 Account
的第一个定义中,我们提供了一个值为 0 的字段 balance
。因此,如果我们在没有初始余额的情况下创建新账户,它将继承此默认值
b = Account:new() print(b.balance) --> 0当我们在
b
上调用 deposit
方法时,它运行等效于
b.balance = b.balance + v(因为
self
是 b
)。表达式 b.balance
求值为零,并且初始存款被分配给 b.balance
。下次我们询问此值时,将不会调用索引元方法(因为现在 b
有自己的 balance
字段)。
版权所有 © 2003–2004 Roberto Ierusalimschy。保留所有权利。 |