方法重写
本课属于 Coddy Lua 学习旅程的“面向对象编程”单元。第 37 课,共 70 课。
到目前为止,子类继承了父类的方法,并添加了新方法。但如果子类需要使用相同的方法名,却实现不同的行为,该怎么办?这叫作方法重写。
当你在子类中定义一个与父类同名的方法时,会优先使用子类中的版本。Lua 的查找链会先检查子类,如果在那里找到该方法,就会停止查找。
local Animal = {}
Animal.__index = Animal
function Animal:new(name)
local obj = {name = name}
setmetatable(obj, Animal)
return obj
end
function Animal:speak()
print(self.name .. " makes a sound")
end
-- Dog 重写了 speak() 方法
local Dog = {}
Dog.__index = Dog
setmetatable(Dog, {__index = Animal})
function Dog:new(name)
local obj = Animal.new(Animal, name)
setmetatable(obj, Dog)
return obj
end
function Dog:speak()
print(self.name .. " barks: Woof!")
end现在,每个类对同一个方法调用的响应都不同:
local generic = Animal:new("Creature")
local buddy = Dog:new("Buddy")
generic:speak() -- 输出:Creature makes a sound
buddy:speak() -- 输出:Buddy barks: Woof!Dog 的 :speak() 会完全替换父类中的版本,并应用于 Dog 实例。这样,你就可以在保持接口一致的同时创建专门的行为:每种动物都可以 :speak(),但每个物种的实现方式各不相同。
挑战
简单让我们构建一个小小的动物王国,让不同的动物用各自独特的方式说话!你将创建一个 Animal 父类,以及两个子类 Cat 和 Cow。它们都会重写父类的 :speak() 方法,实现各自的专属行为。
你需要把代码分成 4 个文件:
Animal.lua:基类,包含一个:new(name)构造函数,用于保存动物的名字。还要添加一个:speak()方法,打印动物的名字,后面跟着makes a sound。这是子类将要重写的默认行为。Cat.lua:一个继承自 Animal 的子类。它的:new(name)构造函数应调用父类构造函数,然后重新将元表关联到 Cat。重写:speak()方法,让猫发出不同的声音:打印猫的名字,后面跟着meows: Meow!Cow.lua:另一个继承自 Animal 的子类。构造函数的设置方式与 Cat 相同。重写:speak()方法,让奶牛打印自己的名字,后面跟着moos: Moo!main.lua:把所有内容整合起来!从输入中读取两个名字,用第一个名字创建一个 Cat,用第二个名字创建一个 Cow,然后分别对它们调用:speak(),演示同一个方法名如何根据类的不同产生不同的输出。
你会收到两个输入:
- 猫的名字
- 奶牛的名字
在主文件中,创建这两只动物,并先对猫调用 :speak(),再对奶牛调用。
例如,如果输入是 Whiskers 和 Bessie,输出应为:
Whiskers meows: Meow!
Bessie moos: Moo!注意,两只动物都会响应同一个 :speak() 方法调用,但各自的输出会根据其类型而不同。Cat 的版本完全替代了父类通用的“makes a sound”行为,Cow 的版本也是如此。这就是方法重写:接口相同,实现不同!
亲手试试
-- main.lua:把所有部分组合起来
local Cat = require('Cat')
local Cow = require('Cow')
-- 读取输入
local catName = io.read()
local cowName = io.read()
-- TODO:用第一个名字创建一个 Cat 对象
-- TODO:用第二个名字创建一个 Cow 对象
-- TODO:先对猫调用 :speak(),再对牛调用 :speak()
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Lua 在线编译器