Lua没有class系统,可是它强有力的元程序设计使定义一个类变的容易。实际上,有不少方式去实现类定义,只是刚开始的时候可能会由于Lua陌生的符号而对Lua的面向对象感到困惑。函数
这里描述的方法是最普通和灵活的,那就是用元表。table的行为能够经过给它设置一个元表并定义一些元方法来自定义。例如,一个元表有__index函数,那么任何一次对表中元素失败的查询都会传递给__index函数。若是__index是一张表的话,那么会以一样的方式对这张表进行查询。(具体请参考《Lua程序设计第二版》第13章 元表与元方法)ui
这里是基本的用法:google
1 Account = {} 2 Account.__index = Account 3 4 function Account.create(balance) 5 local acnt = {} -- our new object 6 setmetatable(acnt,Account) -- make Account handle lookup 7 acnt.balance = balance -- initialize our object 8 return acnt 9 end 10 11 function Account:withdraw(amount) 12 self.balance = self.balance - amount 13 end 14 15 -- create and use an Account 16 acc = Account.create(1000) 17 acc:withdraw(100)
在这里,acc就是一个只包含 balance 这个元素的表。Lua试图去访问acc中的 withdraw 方法,是访问不到的,可是 acc 有元表而且定义了 __index 方法,那么将会去这个元表(Account)里去访问 withdraw 方法。因此 acc:withdraw(100) 其实是调用的 Account.withdraw(acc, 100)。(关于 . 和 : 的区别请自行google)。咱们也能将 withdraw 直接放到 acc 中去,可是那样太浪费,也不灵活,好比添加一个方法须要去改变 create 方法。lua
了解了上面类的定义后,下面,我将定义一个 class() 函数去完成全部这些(甚至更多)转换。spa
1 Account = class(function(acc,balance) 2 acc.balance = balance 3 end) 4 5 function Account:withdraw(amount) 6 self.balance = self.balance - amount 7 end 8 9 -- can create an Account using call notation! 10 acc = Account(1000) 11 acc:withdraw(100)
在这段代码中,提供了一个初始化函数去建立类,一个”构造函数“自动生成了。翻译
同时支持简单的继承,例如,这里基类 Animal 已经定义了,而且声明了一些不一样的子类。全部用 class() 函数生成的类都有一个 is_a() 的方法,去惟一肯定一个子类。设计
1 -- animal.lua 2 3 require 'class' 4 5 Animal = class(function(a,name) 6 a.name = name 7 end) 8 9 function Animal:__tostring() 10 return self.name..': '..self:speak() 11 end 12 13 Dog = class(Animal) 14 15 function Dog:speak() 16 return 'bark' 17 end 18 19 Cat = class(Animal, function(c,name,breed) 20 Animal.init(c,name) -- must init base! 21 c.breed = breed 22 end) 23 24 function Cat:speak() 25 return 'meow' 26 end 27 28 Lion = class(Cat) 29 30 function Lion:speak() 31 return 'roar' 32 end 33 34 fido = Dog('Fido') 35 felix = Cat('Felix','Tabby') 36 leo = Lion('Leo','African') 37 38 39 40 D:\Downloads\func>lua -i animal.lua 41 > = fido,felix,leo 42 Fido: bark Felix: meow Leo: roar 43 > = leo:is_a(Animal) 44 true 45 > = leo:is_a(Dog) 46 false 47 > = leo:is_a(Cat) 48 true
全部的 Animal 子类都定义了 __tostring 这个元方法,任什么时候候 Lua 在操做 Animal 子类的时候有用到 tostring() 这个函数时都会访问到这个元方法。另外一方面,它依赖于 speak 函数,这是在子类中没有定义的。全部者就是 C++ 使用者说的抽象基类。具体的派生类,好比 Dog ,本身定义 speak 。须要注意的是,若是派生类有本身的初始化函数,他们必须显式的调用基类的初始化函数。code
class() 有两个技巧。因为给这个类的元表设置了 __call 方法,它容许你直接调用 class() 去建立一个类,这个主要是用来复制基类中的变量元素。这不是惟一的方式去实现继承,咱们也能经过设置 __index 来让子类在访问函数方法的时候来到基类的方法中,这种方式要好得多,精简了类对象的数据。每一个派生类要去持有一个 _base 数据,用来实现 is_a 方法。对象
能够看到,在运行时修改了基类并不会影响到它的子类。blog
1 -- class.lua 2 -- Compatible with Lua 5.1 (not 5.0). 3 function class(base, init) 4 local c = {} -- a new class instance 5 if not init and type(base) == 'function' then 6 init = base 7 base = nil 8 elseif type(base) == 'table' then 9 -- our new class is a shallow copy of the base class! 10 for i,v in pairs(base) do 11 c[i] = v 12 end 13 c._base = base 14 end 15 -- the class will be the metatable for all its objects, 16 -- and they will look up their methods in it. 17 c.__index = c 18 19 -- expose a constructor which can be called by <classname>(<args>) 20 local mt = {} 21 mt.__call = function(class_tbl, ...) 22 local obj = {} 23 setmetatable(obj,c) 24 if init then 25 init(obj,...) 26 else 27 -- make sure that any stuff from the base class is initialized! 28 if base and base.init then 29 base.init(obj, ...) 30 end 31 end 32 return obj 33 end 34 c.init = init 35 c.is_a = function(self, klass) 36 local m = getmetatable(self) 37 while m do 38 if m == klass then return true end 39 m = m._base 40 end 41 return false 42 end 43 setmetatable(c, mt) 44 return c 45 end