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