Chapter 16_1 Class

  一个类就是一个创建对象的模具。对于一些基于原型的语言,对象是没有“类型”的,而是每个对象都有一个原型(prototype).

原型也是一种常规的对象。当其他对象(类的实例)遇到一个未知操作时,原型会先查找它。这种语言要创建一个类,只需要创建一个专用作其他对象的原型。

类和原型都是一种组织对象间共享行为的方式。

  在Lua中,可以用继承实现原型。如果有两个对象a和b,要让b作为a的原型:

setmetatable(a, {__index = b})

在此之后,a就会在b中查找所有它没有的操作。将b称为是对象a的类,只不过是术语上的一个变化。

回到上面的Account例子中,为了创建更多与Account行为类似的账号,可以让这些新对象从Account行为中继承这些操作。

具体做法是使用__index元方法。可以用一项小优化,则无须创建一个额外的table作为账户对象的元表。而是使用Account table自身作为元素:

function Account:new(o)
    o = o or {}    --如果用户没有提供,就创建一个空表
    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作为元表,当调用a:deposit(100.00)时,就是调用了a.deposit(a, 100.00).

当Lua无法在table  a中找到条目deposit时,会进一步搜索元表的__index条目。

getmetatable(a).__index.deposit(a, 100.00)

a的元表是Account,Account.__index也是Account。因此,上面的表达式可以简化为:

Account.deposit(a , 100.00)

结果为Lua调用了原来的deposit函数,但传入a作为self参数。因此新账号a从Account继承了deposit函数。

  继承不仅可以作用于方法,也可以作用于所有其他在新账户中没有的字段。

比如上面的balance field。如果在创建新账户时没有提供balance的初值,那么它就会继承这个默认值。

b = Account:new()
print(b. balance)        --> 0

在b上调用deposit时,self就是b,相当于:

b.balance = b.balance + v

在第一次调用deposit时,对b.balance的求值结果为0,然后一个初值被赋予了b.balance。

后续对b.balance的访问就不会再涉及到__index元方法了,因为此时b已有自己的balance字段。

以上内容来自:《Lua程序设计第二版》和《Programming in Lua  third edition 》

posted @ 2016-09-18 17:34  daiker  阅读(125)  评论(0编辑  收藏  举报