Lua实现面向对象
前文
市面上已经有非常多的lua实现面向对象的相关文章了,本文也不希望再重复赘述该怎么实现一个面向对象。而是记录下自己实现面向对象的全部流程,以及踩到的坑点和对应的解决的方法,本文将一直更新下去,直到实现个人认为最完美的Lua面向对象为止。
第一版
function class(classname, super)
local cls = { __name = classname, __super = super }
if super then
setmetatable(cls, { __index = super })
end
function cls:new(...)
-- 创建新实例
local obj = {}
setmetatable(obj, {__index = self})
-- 调用构造方法
if self.ctor then
self.ctor(obj, ...)
end
return obj
end
return cls
end
- 新生成一张表cls(可以理解成CLR的类型对象),给cls添加一个元表,元方法__index 赋值为 super(父类型对象),在cls中找不到的key,会到super中去寻找。
- 给cls新增一个方法new(),该方法返回cls类型对象的一个实例,并主动调用构造方法ctor,初始化实例对象中的成员
-- 使用方法展示
local Parent = class("Parent", Grandfather)
function Parent:ctor(name)
self.parentName = name
end
local Child = class("Child", Parent)
function Child:ctor(name)
self.__super:ctor(name)
self.childName = name
end
local A = Child:new('A')
local B = Child:new('B')
print(A.childName) -- 输出:A
print(B.childName) -- 输出:B
可以看到,A和B已经是2个不同的实例对象,我们给A和B的childname进行不同的赋值,最后输出的结果也是不相同的赋值,是正确的。然后我们尝试输出下父类中的parentName值试试,看看有没有什么不同:
print(A.parentName) -- 输出:B
print(B.parentName) -- 输出:B
我去,异常的情况发生了,可以看到B实例“修改”了A实例的parentName,那么这个是怎么造成的呢,我们来分析一下:

我们在对Child实例化时,调用了self.__super:ctor(name),其中的self.__super其实是Parent类型对象,那么我们修改parentName时,其实修改的是Parent类型对象中的值,而非Child实例的值。【说的比较拗口,但懂的人自然懂了。如果读者不明白类型对象和类型对象实例的区别,请自行查阅一下】
这也是为什么2个实例都输出了“B”,那么我们该怎么解决这个问题呢?
我们回想下CLR在实例化一个对象的时候,它做了什么?它会计算类型及其所有基类型(一直到System.Object)中定义的所有实例字段需要的字节数。在托管堆上分配所需的空间,最后对所有实例字段初始化。我们这里也正是少了在实例中分配基类型实例字段空间的过程,由此,我衍生出第二版来修复这个问题。
第二版
function class(classname, super)
local cls = { __name = classname, __super = super }
-- 继承父类方法
if super then
setmetatable(cls, { __index = super })
end
-- 实例的元表指向类
cls.__index = cls
-- 构造函数(自动克隆类字段)
function cls:new(...)
local obj = {}
setmetatable(obj, self)
-- 链式调用构造函数
local function call_ctor(class, ...)
if class.__super then
call_ctor(class.__super, ...)
end
if rawget(class, "ctor") then
class.ctor(obj, ...)
end
end
call_ctor(self, ...)
return obj
end
return cls
end
- 新生成一张表cls(可以理解成CLR的类型对象),给cls添加一个元表,元方法__index 赋值为 super(父类型对象),在cls中找不到的key,会到super中去寻找。
- 给cls新增一个方法new(),该方法返回cls类型对象的一个实例。
- new()方法中会自低向上的调用构造函数,并将实例传递进行,进行实例初始化
-- 使用方法展示
local Parent = class("Parent", Grandfather)
Parent.defaultParentName = "defaultParentName"
function Parent:ctor(name)
self.parentName = name
end
local Child = class("Child", Parent)
function Child:ctor(name)
self.__super:ctor(name)
self.childName = name
end
local A = Child:new('A')
local B = Child:new('B')
print(A.childName) -- 输出:A
print(B.childName) -- 输出:B
print(A.parentName) -- 输出:A
print(B.parentName) -- 输出:B
此时我们可以看到,无论是childName还是parentName都输出了正确的结果。

浙公网安备 33010602011771号