十分鐘學會Lua五:類與繼承的實現
Lua中沒有類,即沒有在物件生成中有模子的概念,但是有原型的概念。基於原型的語言(prototype-based language)中,每個物件可以有一個原型(prototype)。原型也是一種普通的物件,當物件(類的例項)遇到一個未知操作時會首先在原型中查詢。類和原型都是一種組織多個物件間共享行為的方式。
建立原型的方法如setmetatable(A, {__index = B}),即把B作為A的原型。下面的程式碼是簡單表達賬戶存取和提取的操作:
Account = { balance=0}
function Account : withdraw ( v)
self.balance = self.balance – v
end
function Account : deposit (v)
self.balance = self.balance + v
end
現在,需要實現類的概念,即Account作為類,下列程式碼中定義的a作為其物件。
local mt = {__index = Account}
function Account. new (o) --該函式用於新建賬戶,即建立物件
o = o or {f} --如果使用者沒有提供則建立一個新的表
setmetatable(o,mt ) --將mt作為新建立的物件的元表,而元表中的__index為Account類
return o
end
a = Account . new{ balance = 0}
a : deposit ( 100.00 ) --相當於 getmetatable(a).__index . deposit(a,100.00)
對於上述程式碼,我們可以進行一些改進。方法new會變成:
function Account : new ( o)
o = o or {}
self. __index = self
setmetatable(o, self)
return o
end
該改進是,不建立扮演元表角色的新表而是把表Account直接用作元表。第同時對new方法也使用冒號語法。加入了這兩個改動後,現在,當我們定義new ()函式時,隱藏的引數self得到的實參是Account,Account._index等於Account,並且 Account被用作新物件的元表。
若想從Account這個類派生一個子類SpecialAccount以允許客戶透支,那麼可以先建立一個從基類繼承了所有操作的空類:
SpecialAccount = Account :new()
直到現在,SpecialAccount還只是基於Account 類建立的一個物件例項。下面:
s = SpecialAccount : new{limit=1000.00}
SpecialAccount就像繼承其他方法一樣從 Account繼承了new,這是由於基於之前的改進,Account被用作新物件的元表。不過,現在執行new時,它的self引數指向的是SpecialAccount。因此,s 的元表會是 SpecialAccount,即s 繼承自SpecialAccount,而SpecialAccount又繼承自Account。
同時我們可以重新定義子類從基類繼承的任意方法,只需要編寫一個新方法即可,以修改SpecialAccount從Account類繼承的withdraw ()函式為例:
function SpecialAccount : withdraw (v)
if v - self.balance >= self.limit then
error "insufficient funds"
end
self.balance = self.balance – v
end
現在,當呼叫s:withdraw (200.00)時,因為Lua語言會在SpecialAccount中先找到新的withdraw方法,所以不會再從Account中查詢。