原文链接:https://www.jianshu.com/p/fefe11d4544e
Lua元表使用 中的__index元方法可以实现面向对象和继承关系:
lua中没有类的概念,只有table,但可以用__index模拟类和对象:
代码语言:javascript复制local A = {}
function A:new(name)
self.__index = self
return setmetatable({
name = name
}, self)
end
function A:print()
print("name is ", self.name)
end
local a = A:new("Tony")
a:print()
-- output: name is Tony
A和a两个表之间的关联是这样的:
这里name是表a的key,print是表A的函数,当用a调用print时,找到的元表A中的__index(指向A自己)中的print方法,而方法内的self是调用者a,所以self.name是a的name。 (Lua中的self)
两个类之间同样可以用__index实现继承关系:
代码语言:javascript复制local Person = {}
function Person:new(name)
self.__index = self
return setmetatable({
name = name
}, self)
end
function Person:print()
print("name is ", self.name)
end
local Student = setmetatable({}, Person)
Student.super = Person
function Student:new(name, score)
self.__index = self
local student = self.super:new(name)
student.score = score
setmetatable(student, self)
return student
end
function Student:print()
self.super.print(self)
print("score is ", self.score)
end
local s = Student:new("Tony", 98)
s:print()
-- output :
-- name is Tony
-- score is 98
用图表示这三个table的关系:
在调用s:print()时,由于s没有print这个函数,找到的s的元表Student的__index(指向Student自己)中的print函数,
首先执行self.super.print(self),这里self是调用者s,s没有super这个属性,同样是找到元表Student中的super即Person,执行Person的print打出name is Tony(这里用点调用函数传入的第一个参数self还是s(Lua中的self)
然后第二行打印score即s.score;
当继承关系比较复杂时,这种调用显得比较混乱且容易出问题,可以封装一个Object基类,实现继承关系链,方便方法调用且减少出问题的几率。
实现面向对象的Object基类:
将设置__index和setmetatable的操作统一写在Object类里,方便使用和减少出错,一共有两处:
实现继承关系时:在Object的方法中实现继承关系(设置__index和元表的一系列操作)
代码语言:javascript复制local Object = {}
Object.__index = Object
function Object:new()
end
function Object:extend()
local SubClass = {}
for k, v in pairs(self) do
if k:find("__") == 1 then
SubClass[k] = v
end
end
SubClass.__index = SubClass
SubClass.super = self
setmetatable(SubClass, self)
return SubClass
end
创建实例对象时:用__call实现构造方法(new方法中的设置元表)
代码语言:javascript复制function Object:__call(...)
local object = setmetatable({}, self)
object:new(...)
return object
end
上面Student的例子可以写成:
代码语言:javascript复制local Person = Object:extend()
function Person:new(name)
Person.super.new(self)
self.name = name
end
function Person:print()
print("name is ", self.name)
end
local Student = Person:extend()
function Student:new(name, score)
Student.super.new(self, name)
self.score = score
end
function Student:print()
Student.super.print(self)
print("score is ", self.score)
end
local s = Student("Tony", 98)
s:print()
-- output :
-- name is Tony
-- score is 98
ps. 要注意的是,在子类调用父类方法时,尽量都是用 ClassName.super 而不要用self.super,因为lua里的self是不确定的。(当传入self调用的父类方法中也有self.super时会进入死循环) metatable:表示文件的元表 元表里记录了 函数、table访问、操作符行为
代码语言:javascript复制local Base = {
--定义要使用的成员变量
_arrData = {}
}
function Base:new(oMataTable )
--实例化后的对象
oMataTable = oMataTable or {}
--将Base的元表 也就是函数说明、操作符、table访问都复制到新对象上
--简单的解释就是 oMataTable 表有了Base的所有函数和table操作
setmetatable(oMataTable, self)
--这句很重要 因为self会根据table自身变化
self.__index = self
return oMataTable
end
function Base:SetData(key, value)
self.arrData[key] = value
print(key, self.arrData[key])
end
function Child:GetData(key)
return self.arrData[key]
end
return Base
代码语言:javascript复制local Child = {
--定义要使用的成员变量
_iData = 11
}
--这句作用就是将Child定义的成员保留下来_iData 然后再继承
Child = require("class.Base"):new(Child)
--覆盖基类函数
function Child:SetData(key, value)
self.arrData[key] = value + self._iData
print(key, self.arrData[key])
end
return Child
代码语言:javascript复制function TestClass()
local base = require("class.Base"):new()
base:SetData(1, 10)
--输出 1,10
local childTest = require("class.Child"):new()
childTest :SetData(1, 10)
--输出 1,21 父类方法被覆盖了
print(childTest :GetData(1))
--输出 20 子类继承了父类方法
end
分析: childTest 又是由Child创建而来,local childTest = require(“class.Child”):new() 这个时候内部的 self 代表的又是 Child 本身而并不是 Base self.__index = self 就指明访问表数据的时候查找 Child 本身 这样一直包装下去就实现了类继承和覆盖