Lua从函数到类
第一部分 函数使用
函数调用与语法糖:
[code]local Monster = { name = "Monster", HP = 100, pos = {x = 100, y = 200} } function Monster:TakeDamage(damage) --等于Monster.TakeDamage = function(self, damage) self.HP = self.HP - damage end print(Monster.HP) Monster:TakeDamage(10) --等于Monster.TakeDamage(Monster, 10) print(Monster.HP)
其他对象调用函数:
[code]monster1 = {HP = 200} print(monster1.HP) Monster.TakeDamage(monster1, 10) print(monster1.HP)
将TakeDamage函数写在内部:
[code]local Monster = { name = "Monster", HP = 100, pos = {x = 100, y = 200}, TakeDamage = function(self, damage) self.HP = self.HP - damage end } print(Monster.HP) Monster:TakeDamage(10) --等于Monster.TakeDamage(Monster, 10) print(Monster.HP) monster1 = {HP = 200} print(monster1.HP) Monster.TakeDamage(monster1, 10) print(monster1.HP)
已经有一点像面向对象的形式了。
但如果只是a对象=b对象,会发现,两者指向同样的地址,操作的是同样的内容:
[code]local monster1 = Monster monster1.HP = 200 print(Monster.HP) --200 print(monster1.HP) --200 monster1:TakeDamage(10) print(Monster.HP) --190 print(monster1.HP) --190
第二部分 metatable、__index与__newindex
于是需要引入云表meta table的概念。另一个例子起始如下:
[code]CMonster = { name = "Monster", HP = 100, TakeDamage = function(self, damage) self.HP = self.HP - damage end } objMonster = {} print(objMonster.HP)--nil objMonster2 = { HP = 10 } print(objMonster2.HP)--10
我们希望对象有自己的属性的时候,使用自己的,没有,则使用CMonster的。于是要设置云表:
[code]objMonster = {} setmetatable(objMonster, {__index = CMonster}) print(objMonster.HP)--100
函数setmetatable()把后面设置为前面的云表,即后面的表定义了前面的表的一些行为,比如索引(即按照前面设定,想办法输出索引为"HP"的元素)。
[code]objMonster = { } setmetatable(objMonster, { __index = CMonster }) print(CMonster.HP)--100 print(objMonster.HP)--100 objMonster.HP = 123 print(CMonster.HP)--100 print(objMonster.HP)--123 objMonster.HP = 456 print(CMonster.HP)--100 print(objMonster.HP)--456
通过这样的方法,看上去是解决了第一部分例子中的问题。
但是,objMonster.HP = 123调用的是__newindex而不是__index。访问、查询用的是__index定义的方法或者表,改值、新增加值用的是__newindex。
[code]objMonster = { } setmetatable(objMonster, { __index = CMonster, __newindex = CMonster }) print(CMonster.HP)--100 print(objMonster.HP)--100 objMonster.HP = 123 print(CMonster.HP)--123 print(objMonster.HP)--123 objMonster.HP = 456 print(CMonster.HP)--456 print(objMonster.HP)--456
这样就类似类的静态变量。
第三部分 类的构建与优化
根据上个例子进行修改:
[code]CMonster = { name = "Monster", HP = 100, TakeDamage = function(self, damage) self.HP = self.HP - damage end, ShowInfo = function(self) print("name:", self.name, "HP:", self.HP) end } function CMonster:new(name,hp, x, y) local obj = {} obj.name = name obj.HP = hp obj.x = x obj.y = y setmetatable(obj, {__index = CMonster}) return obj end local obj2 = CMonster:new("monster2", 20, 100, 200) obj2:ShowInfo()--20 obj2:TakeDamage(20) obj2:ShowInfo()--0 local obj3 = CMonster:new("monster3", 10, 200, 500) obj3:ShowInfo()--10 obj3:TakeDamage(5) obj3:ShowInfo()--5
这样已经很接近类的样子了。
这里有一个非常容易出现的错误:attempt to index a nil value (local 'self')
obj2.ShowInfo()改为obj2:ShowInfo()(上面已经修正)
再次改动,{__index = CMonster}等于额外构建了一张表,obj将这个额外构建的表作为云表。可以优化如下:
[code]CMonster = { name = "Monster", HP = 100, TakeDamage = function(self, damage) self.HP = self.HP - damage end, ShowInfo = function(self) print("name:", self.name, "HP:", self.HP) end } CMonster.__index = CMonster function CMonster:new(name,hp, x, y) local obj = {} obj.name = name obj.HP = hp obj.x = x obj.y = y setmetatable(obj, CMonster) return obj end
上面已经是可以使用的版本了。
再次改写,靠近其他语言类的构建方法,可以利用__call函数在调用时执行。如下:
[code]CMonster = { name = "Monster", HP = 100, TakeDamage = function(self, damage) self.HP = self.HP - damage end, ShowInfo = function(self) print("name:", self.name, "HP:", self.HP) end } CMonster.__index = CMonster function CMonster:new(name,hp, x, y) local obj = {} obj.name = name obj.HP = hp obj.x = x obj.y = y setmetatable(obj, CMonster) return obj end setmetatable(CMonster, {__call = function(self, name, hp, x, y) return self:new(name,hp,x,y) end }) local obj2 = CMonster("monster2", 20, 100, 200) obj2:ShowInfo()--20 obj2:TakeDamage(20) obj2:ShowInfo()--0 local obj3 = CMonster("monster3", 10, 200, 500) obj3:ShowInfo()--10 obj3:TakeDamage(5) obj3:ShowInfo()--5
第四部分 继承
去掉测试部分代码,开头加上local并在最后加上return CMonster,形成文件。如下:
[code]local CMonster = { name = "Monster", HP = 100, TakeDamage = function(self, damage) self.HP = self.HP - damage end, ShowInfo = function(self) print("name:", self.name, "HP:", self.HP) end } CMonster.__index = CMonster function CMonster:new(name,hp, x, y) local obj = {} obj.name = name obj.HP = hp obj.x = x obj.y = y setmetatable(obj, CMonster) return obj end setmetatable(CMonster, {__call = function(self, name, hp, x, y) return self:new(name,hp,x,y) end }) return CMonster
在新文件中,引用其他lua文件使用require。如上段代码文件名为2.lua,则使用方法如下:
[code]local CM = require("2") local cm1 = CM:new("monster1",123,2,3) cm1:ShowInfo()
可以使用print(package.path)打印引用路径。说白了,就是在这些目录下寻找2.lua文件。
另外注意,如果local CMonster不加上local,那么local cm1 = CMonster:new("monster1",123,2,3)也可行,其原因是2.lua文件中,其未被规定为局部变量。这容易出现问题,所以变量作用域一定要控制。
[code]local CM = require("2") local cm1 = CM:new("monster1",123,2,3) cm1:ShowInfo() local MagicMonster = { MP = 200, Attack = function(self) print("Attack..") self.MP = self.MP - 10 end } setmetatable(MagicMonster, { __index = CM, __call = function(self,name,hp,mp,x,y) local obj = CM(name,hp,x,y) setmetatable(obj, {__index = MagicMonster}) return obj end }) local obj2 = MagicMonster("m1", 800, 22, 33) obj2:ShowInfo() obj2:TakeDamage(33) obj2:ShowInfo()
继承类如上所示。外层setmetatable是MagicMonster的__index指向CM(即CMonster),同时调用时执行这个匿名函数。该匿名函数其实就是MagicMonster的构造函数,等于不再写MagicMonster:new()函数了。内层setmetatable是obj的__index指向MagicMonster。
[code]function MagicMonster:ShowInfo() print("MagicMonster", self.name, self.HP) end
优化补充:这种可以在构造时,提前传入一个表,构成某些属性。
[code]function ClassA:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end
第五部分 私有成员
通过封装的方式实现私有:
[code]function GetMonster() local self = {HP = 123, Name = "monster", otherdata = {a=1,b=2}} local function GetHP() return self.HP end local function TakeDamage(_,count) self.HP = self.HP - count end local function GetName() return self.Name end return {GetHP = GetHP, TakeDamage = TakeDamage, GetName = GetName} end local monster = GetMonster() print(monster:GetHP())--123 monster:TakeDamage(20) print(monster:GetHP())--103 monster.HP = 10000 --等于在返回的表里增加了一个字段 但对原本的表没有影响 print(monster:GetHP())--103
这种方法对编程不友好。另一种方法是使用原表,把__index、__newindex指向不同的地方。
[code]local Monster = {} Monster.HP = 100 Monster.x = 123 Monster.y = 456 Monster.Type = "Monster" function Monster:GetHP() return self.HP end function Monster:TakeDamage(count) self.HP = self.HP - count end function Monster:SetHP(hp) self.HP = hp end function Monster.new() local obj = { HP = Monster.HP} setmetatable(obj, Monster) Monster.__index = {GetHP = Monster.GetHP, TakeDamage = Monster.TakeDamage, Type = Monster.Type} Monster.__newindex = function(tab,key,value) if key == "Type" then print("Forbiden operate.") return end rawset(tab,key,value) end return obj end return Monster
[code]local CMonster = require("4") local obj = CMonster:new() print(obj:GetHP())--100 obj:TakeDamage(33) print(obj:GetHP())--67 --obj:SetHP(200) --报错 print(obj.Type) obj.Type = "Mon" --显示Forbiden operate. print(obj.Type) --未修改 print(obj.z) --nil obj.z = 10 print(obj.z) --10 增加字段不影响
print(obj:GetHP())、obj:TakeDamage(33)、print(obj.Type)对应__index的三条。
obj:SetHP(200)不在__index,所以报错。
1c6f4
obj.Type = "Mon"、obj.z = 10对应__newindex,前者报错直接返回了,后者执行rawset(tab,key,value)对表进行了修改。
- 点赞
- 收藏
- 分享
- 文章举报
- lua学习之函数篇
- xLua中调用C#中操作方法(无返回值,没有ref out修饰参数 最终修改参数值得方法)
- Lua math数学函数库 对于取整存在误差的情况(例如math.modf())
- openresty安装LuaXml
- lua web 开发
- Lua面向对象~创建类以及其实例的概念
- 转载:Valuation Method,评估方法
- C语言调用Lua脚本
- Wow 插件开发和 Lua语言的研究
- lua_table 学习
- Xlua热更新
- Lua 安装
- 浅析android手游lua脚本的加密与解密
- EvaluateExpression 伟大而卑微的发现
- EvaluateExpression 2.0
- 「Codeforces」742D Arpa's weak amphitheater and Mehrdad's valuable Hoses (背包、并查集)
- Beginning Lua with WoW Addons - The TOC File
- Unable to evaluate expression using this context
- Lua中的迭代器
- Lua实现继承