一、Lua表的self标识
在lua中,表拥有一个标识:self。self类似于c++中的this指针和python中的self。在lua中,提供了冒号操作符来隐藏这个参数,例如:
t1 = { id = 1, name = "panda", addr = "beijing" } -- 使用冒号语法实现 function t1:getId() return self.id end function t1:setId(val) self.id = val end t1:setId(10) print(t1:getId()) -- 使用点语法实现 function t1.getName(obj) -- 需要将表作为参数传入 return obj.name end function t1.setName(obj, val) obj.name = val end t1.setName(t1, "mark") print(t1.getName(t1))
运行结果:
冒号只是一种语法机制,提供的是便利性,并没有引入任何新的东西。使用冒号完成的事情,都可以使用点语法来完成。
两者有什么区别呢?
- lua 为冒号提供了独有的指令:self
- 从用法上来说,使用点语法实现需要传入表对象,而冒号就显得较为简洁了。
二、自索引
t2 = {id = 2, name = "lwang", age = 18} meta_t2 = { -- __index = meta_t2, -- 为什么写在这里不行,因为此时 meta_t2 还不存在,所以是nil addr = "shenzhen" } meta_t2.__index = meta_t2; -- 自索引,自己索引自己 setmetatable(t2, meta_t2) print(t2.addr)
运行结果:
三、自索引实现继承
father = {a = 1, b = 2} function father:fatherSayHello() print("father say hello: ", father.a, father.b) end father.fatherSayHello() father.__index = father son = {a = 10, b = 20} function son:sonSayHello() print("son say hello") end son.sonSayHello() setmetatable(son, father) -- 设置元表 son.sonSayHello() son.fatherSayHello() --子类son有元表,并且有__index = father,指向父类的表,调用父类的方法
运行结果:
四、Lua 模拟类的实例化(带参构造)
-- 4. 面向对象 类的实例化(带参构造) stuInfo = { id = 1, name = "panda", age = 18 } -- stuInfo stu1 stu2 都是同一张表 stu1 = stuInfo stu2 = stu1 print(stuInfo, stu1, stu2) print(stuInfo.id, stu1.id, stu2.id) stuInfo.id = 100 print(stuInfo.id, stu1.id, stu2.id) -- 都会休改为:100 print("") stuInfo.__index = stuInfo -- 设置自索引 function stuInfo:newInstance(tab) obj = tab or {} setmetatable(obj, self) -- 为obj设置元表为self 也就是stuInfo 谁调用newInstance,self就是谁 -- self.__index = self return obj end -- stu3 接收newInstance出来的实例 stu3 = stuInfo:newInstance({id = 3, age = 20}) print("stu3: ", stu3.id, stu3.name, stu3.age) -- name会使用stu3 元表也就是stuInfo中的name的值 print("") stu4 = stu3:newInstance() print("stu4: ", stu4.id, stu4.name, stu4.age) -- 输出 nil nil nil print("") stu3.__index = stu3 -- 设置自索引 stu5 = stu3:newInstance({id = 1111}) print("stu5: ", stu5.id, stu5.name, stu5.age) -- 输出 nil nil nil print("")
运行结果:
结果分析:
为什么stu3可以正常输出,stu4为什么输出都是nil?
因为:stu3 = stuInfo:newInstance({id = 3, age = 20}) ,stu3 在实例化的过程中,传了参数:id 和 age 所以,id 和 age 直接输出3 和 20 没有问题,在stu3输出name的时候,因为stu3 没有name索引,会找自己的元表,也就是stuInfo,触发 stuInfo的__index因为stuInfo.__index 指向的是自己 (设置了自索引 stuInfo.__index = stuInfo),所以会到stuInfo表中找name 找到 name = “panda” 输出:panda
而,stu4是通过stu3实例化出来的(stu4 = stu3.newInstance())且没有传递参数,在输出stu4的id name 和 age 时,本身都没有,就会找stu4的元表,stu4的元表是stu3。 关键就在于 stu3 没有__index元方法,所以stu4输出的都是nil
如何让stu4 正常输出呢?两种解決方法:
-- 1. 为stu3 设置自索引 stu3.__index = stu3 -- 2. 直接在newInstance 方法中 通过 self.__index = self 为调用者设置自索引 function stuInfo:newInstance(tab) obj = tab or {} setmetatable(obj, self) -- 为obj设置元表为self 也就是stuInfo 谁调用newInstance,self就是谁 self.__index = self -- 为调用者设置自索引 return obj end
五、多重继承
grandfather = {id = 1, name = "grandfather"} grandfather.__index = grandfather father = {name = "father"} father.__index = father -- 设置father的元表为grandfather setmetatable(father, grandfather) son = {name = "son"} -- 设置son的元表为father setmetatable(son, father) print(grandfather.id, father.id, son.id) -- 输出: 1 1 1 print(grandfather.name, father.name, son.name) -- 输出: grandfather father son
运行结果:
结果分析:
先分析id:
- grandfather.id 因为grandfather有id 所以直接输出1
- father.id 因为father没有id索引,会找fahter的元表,也就是grandfather,触发grandfather的__index 此时__index指向grandfather本身,grandfather本身就是一张表,就会找自己有没有id索引,找到id = 1返回
- son.id 因为son没有id索引,会找son的元表,也就是father,触发father的__index 此时__index指向father本身, father本身就是一张表,就会找自己有没有id索引,找到id = 1返回, 没有继续找会找fahter的元表,也就是grandfather,触发grandfather的__index 此时__index指向grandfather本身,grandfather本身就是一张表,就会找自己有没有id索引,找到id = 1返回
分析name:
name 索引因为都有,所以正常输出
六、子类重写父类方法
stuInfo = { id = 1, name = "panda", age = 18, say = function() print("stuInfo say.") end } function stuInfo:newInstance(tab) obj = tab or {} setmetatable(obj, self) -- 为obj设置元表为self 也就是stuInfo 谁调用newInstance,self就是谁 self.__index = self -- 设置自索引 return obj end function stuInfo:getId() return self.id end function stuInfo:setId(newId) self.id = newId end stu1 = stuInfo:newInstance() stu2 = stu1:newInstance() print(stuInfo, stu1, stu2) print(stuInfo.id, stu1.id, stu2.id) -- 如果子类没有赋值,默认会使用父类的值;如果父类的成员或方法发生变化会影响子类,前提是子类没有重写 stuInfo.age = 25 print(stuInfo.age, stu1.age, stu2.age) stu1.id = 100 -- stu1以下都会改变 这就是重写 print(stuInfo.id, stu1.id, stu2.id) print("") stuInfo.say() stu1.say() stu2.say() print("") function stu1:say() -- stu1 重写say方法 print("stu1 say.") end stuInfo.say() stu1.say() -- stu1 调用自己的say方法 stu2.say() -- stu2 调用stu1的say方法
运行结果
七、成员私有化
function personInfo() -- 将成员属性隐藏起来 local members = { id = 1, name = "lwang", age = 25 } local function getId() return members.id end local function getName() return members.name end local function setId(id) members.id = id end local function setName(name) members.name = name end -- 对外暴露的方法 return { getId = getId, setId = setId, getName = getName, setName = setName } end p1 = personInfo() print(p1.getId(), p1.getName()) p1.setId(22) p1.setName("mark") print(p1.getId(), p1.getName())
运行结果