一、元表
在认识lua中的面向对象前,首先要了解元表的概念。
元表使table之间有了父子的概念,当我们子表进行一些特定操作时,会执行元表中的内容。
1.__tostring与__index
print("--元表操作--")
meta = {
--当子表被当作字符串调用时,会默认执行tostring函数
__tostring = function(t)
return t.name
end,
--当子表被当作一个函数使用时,会默认call函数
__call = function(a,b)
print(a)
print(b)
print("子表被当作函数使用")
end
}
mytable = {
name = '我是子表'
}
setmetatable(mytable,meta)--设置元表
print(mytable)
mytable(666);
可以发现当子表被当作函数使用时,也可以传递参数,其中第一个参数默认为自身,后面的参数可通过调用子表函数时传入。
2.运算符重载
利用运算符重载,我们可以直接对两表进行+ - * /等操作,同时也可以进行 = < 等比较。
例如在下面代码中我们可以在__add函数中定义返回值(即定义加法的操作)
print('--运算符重载--')
meta2 = {
--使用+时调用add
__add = function(n1,n2)
return n1.num_2 + n2.num
end
--其他还有 - sub;* mul;/ div;% mod;^ pow;== eq;< lt;<= le;.. concat
}
mytable2_1 = {
num = 1,
num_2 = 2
}
mytable2_2 = {
num = 2
}
setmetatable(mytable2_1,meta2)
setmetatable(mytable2_2,meta2)
print(mytable2_1+mytable2_2)
3.__index指定与__newindex
当子表找不到某一属性时,会自动到__index指定的表中寻找。
print('--指定操作__index')
meta3 = {
age = 1
}
--当子表中找不到某一属性时,回到index指定的表中找属性
meta3.__index = meta3
mytable3 = {}
setmetatable(mytable3,meta3)
print(mytable3.age)
--当赋值时,如果赋值一个不存在的索引,那么会把这个值赋到newindex所指的表中
meta3.__newindex = {}
mytable3.number = 3
print(mytable3.number)--此时新定义的number在__newindex指定的新表中
print(meta3.__newindex.number)
4.其他操作
--获取元表
print(getmetatable(mytable3))
--查看自身是否有属性
print(rawget(mytable3,'age'))
--绕过newindex 直接设置自身变量
rawset(mytable3,'age',6)
print(mytable3.age)
二、面向对象
创建一个表table充当类
Object = {
num = 1,
fun = function()
print("这是父类")
end
}
1.封装
通过__index指向本身,完成类的初始化创建
--------封装-------------
function Object:new()
local obj = {}
self.__index = self
setmetatable(obj,self)
return obj
end
obj = Object:new()
print(obj.num)
输出结果为:1
2.继承
这里主要是通过新建一个表(可以看作类),并同样为它执行元表创建与__index的指定操作来实现继承。有点类似封装,但这里是创建一个新表。(该例子中采用大G表进行对子类的创建)
--------继承-------------
print("继承")
function Object:subClass(className)
_G[className] ={}
local obj = _G[className]
self.__index = self
obj.base = self
setmetatable(obj,self)
end
Object:subClass('son')
print(son.num)
son.fun()
输出结果:1
这是父类
3.多态
在下面的例子中gameobject充当player的父类,并在父类中定义一个test函数,而要实现多态只需在子类中对父类函数进行重写即可。同时也可以看到上述的封装与继承也可以正常使用。
此外,这里在重写父类函数时存在一个坑点,因此我们在执行父类逻辑时,最好不用冒号而是用点然后自己传参数,具体原因可通过下方代码注释理解。
--------多态-------------
print("多态")
Object:subClass("gameObject")
function gameObject:test()
-- body
print("父类test函数")
end
gameObject:subClass("player")
function player:test()
--保留父类
--坑点:这种方法若还有另一个子类p2也执行了修改变量的操作,则修改的变量都是父类的变量
--换而言之就是两个子类间的修改变量的操作会相互影响
--因此执行父类逻辑时,最好不用:而是用. 然后自己传参数
--self.base:test()
--正确的方式
self.base.test(self)
print("子类test函数")
end
local p1 = player:new()
p1:test()
输出结果: