Lua面向對象編程
面向對象的三個基本特征
- 封裝:隱藏對象的屬性和實現細節,僅對外公開接口,控制在程序中屬性的讀和修改的訪問級別。
- 繼承:使子類具有父類的屬性、方法或者重新定義、追加屬性和方法等。
- 多態:同以操作作用於不同的對象,可以有不同的解釋,產生不同的執行結果。在運行時,可以通過指向基類的指針,來調用實現派生類的方法。
Lua中的面向對象
Lua中的表和對象有很多相似性,因此在Lua中可以使用表來實現面向對象。
Lua中類的方法可以通過表+function來模擬:
zhangsan = {name = ""} function zhangsan.setName(self, name) self.name = name end zhangsan.setName(zhangsan, "張三") print(zhangsan.name) --輸出: --張三
類(Class)
上面的例子中,zhangsan對象擁有了屬性和方法,但是這個對象卻不是通過類來創建的。在大多數面向對象的語言中都提供了類的概念,類在對象創建中扮演了模板的作用。
在Lua中,我們可以使用原型的思想來模擬類。讓每一個對象都有一個原型,當對象遇到未知的操作時就在原型中去查找。
如下,為zhangsan指定它的原型Person:
Person = {} function Person.setName(self, name) self.name = name end function Person:new(person) person = person or {} self.__index = self setmetatable(person, self) return person end zhangsan = Person:new() print(zhangsan.name) zhangsan:setName("張三") print(zhangsan.name) --輸出: --nil --張三
在上面的代碼中,使用了冒號:來操作,如Person:new(person)這樣的寫法相當於Person.new(Person, person),冒號只是一個語法糖,這個函數中的self即為Person類本身。
繼承
在類(Class)的new操作中,self.__index = self,將Person的元方法__index指向了自身,這樣做會導致當調用一個方法時,解釋器第一時間將會查找Person自身有沒有這個方法。這一點和繼承很相似。
在繼承中,子類可以擁有父類的屬性和方法,也可以定義自己的屬性和方法。
如果要從一個類中派生一個子類,可以先創建一個從基類繼承了所有操作的空類:
Male = Person:new() Male.sex = "男" zhangsan = Male:new() zhangsan:setName("張三") print(zhangsan.name, zhangsan.sex) --輸出: --張三 男
Male擁有了Person的new方法,同時通過Male創建的zhangsan擁有Person的setName方法。
在這個過程中,Male = Person:new()會將Male的元表設置為Person,zhangsan = Male:new()則將zhangsan的元表設置為了Male。
當調用zhangsan:setName時,Lua將首先從zhangsan查找setName方法,發現不存在時,則從zhangsan元表Male的元方法__index中(即Male本身)查找,直到在Person中找到該方法。因此,zhangsan不僅有Male的sex屬性,還擁有Person的setName方法。
同時,還可以很方便地在Male中重寫Person的setName方法,只需要定義一個新方法即可。
function Male:setName(name)
self.name = "Male:"..name
end
zhangsan = Male:new()
zhangsan:setName("張三")
print(zhangsan.name)
--輸出:
--Male:張三
這是由於,在Male中已經有setName方法了,所以解釋器不會再去Person中去查找。
Lua面向對象編程