Lua元表使用 中的__index元方法可以实现面向对象和继承关系:
- lua中没有类的概念,只有table,但可以用__index模拟类和对象:
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两个表之间的关联是这样的:
(Lua中的self)
- 两个类之间同样可以用__index实现继承关系:
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和元表的一系列操作)
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方法中的设置元表)
function Object:__call(...)
local object = setmetatable({}, self)
object:new(...)
return object
end
上面Student的例子可以写成:
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时会进入死循环)Lua中的self