我们都知道类具有抽象、封装,继承、多态等特性。
lua中的数据集合都是以表的形式呈现,一个table就是一个类。
而lua中继承的实现以元表和元方法来实现。
下面看下lua中类的简单实现:
---创建类
---@param className string 类名
---@param superClass table|function|nil Class 父类
function Class(className, superClass)
local clazz = {}
clazz._className = className
local superType = type(superClass)
if superType == "function" or superType == "table" then
setmetatable(clazz,{__index = superClass}) --实现继承的关键代码
clazz._super = superClass
else
clazz.Ctor = function () end --提供一个默认的构造函数
end
function clazz.New(...)
local instance = {}
setmetatable(instance,{__index = clazz}) --实现:类的实例访问类的字段或函数
instance._class = clazz
instance:Ctor(...)
return instance
end
return clazz
end
local A = class("A") --实现一个A类
local B = class("b", A) -- B继承A
- 注意:
---------类的实现例子中可以看出,实际上就是两张表的表现,类模板表:clazz ,实例对象表:instance,后续的一切操作都基于这两张表。
这里不得不提一下lua中的 “.” 和 “:”,其中 . 是提取表中元素方法; :在提取表元素的同时,会传递当前作用域。
如定义表方法时:
A.Ctor(A, …)
等同 A:Ctor(…)
下面通过几个例子深入理解一下
示例1:
local A = class("A")
function A:ctor(i)
self.tb = i
end
local ins_1 = A.new(2)
local ins_2 = A.new(3)
-- A:ctor(1)
print("zxf ====== ".. A.tb)
print("zxf ====== ".. ins_1.tb)
print("zxf ====== ".. ins_2.tb)
上面说过,lua中的类就是两张表,模板表(clazz)和实例表(instance),这里分别赋值给了 A 和 ins_1、ins_2。
之后分别打印三张表中的属性tb, 两个实例ins_1、ins_2都能正常打印,A.tb报错提示属性为空不存在。
原因在于 :
实例使用了A表的new方法创建一个实例并返回,
new方法中:
instance:Ctor(...)
相当于ins_1:Ctor(...)
ins_1.Ctor(ins_1, ...)
所以,实例中存在了tb属性。
而A表只存在Ctor方法,并未执行,所以并不存在tb属性,将注释A:Ctor(1)释放,即可打印出A的tb属性。
示例2
深入理解了这个示例,也就清楚了lua继承的使用,也就是几张表与作用域的关联
-- 元表继承的理解
local A = class("A")
function A:ctor(i)
self.tb = i
end
local B = class("B", A)
function B:ctor(i)
self.super.ctor(self,i)
end
-- 打印A表属性tb
A:ctor(1)
B.super:ctor(1)
B.super.ctor(A, 1) -- 这种写法有点多余
print("zxf ====== ".. A.tb)
-- 打印B表属性tb
B:ctor(2)
B.ctor(B,2)
A.ctor(B,2)
print("zxf ====== ".. B.tb)
-- 打印A类实例属性tb
local ins_1 = A.new(3)
print("zxf ====== ".. ins_1.tb)
-- 利用A实例打印A表属性tb
local ins_1 = A.new(3) -- 这里必须要设置,才能区分ins_1表、A表自己的值,不然会顺着元表自动找值(元表的特性),导致打印的都是A表的属性tb
ins_1.ctor(A,5) -- 不必要的写法 完全等同于 A:ctor(5)
print("zxf ====== ".. ins_1.tb)
-- 打印B类实例属性tb
local ins_B_1 = B.new(5)
local ins_B_2 = B.new(6)
print("zxf ====== ".. ins_B_1.tb)
print("zxf ====== ".. ins_B_2.tb)
-- 利用B类实例打印B类属性tb
local ins_B_1 = B.new(5)
B:ctor(50)
-- 实例和B表 必须要设置,才能区分ins_B_1表、B表、A表自己的值,不然会顺着元表自动找值(元表的特性),导致打印的都是A表的属性tb
ins_B_1.super:ctor(100) --B类实例中不存在super,会去元表B中找,找到super,而ins_B_1.super == A,所以这里执行的是A:ctor(100)
print("zxf ====== ".. ins_B_1.tb)
print("zxf ====== ".. B.tb)
print("zxf ====== ".. A.tb)
ins_B_1.super.ctor(B, 78) -- 使用A表方法更改B表的属性tb
A.ctor(B, 78)
print("zxf ====== ".. B.tb)
print("zxf ====== ".. A.tb)
看懂了美~~~