Lua本身是没有class之类的关键字的,但是我们可以巧妙利用function也是值和table的特性来实现面向对象的特性。
通过复制表的实现
Lua中的类也是一个table对象,下面我们看看一个简单的类的定义:
People = {}--定义表 People --添加方法的第一种方式
People.talk = function()
print("people talk.")
end --添加方法的第二种方式
function People.say()
print("people say.")
end People.talk()--people talk.
People.say()--people say.
我们发现添加一个方法有两种方式,这两种方式的效果都是一致的,至于使用哪种就看个人喜好了。
通过拷贝创建实例
不知道大家发现没有,上面的示例中并没有创建实例这个步骤,而在面向对象中,类是需要创建实例来使用的,下面我们就用复制的方法来创建实例:
--克隆一个表
function clone(obj)
local instance = {} for key, var in pairs(obj) do
instance[key] = var
end return instance;
end People = {}--定义表 People --定义方法
People.talk = function()
print("people talk.")
end --创建 People 的实例
local p = clone(People)
p.talk()--people talk.
构造函数
我们发现虽然可以创建实例了,但是却没有构造函数,下面我们把构造函数也添加进去:
--克隆一个表
function clone(obj)
local instance = {} for key, var in pairs(obj) do
instance[key] = var
end return instance;
end People = {}--定义表 People --定义构造函数
People.new = function(name)
local ins = clone(People)
ins.name = name;
return ins
end --定义方法
People.talk = function(self)
print(self.name.." talk.")
end --创建 People 的实例
local p = People.new("Li Lei")
p.talk(p)--people talk.
p:talk()--使用 : 号可以省略 self 的填写, 效果和上一行一致
这里引入了一个新的变量self和“:”符号,下面我们来说说:
self
self作为第一个参数表示调用则本身。
.号和:号
调用时为了传递调用者本身,所以第一个参数需要将自身传入,这是.号的写法;
如果使用:号,则默认第一个参数传入调用者本身,无需我们再写第一个参数了,我们可以从第二个参数开始写,但是函数声明时仍然要写第一个参数self的。
定义函数时也可以使用:号进行定义,这样定义方法时也可以省略self参数,但是写法只能写成function tab:foo() end这样的形式,如下:
obj = {name = "Li Lei"}
--.号的写法和调用
obj.foo1 = function()
print "foo1"
end
function obj.foo2(self)
print(self.name.."foo2")
end
obj.foo1()--foo1
obj.foo2(obj)--Li Leifoo2
--:号的写法和定义
--[[注释掉的这种写法是不允许的
obj:foo3 = function()
print(self.name.."foo3")
end
]]
function obj:foo4()
print(self.name.."foo4")
end
obj:foo4()--Li Leifoo4
继承
下面我们来看看如何实现类的继承:
--克隆一个表
function clone(obj)
local instance = {} for key, var in pairs(obj) do
instance[key] = var
end return instance;
end People = {}--定义表 People --定义构造函数
People.new = function(name)
local ins = clone(People)
ins.name = name;
return ins
end --定义方法
People.talk = function(self)
print(self.name.." talk.")
end --定义方法2
People.talk2 = function(self)
print(self.name.." talk2.")
end --创建 People 的实例
local p = People.new("Li Lei")
p.talk(p)--people talk.
p:talk()--使用 : 号可以省略 self 的填写, 效果和上一行一致 --下面是关于继承的实现 --将 origin 表的值复制到 dist 表中
function copy(origin, dist)
for key, var in pairs(origin) do
dist[key] = var
end
end Man = {}--定义表 Man, 继承自 People Man.new = function(name)
local ins = People.new(name)--创建继承的类, 这样就拥有了继承类的所有属性
copy(Man, ins)--将 Man 的属性附加到实例
return ins
end --定义方法
Man.say = function()
print("Man say!")
end --重写方法
Man.talk2 = function(self)
print(self.name.." talk2. (Man)")
end --创建 Man 的实例
local m = Man.new("Han Meimei")
m:talk()--Han Meimei talk.
m:talk2()--Han Meimei talk2. (Man)
m:say()--Man say!
简单来说,就是先创建父类的实例,然后把新类的所有属性都拷贝到刚创建的实例上即可。
通过函数闭包的实现
我们可以通过在函数内定义函数的方式来实现面向对象的特性:
--定义方法直接返回表的实例
function People(name)
local ins = {}--创建表 --初始化函数
local function init()
ins.name = name
end --定义方法
ins.talk = function()
print(ins.name.." talk.")
end --定义带参数的方法
ins.talk2 = function(content)
print(ins.name.." talk "..content..".")
end -- self 可以省去了, 因为可以通过闭包的特性获取到, 或者如果要统一 : 符号可以这么写
ins.talk3 = function(self, content)
print(self.name.." talk "..content..".")
end init()--调用初始化函数 return ins
end --创建对象
p = People("Li Lei")
p:talk()--没有参数可以这么写
p.talk() --p:talk2("hello")--没有 self 参数不能这么调用了
p.talk2("hello") p:talk3("hello 2")
p.talk3(p, "hello 2")
这种写法的运行效率略低于上一种方法,但是却更加清晰,在实际使用时推荐使用该方法来编写类。
继承
使用这种写法也可以实现类的继承,如下:
--定义方法直接返回表的实例
function People(name)
local ins = {}--创建表 --初始化函数
local function init()
ins.name = name
end --定义方法
ins.talk = function()
print(ins.name.." talk.")
end --定义带参数的方法
ins.talk2 = function(content)
print(ins.name.." talk "..content..".")
end -- self 可以省去了, 因为可以通过闭包的特性获取到, 或者如果要统一 : 符号可以这么写
ins.talk3 = function(self, content)
print(self.name.." talk "..content..".")
end init()--调用初始化函数 return ins
end --下面是继承的代码 function Man(name)
local ins = People(name)--关键: 创建一个 People 类的实例 --添加新项目
ins.say = function()
print(ins.name.." say.")
end --覆写老方法
ins.talk = function()
print(ins.name.." talk. (Man)")
end return ins
end m = Man("Han Meimei")
m.say()--Han Meimei say.
m.talk()--Han Meimei talk. (Man)
其实就是创建父类的对象后添加新的属性,比较容易理解。