扩展构造函数
本课属于 Coddy Lua 学习旅程的“面向对象编程”单元。第 33 课,共 70 课。
子类继承父类时,你通常希望子类的构造函数初始化父类需要初始化的所有内容,以及子类特有的其他字段。你可以直接调用父类的构造函数,而不必重复编写代码。
这种技术会使用父类名称来调用它的 :new() 方法,并将 self 作为第一个参数传入。这样会创建一个包含所有父类初始化内容的基础对象,然后你可以针对子类进行自定义。
local Vehicle = {}
Vehicle.__index = Vehicle
function Vehicle:new(speed)
local obj = {speed = speed}
setmetatable(obj, Vehicle)
return obj
end
-- Car 继承自 Vehicle
local Car = {}
Car.__index = Car
setmetatable(Car, {__index = Vehicle})
function Car:new(speed, brand)
local obj = Vehicle.new(Vehicle, speed) -- 调用父类构造函数
obj.brand = brand -- 添加子类特有的字段
setmetatable(obj, Car) -- 重新关联到 Car
return obj
end注意子类构造函数中的关键步骤:首先,我们调用 Vehicle.new(Vehicle, speed),获取一个已设置 speed 字段的对象。然后添加子类专属的 brand 字段。最后,将元表从 Vehicle 改为 Car,这样实例会优先在子类中查找方法。
调用父构造函数时,你也可以使用冒号语法:Vehicle:new(speed) 等同于 Vehicle.new(Vehicle, speed),因为两种写法都会将 Vehicle 作为第一个参数传入。在这里,两种写法都能正常工作,所以你可以选择自己觉得更易读的写法。
local myCar = Car:new(120, "Toyota")
print(myCar.speed) -- 输出:120
print(myCar.brand) -- 输出:Toyota这种模式可以让你的代码保持 DRY:共享的初始化逻辑放在父类中,而每个子类只处理各自独有的属性。
挑战
简单让我们构建一个生物类层次结构,让子类扩展父类的构造函数!你将创建一个 Animal 父类和一个 Bird 子类。Bird 的构造函数会先调用 Animal 的构造函数来处理共享的初始化,再添加它独有的属性。
你将把代码分到三个文件中:
Animal.lua:定义父类,其中包含一个用于存储动物名称的:new(name)构造函数。添加一个会打印动物名称的:describe()方法。使用标准的类模式设置__index,并返回这个类。Bird.lua:定义继承自Animal的子类。Bird的:new(name, wingspan)构造函数应使用Animal.new(Animal, name)调用父类构造函数来初始化名称,然后将wingspan字段添加到对象中,最后将元表重新关联到Bird。添加一个以指定格式打印名称和翼展的:info()方法。main.lua:引入Bird模块,读取两个输入(名称和翼展),创建一个Bird实例,并调用两个方法,演示继承功能和子类特有功能如何协同工作。
你会收到两个输入:
- 鸟的名称
- 鸟的翼展(一个数字)
在主文件中,使用给定的名称和翼展创建一个 Bird。然后在单独的行上依次调用 :describe() 和 :info()。
(从 Animal 继承的):describe() 方法应打印:
Animal: {name}(在 Bird 中定义的):info() 方法应打印:
{name} has a wingspan of {wingspan}例如,如果输入是 Eagle 和 200,输出应为:
Animal: Eagle
Eagle has a wingspan of 200关键在于,Bird 构造函数不会重复初始化名称,而是把这项工作交给 Animal 的构造函数,然后只添加鸟类独有的内容。这样可以让代码保持 DRY,同时构建正确的继承链!
亲手试试
-- main.lua:入口
local Bird = require('Bird')
-- 读取输入
local name = io.read()
local wingspan = tonumber(io.read())
-- TODO:使用给定的 name 和 wingspan 创建一个 Bird 实例
-- TODO:调用鸟的 :describe() 方法(继承自 Animal)
-- TODO:调用鸟的 :info() 方法(在 Bird 中定义)
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
自己练习: Lua 在线编译器