- 在Lua中如何模拟“继承”
方法是通过元表来模拟“继承”。如下所示,先定义一个A作为父类;再实现New方法,该方法主要是将A设置为o的元表,然后返回o;最后将A:New返回的值赋值给B,此时B就是继承于A。在New中有一个关键的一步是self.__index = self,这一步必不可少,原因是当你通过Key来访问表中的值,若表中该Key为nil,那么Lua会寻找元表中__index键;__index键如果是function则会直接执行,若__index键为表,则会在该表中寻找Key值。
A = {}
function A:New()
local o = {}
setmetatable(o, self)
self.__index = self
return o
end
B = A:New()
- 在“继承”中该值是想象中的值吗
A = {Num = 1}
function A:New()
local o = {}
setmetatable(o, self)
self.__index = self
return o
end
B = A:New()
(1)B.Num = 2
(2)B.Num = nil
打印B.Num按照常规的继承思路(1) Num = 2, (2) Num 是一个空值。然而并非如此,(1) Num = 2, (2) Num = 1。其实使用元表不是真正意义上的继承,元表的概念只是简单的将两张表关联起来,通过元方法来表操作两表。B.Num = 2 其实不是操作A表中的Num,而是在B表中新建一个Key叫Num并且赋值为2。B.Num = nil 是将B表中的Num删除,再根据模拟“继承”的理论来推,当B表中的Key为空时,会去寻找元表中__index赋值的表Key的值。
- 小测验
A = {Num1 = 1}
function A:New()
local o = {}
setmetatable(o, self)
self.__index = self
o.Num2 = 1
self.Num3 = 1
return o
end
B = A:New()
B.Num4 = 1
问:Num1、Num2、Num3、Num4分别属于谁的表
答:Num1、Num3为A表中的Key,Num2、Num4为B表中的值。
故:在模拟“继承”的时候,要格外注意该Key属于谁,在操作谁的Key,当Key为nil的时候,再次使用是否会读取不想要的值
思考:如果Key为nil,元表中同名的Key不nil,使用if判断是否会判空。
- 在“继承中”函数里的self.Value是谁
A = {Num = 1}
function A:New()
local o = {}
setmetatable(o, self)
self.__index = self
return o
end
function A:Init()
self.Num = 2
end
B = A:New()
(1)B:Init()
(2)B.Num = nil
按照上述的思路来看(1)Num = 2, (2)Num = 2。然后并非如此,(1)Num = 2,(2)Num = 1 首先B:Init(),B表没有Init函数,按照上述理论,应该是调用A:Init函数,self.Num = 2是操作A表中Num;这是错误的想法。首先要明确Lua中的:操作符表明了什么,表明了在调用的时候将会调用者传入为self。打印下A,B,A:Init中的self;会发现地址B和self地址相同。所以B:Init()调用时,self是指B。
- 小测验
A = {Num = 1}
function A:New()
local o = {}
setmetatable(o, self)
self.__index = self
return o
end
function A:Init(c)
self.Num = c
end
B = A:New()
C = A:New()
B.Init(C,3)
问:A.Num, B.Num, C.Num 分别是多少
答:A.Num为1, B.Num为1, C.Num为3
故:Lua函数调用中:的操作会默认将调用者传入函数,而B.Init(C,3)是显示的将默认参数给传入C,所以C是self
思考:如果函数直接定义为A.Init效果会如何呢