LCs*_*Csa 0 oop inheritance lua
如何在Lua中使用父类和子类构造函数来模拟简单继承?
我将提供、接受和回答,如果您对此发表评论或编辑有趣的信息,我将不胜感激。
这是在 Lua 中模仿基本父/子类继承的指南。
我假设读者已经了解 Lua 的基本特性和语法。以下声明尤其重要:
mt表的元表t包含__index元方法(因此),则尝试通过分配给 的内容来解析mt.__index对 in 中不存在的键的访问。tmt.__indext函数,则其本身将作为第一个参数传递给名为 的函数。在函数内部,它本身可以作为 访问。t.foo()t:foo()tfoo()selftselfBase = {}
function Base:new(name)
Base.__index = Base
local obj = {}
setmetatable(obj, Base)
obj.name = name
return obj
end
function Base:sayName()
print(self.name..": My name is "..self.name..".")
end
Run Code Online (Sandbox Code Playgroud)
由于该函数的作用Base:new(name),Base现在可以将其视为一个新类,并Base:new(name)作为其构造函数。请记住,这Base实际上是一个位于内存中某处的表,而不是一些抽象的“类蓝图”。它包含一个函数Base:sayName()。这就是我们可以用 OOP 术语来称呼的方法。
Base:new(name)但是let如何Base表现得像一个类呢?
Base.__index = Base
Run Code Online (Sandbox Code Playgroud)
该Base表有一个__index元方法,即它本身。每当Base用作元表时,对不存在索引的搜索将被重定向到...Base本身。(等等。)该行也可以写为self.__index = self,因为Base调用:new(name)且 因此self也在Base其中。我更喜欢第一个版本,因为它清楚地显示了正在发生的事情。另外,Base.__index = Base可以超出Base:new(name),但是,为了清楚起见,我更喜欢让所有“设置”发生在一个范围(“构造函数”)内。
local obj = {}
setmetatable(obj, Base)
Run Code Online (Sandbox Code Playgroud)
obj创建为一个新的空表。它将成为我们所认为的“类”的对象Base。现在是 的Base元表obj。由于Base有__index,对不存在的键的访问将obj被重定向到分配给 的内容Base.__index。由于Base.__indexisBase本身,对 in 中不存在的键的访问obj将被重定向到(例如,Base它会找到 的地方)!Base:sayName()
obj.name = name
return obj
Run Code Online (Sandbox Code Playgroud)
( obj!) 获取一个新条目,即成员,构造函数参数被分配给该成员。然后返回obj,我们将其解释为class 的对象Base。
b = Base:new("Mr. Base")
b:sayName()
Run Code Online (Sandbox Code Playgroud)
这会打印出“Mr. Base:我的名字是 Mr. Base”。正如预期的那样。通过如上所述的元表机制b查找,因为它没有这样的密钥。存在于内部(“类表”)和内部(“对象表”)。sayName()__indexsayName()Basenameb
Child = {}
function Child:new(name, age) -- our child class takes a second argument
Child.__index = Child
setmetatable(Child, {__index = Base}) -- this is different!
local obj = Base:new(name, age) -- this is different!
setmetatable(obj, Child)
obj.age = age
return obj
end
function Child:sayAge()
print(self.name..": I am "..tonumber(self.age).." years old.")
end
Run Code Online (Sandbox Code Playgroud)
该代码几乎与基类完全相同!在(即构造函数)中添加第二个参数Child:new(name, age)并不是特别值得注意。Base也可以有多个参数。Child:new(name, age)然而,添加了里面的第二行和第三行,这就是导致Child“继承”的原因Base.
请注意,Base可能包含Base.__index,这使得它在用作元表时很有用,但它本身没有元表。
setmetatable(Child, {__index = Base})
Run Code Online (Sandbox Code Playgroud)
在这一行中,我们将元表分配给Child类表。该元表包含一个__index元方法,该元方法设置为Base类表。因此,Child类表将尝试通过类表解决对不存在键的访问Base。因此,Child类表可以访问它自己的所有方法和 的所有Base方法!
local obj = Base:new(name, age)
setmetatable(obj, Child)
Run Code Online (Sandbox Code Playgroud)
在第一行中,Base创建了一个新的对象表。此时,它的__index元方法指向Base类表。然而,在第二行,它的元表被分配给Child类表。之所以obj不会失去对Base类方法的访问权限,是因为我们之前将不成功的键访问重定向Child到Base(通过提供正确的元表)!Child由于obj是作为Base对象表创建的,因此它包含其所有成员。此外,它还包含对象表的成员Child(一旦它们被添加到“构造函数”中。它通过自己的元方法Child:new(name, age)查找类表的方法。并通过类中的元方法查找类表中的方法桌子。ChildBaseChild
注意: “Base对象表”是指由 . 返回的表Base:new(name)。“Base类表”是指实际的Base表。请记住,Lua 中没有类/对象!类Base表和Base对象表一起模仿了我们所认为的 OOP 行为。Child当然,这同样适用。
Child此外,确定's 元表内部赋值的范围Child:new(name, age)允许我们调用Base's “构造函数”并将name参数传递给它!
c = Child:new("Mrs. Child", 42)
c:sayName()
c:sayAge()
Run Code Online (Sandbox Code Playgroud)
这将打印“Mrs. Child:我的名字是 Mrs. Child”。”和“柴尔德夫人:我今年 42 岁。正如预期的那样。
上面的部分描述了如何在 Lua 中实现 OOP 行为。重要的是要明白
Base类表中Base由返回的对象表内Base:new()Child类表中Child返回的对象表内Child:new()引用正确的表是通过表的元表来完成的。
__index键。当用作元表时,它们引用自身(即类方法所在的位置)。每个“类”只有一个类表。{__index = Base}(它将调用重定向到Base类表,即Base类方法所在的位置)。__index元方法,因此对对象表的调用可以重定向到类方法所在的类表。如果它是子类表(这意味着它也有元表),则重定向可能会发生得更远。可以有任意多个对象表。使用上图继续操作:如果Child对象表c尝试访问Base方法,例如,会发生什么c:sayName()?嗯:有c钥匙sayName()吗?没有。它有元表吗?是的:(Child班级Child表)。Child有元方法吗__index?是的。它指向哪里?对Child自己。Child有钥匙吗sayName()?没有。Child有元表吗?是的。它有__index元方法吗?是的。它指向哪里?到Base班级餐桌。它有sayName()钥匙吗?是的!:-)
我不是 Lua 专家!到目前为止,我只用 Lua 编写了一些脚本,但在过去的几天里,我试图集中精力解决这个问题。我发现了很多不同的、有时令人困惑的解决方案,最后得出了这个,我称之为最简单但透明的解决方案。如果您发现任何错误或警告,请随时发表评论!