Lua面向对象编程学习笔记
其实Lua中的table是一种对象,因为它跟对象一样,有其自己的操作方法:
Role={hp=100} functionRole.addHp(hp) Role.hp=Role.hp+hp end Role.addHp(50) print(Role.hp)
上面代码创建了一个名为Role对象,并有一个addHp的方法,执行"Role.addHp"便可调用addHp方法。
不过上面对象Role是以全局变量的方式创建,会有一种“全局污染”的威胁,即变量Role在其他地方被重新赋值(例如被赋成nil),对象里的属性或方法可能会面临被销毁或不能正常工作的情况。
对于这种问题,Lua提供一种“接受者”的解决方法,即额外添加一个参数self来表示对象本身:
Role={hp=100} functionRole.addHP(self,hp) self.hp=self.hp+hp end r=Role r.addHP(r,50) print(r.hp)
这样就不怕对象Role被“全局污染”,因为构造了一个子对象r,并以参数的方式传入,以供其方法调用操作。
对于这种把对象本身以参数的方式传入对象方法里的写法,Lua提供了一种更优雅的写法,把点号(.)替换为冒号(:),这样在方法定义或调用时,便可隐藏self参数。修改如下:
Role={hp=100} functionRole:addHp(hp) self.hp=self.hp+hp end r=Role r:addHp(50) print(r.hp)
上面的"r.addHp(50)"的写法等价于"r.addHp(r,50)"
类
Lua没有类的概念,不过可以通过元表(metatable)来实现与原型prototype类似的功能,而prototype与类的工作机制一样,都是定义了特定对象行为。Lua里的原型特性主要使用元表的__index事件来实现,这样当调用对象没定义的方法时,会向其元表的__index键(事件)查找。例如有a和b两个对象,想让b作为a的原型prototype,只需要把b设置为a元表的__index值就行:
setmetatable(a,{__index=b})
这样,当对象a调用任何不存在的成员都会到对象b中查找,a可以拥有或调用b的属性或方法,从某种意义上看,b可以看作是一个类,a是b的对象。
对于上面Role的例子,对象的创建可以用__index元方法来改写,这样新创建的对象就拥有和Role一样的属性和方法。
functionRole:new(o) o=oor{} setmetatable(o,self) self.__index=self returno end
当执行"r=Role:new()"创建一个对象时,r将Role设置为自己的元表,那么调用"r:addHp(50)"的时候,会在r里查找addHp方法,如果没有找到,则会进一步搜索其元表的__index,因此等价于:
getmetatable(r).__index.addHp(r,50)
从上面的Role:new方法可以知道,Role的__index在创建时被指定为self,因此其实就是执行:
Role.addHp(R,50)
完整的类例子:
Role={hp=100} functionRole:new(o) o=oor{} setmetatable(o,self) self.__index=self returno end functionRole:addHp(hp) self.hp=self.hp+hp end r=Role:new() r:addHp(50) print(r.hp)
继承
Lua里继承机制还是像实现类那样实现。
假如打算从类Role派生出一个子类Priest,它有一个魔法属性值mp,那么可以先从类Role构造一个Priest,继承类Role的所有属性和方法:
Priest=Role:new()
虽然Priest是Role的一个实例,不过它具有类Role的所有属性和方法,其实也可以把它看做是从类Role派生出来的类,因此可以从类Priest继续new一个对象出来:
p=Priest:new({mp=100})
上面实例p除了多出一个魔法属性值mp外,还继承类Role的所有属性和方法,当调用"p.addHp"方法时,Lua在p中找不到addHp方法,会到Priest中找,在Priest中找不到,会到Role中找。
因此,想重定义从父类Role继承来的方法,在类Priest上定义即可。假如想重定义addHp方法:每次加血都要先判断魔法值够不够,如果够,则加血,并扣除一定的魔法值。修改如下:
functionPriest:addHp(hp) ifself.mp>=20then self.mp=self.mp-20 self.hp=self.hp+hp end end
这样,当调用"p:addHp"时,Lua会优化取类Priest定义的addHp方法。