第一版是為 Lua 5.0 編寫的。雖然在很大程度上仍然與後續版本相關,但仍有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您也可以協助 支援 Lua 專案。
![]() |
用 Lua 程式設計 | ![]() |
第二部分。表格和物件 第 16 章。物件導向程式設計 |
類別用於建立物件的模型。許多 OO 語言提供類別的概念。在這些語言中,每個物件都是特定類別的執行個體。Lua 沒有類別的概念;每個物件都定義自己的行為,並有自己的形狀。不過,遵循 Self 和 NewtonScript 等基於原型的語言,在 Lua 中模擬類別並不困難。在這些語言中,物件沒有類別。相反地,每個物件可能有一個原型,它是一個常規物件,第一個物件會在其中尋找它不知道的任何運算。若要表示這些語言中的類別,我們只需建立一個物件,專門用作其他物件(其執行個體)的原型。類別和原型都用於放置多個物件要共用的行為。
在 Lua 中,使用我們在前一章節中看到的繼承概念,可以輕易地實作原型。更具體地說,如果我們有兩個物件 a
和 b
,我們只需執行下列動作,就能讓 b
成為 a
的原型:
setmetatable(a, {__index = b})之後,
a
會在 b
中尋找它沒有的任何運算。將 b
視為物件 a
的類別,這只不過是術語上的改變。
讓我們回到銀行帳戶的範例。若要建立行為類似於 Account
的其他帳戶,我們安排這些新物件繼承 Account
的運作,使用 __index
元方法。請注意一個小最佳化,我們不需要建立一個額外的表格作為帳戶物件的元表格;我們可以使用 Account
表格本身來達到此目的
function Account:new (o) o = o or {} -- create object if user does not provide one setmetatable(o, self) self.__index = self return o end(當我們呼叫
Account:new
時,self
等於 Account
;因此我們可以使用 Account
直接,而不是 self
。然而,在下一節介紹類別繼承時,使用 self
會很合適。)在該程式碼之後,當我們建立一個新帳戶並呼叫其方法時,會發生什麼事?
a = Account:new{balance = 0} a:deposit(100.00)當我們建立這個新帳戶時,
a
會將 Account
(在呼叫 Account:new
中的 self)作為其元表格。然後,當我們呼叫 a:deposit(100.00)
時,我們實際上呼叫 a.deposit(a, 100.00)
(冒號僅為語法糖)。然而,Lua 無法在表格 a
中找到 "deposit"
項目;因此,它會查看元表格的 __index
項目。現在的情況大致如下
getmetatable(a).__index.deposit(a, 100.00)
a
的元表格是 Account
,而 Account.__index
也是 Account
(因為新方法執行 self.__index = self
)。因此,我們可以將前一個表達式改寫為
Account.deposit(a, 100.00)也就是說,Lua 呼叫原始的
deposit
函式,但將 a
傳遞為 self 參數。因此,新帳戶 a
從 Account
繼承了 deposit
函式。透過相同的機制,它可以繼承 Account
中的所有欄位。
繼承不僅適用於方法,也適用於新帳戶中不存在的其他欄位。因此,類別不僅提供方法,也提供其執行個體欄位的預設值。請記住,在我們對 Account
的第一個定義中,我們提供了值為 0 的欄位 balance
。因此,如果我們建立一個沒有初始餘額的新帳戶,它將繼承這個預設值
b = Account:new() print(b.balance) --> 0當我們在
b
上呼叫 deposit
方法時,它會執行等同於
b.balance = b.balance + v(因為
self
是 b
)。表達式 b.balance
會評估為零,而初始存款會指定給 b.balance
。下次我們詢問這個值時,不會呼叫索引元方法(因為現在 b
有自己的 balance
欄位)。
版權所有 © 2003–2004 Roberto Ierusalimschy。保留所有權利。 | ![]() |