一、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())
运行结果