面向对象教程 |
|
local MyClass = {} -- the table representing the class, which will double as the metatable for the instances MyClass.__index = MyClass -- failed table lookups on the instances should fallback to the class table, to get methods -- syntax equivalent to "MyClass.new = function..." function MyClass.new(init) local self = setmetatable({}, MyClass) self.value = init return self end function MyClass.set_value(self, newval) self.value = newval end function MyClass.get_value(self) return self.value end local i = MyClass.new(5) -- tbl:name(arg) is a shortcut for tbl.name(tbl, arg), except tbl is evaluated only once print(i:get_value()) --> 5 i:set_value(6) print(i:get_value()) --> 6
首先,我们创建一个表来表示类并包含其方法。我们也让它充当实例的元表,但如果你愿意,可以使用单独的实例元表。
在构造函数中,我们创建实例(一个空表),赋予它元表,填充字段,并返回新实例。
在方法中,我们使用“self”参数来获取要操作的实例。这很常见,因此 Lua 提供了:
语法糖,它从表中调用函数条目并将表本身插入第一个参数之前。
可以做一些改进
local MyClass = {} MyClass.__index = MyClass setmetatable(MyClass, { __call = function (cls, ...) return cls.new(...) end, }) function MyClass.new(init) local self = setmetatable({}, MyClass) self.value = init return self end -- the : syntax here causes a "self" arg to be implicitly added before any other args function MyClass:set_value(newval) self.value = newval end function MyClass:get_value() return self.value end local instance = MyClass(5) -- do stuff with instance...
在这里,我们在类表中添加了一个元表,该元表具有__call
元方法,当像函数一样调用值时会触发该方法。我们让它调用类的构造函数,因此在创建实例时不需要.new
。另一个选择是将构造函数直接放在元方法中。在元方法中,“cls”指的是当前表。
此外,为了补充:
方法调用快捷方式,Lua 允许你在表中定义函数时使用:
,这会隐式添加一个self
参数,因此你无需自己键入它。
很容易扩展上面示例中类的设计以使用继承
local BaseClass = {} BaseClass.__index = BaseClass setmetatable(BaseClass, { __call = function (cls, ...) local self = setmetatable({}, cls) self:_init(...) return self end, }) function BaseClass:_init(init) self.value = init end function BaseClass:set_value(newval) self.value = newval end function BaseClass:get_value() return self.value end --- local DerivedClass = {} DerivedClass.__index = DerivedClass setmetatable(DerivedClass, { __index = BaseClass, -- this is what makes the inheritance work __call = function (cls, ...) local self = setmetatable({}, cls) self:_init(...) return self end, }) function DerivedClass:_init(init1, init2) BaseClass._init(self, init1) -- call the base class constructor self.value2 = init2 end function DerivedClass:get_value() return self.value + self.value2 end local i = DerivedClass(1, 2) print(i:get_value()) --> 3 i:set_value(3) print(i:get_value()) --> 5
这里我们有派生类表和一个__index
元方法,它使它继承基类。我们还将实例的创建移到了__call
元方法中,并将构造函数纯粹变成了初始化方法。这样派生类就可以在自身上调用基类初始化函数。
可以做的最后一项优化是将基类的内容复制到派生类中,而不是使用__index
。这避免了可能减慢方法调用的长__index
链,并且还使得如果基类具有像__add
这样的方法,它们将在派生类上像适当的元方法一样工作。这是因为在查找元方法时不会遵循__index
local DerivedClass = {} for k, v in pairs(BaseClass) do DerivedClass[k] = v end DerivedClass.__index = DerivedClass
了解了所有这些,就可以创建一个方便的函数来创建类,可以选择继承其他类。以下是一个此类函数的示例
function (...) -- "cls" is the new class local cls, bases = {}, {...} -- copy base class contents into the new class for i, base in ipairs(bases) do for k, v in pairs(base) do cls[k] = v end end -- set the class's __index, and start filling an "is_a" table that contains this class and all of its bases -- so you can do an "instance of" check using my_instance.is_a[MyClass] cls.__index, cls.is_a = cls, {[cls] = true} for i, base in ipairs(bases) do for c in pairs(base.is_a) do cls.is_a[c] = true end cls.is_a[base] = true end -- the class's __call metamethod setmetatable(cls, {__call = function (c, ...) local instance = setmetatable({}, c) -- run the init method if it's there local init = instance._init if init then init(instance, ...) end return instance end}) -- return the new class table, that's ready to fill with methods return cls end
也可以使用闭包创建对象。实例创建速度较慢,占用更多内存,但也有一些优点(例如实例字段访问速度更快),并且它是一个有趣的例子,说明了如何使用闭包。
local function MyClass(init) -- the new instance local self = { -- public fields go in the instance table public_field = 0 } -- private fields are implemented using locals -- they are faster than table access, and are truly private, so the code that uses your class can't get them local private_field = init function self.foo() return self.public_field + private_field end function self.bar() private_field = private_field + 1 end -- return the instance return self end local i = MyClass(5) print(i.foo()) --> 5 i.public_field = 3 i.bar() print(i.foo()) --> 9
请注意,.
语法用于调用方法,而不是 :
。这是因为 self 变量已经作为上值存储在方法中,因此调用代码不需要传递它。
这种方式也支持继承
local function BaseClass(init) local self = {} local private_field = init function self.foo() return private_field end function self.bar() private_field = private_field + 1 end -- return the instance return self end local function DerivedClass(init, init2) local self = BaseClass(init) self.public_field = init2 -- this is independent from the base class's private field that has the same name local private_field = init2 -- save the base version of foo for use in the derived version local base_foo = self.foo function self.foo() return private_field + self.public_field + base_foo() end -- return the instance return self end local i = DerivedClass(1, 2) print(i.foo()) --> 5 i.bar() print(i.foo()) --> 6
基于表的优点
MyClass.method(instance, args)
)。
:
用于方法调用与绝大多数面向对象的 Lua 代码更加一致。
基于闭包的优点
__index
元方法。
.
方法调用语法更熟悉。