0Pricing
Lua Academy · 课时

构造函数和 new()

编写用于创建和初始化实例的构造函数。

构造函数和 new() 是 CoddyKit 上的免费 Lua Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Lua Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Lua Academy 课程共包含 4 节课。

构造函数的职责

构造函数会创建并初始化一个实例。它接收初始值,验证这些值,为缺失的字段设置默认值,并返回新实例。设计良好的构造函数可确保每个实例都从有效状态开始。

local Player = {}
Player.__index = Player

function Player.new(name, hp, mp)
  assert(type(name)=="string" and name~="","need name")
  return setmetatable({
    name = name,
    hp   = hp or 100,
    mp   = mp or 50,
    alive = true,
    items = {},
  }, Player)
end

local p = Player.new("Hero")
print(p.name, p.hp, p.mp)  -- Hero  100  50

流式构造函数

流式构造函数会让 setter 方法返回 self,从而支持在构造过程中进行方法链式调用。这是 Lua 中的一种构建器模式。

local Config = {}
Config.__index = Config

function Config.new()
  return setmetatable({_data={}}, Config)
end

function Config:set(k,v) self._data[k]=v; return self end
function Config:build() return self._data end

local cfg = Config.new()
  :set("host","localhost")
  :set("port",8080)
  :set("debug",true)
  :build()

print(cfg.host, cfg.port)

复制构造函数

复制构造函数会创建一个新实例,其值与现有实例相同。重要提示:根据嵌套表是否应共享或彼此独立,选择浅复制或深复制。

local function deepCopy(orig)
  if type(orig)~="table" then return orig end
  local copy = {}
  for k,v in pairs(orig) do copy[deepCopy(k)]=deepCopy(v) end
  return setmetatable(copy, getmetatable(orig))
end

local Item = {}
Item.__index = Item

function Item.new(name, stats)
  return setmetatable({name=name, stats=stats or {}}, Item)
end

function Item:clone()
  return deepCopy(self)
end

local sword = Item.new("Sword",{damage=50})
local clone = sword:clone()
clone.stats.damage = 30
print(sword.stats.damage)  -- 50 (unchanged)

带验证的构造函数

始终验证构造函数的输入。在第 2 级(归咎于调用方)对无效输入抛出描述性错误。这样可以尽早发现错误,并生成有帮助的错误消息。

local Rectangle = {}
Rectangle.__index = Rectangle

function Rectangle.new(w, h)
  assert(type(w)=="number" and w>0,"width must be positive number")
  assert(type(h)=="number" and h>0,"height must be positive number")
  return setmetatable({width=w, height=h}, Rectangle)
end

function Rectangle:area() return self.width * self.height end
function Rectangle:perimeter() return 2*(self.width+self.height) end

local r = Rectangle.new(5, 3)
print(r:area(), r:perimeter())  -- 15  16

默认值模式

使用默认值表来简洁地处理可选的构造函数参数。这样可以避免冗长的 x = x or default 链,并使默认值明确且易于修改。

local Sprite = {}
Sprite.__index = Sprite

local DEFAULTS = {x=0,y=0,rotation=0,scale=1,visible=true,alpha=1}

function Sprite.new(image, opts)
  local cfg = opts or {}
  local self = {image=image}
  for k,v in pairs(DEFAULTS) do
    self[k] = cfg[k] ~= nil and cfg[k] or v
  end
  return setmetatable(self, Sprite)
end

local s = Sprite.new("hero.png",{x=100,y=200})
print(s.x,s.y,s.scale)  -- 100 200 1

静态工厂方法

一个类可以针对不同的创建场景提供多个工厂方法。与使用许多可选参数重载单个构造函数相比,这种方式更加清晰。

local Color = {}
Color.__index = Color

function Color.fromRGB(r,g,b)
  return setmetatable({r=r,g=g,b=b}, Color)
end

function Color.fromHex(hex)
  hex = hex:gsub("#","")
  return setmetatable({
    r=tonumber(hex:sub(1,2),16),
    g=tonumber(hex:sub(3,4),16),
    b=tonumber(hex:sub(5,6),16),
  }, Color)
end

function Color:toHex()
  return string.format("#%02X%02X%02X",self.r,self.g,self.b)
end

local red = Color.fromHex("#FF0000")
print(red:toHex())  -- #FF0000

构造函数注册表

使用注册表跟踪所有实例。这样就可以执行“销毁全部实例”等全局操作,或遍历某一类型的所有对象。

local Enemy = {}
Enemy.__index = Enemy
Enemy._all = {}

function Enemy.new(name, hp)
  local self = setmetatable({name=name, hp=hp}, Enemy)
  Enemy._all[#Enemy._all+1] = self
  return self
end

function Enemy.destroyAll()
  for _, e in ipairs(Enemy._all) do
    print("Destroying:", e.name)
  end
  Enemy._all = {}
end

Enemy.new("Goblin",30)
Enemy.new("Orc",80)
print("Count:", #Enemy._all)  -- 2
Enemy.destroyAll()

延迟初始化

有些字段的计算成本很高。使用带函数的 __index,在首次访问时计算并缓存这些字段。

local BigData = {}
BigData.__index = function(t, k)
  if k == "processed" then
    print("Computing processed...")
    local result = {}
    for i,v in ipairs(rawget(t,"raw")) do result[i]=v*2 end
    rawset(t,"processed",result)
    return result
  end
end

function BigData.new(raw)
  return setmetatable({raw=raw}, BigData)
end

local d = BigData.new({1,2,3,4,5})
print(d.processed[3])  -- Computing...  6
print(d.processed[3])  -- 6 (cached, no recompute)

从实例创建原型

复制一个实例,以创建另一个具有相同基础值的实例。副本会获得相同的元表(即相同的类)以及一份实例数据副本。这适用于创建“模板”对象。

local Unit = {}
Unit.__index = Unit

function Unit.new(t)
  return setmetatable({name=t.name,hp=t.hp,atk=t.atk}, Unit)
end

function Unit:clone()
  return Unit.new({name=self.name,hp=self.hp,atk=self.atk})
end

function Unit:__tostring()
  return self.name.."(HP:"..self.hp..",ATK:"..self.atk..")"
end

local template = Unit.new({name="Soldier",hp=100,atk=10})
local u1 = template:clone(); u1.name="Alpha"
local u2 = template:clone(); u2.name="Beta"
print(tostring(u1),tostring(u2))

构造函数错误安全

如果构造过程在中途失败(例如资源分配失败),请确保不会泄漏不完整的对象。使用 pcall 捕获构造错误,并在需要时清理资源。

local FileHandle = {}
FileHandle.__index = FileHandle

function FileHandle.new(path, mode)
  local f, err = io.open(path, mode or "r")
  if not f then
    return nil, "cannot open " .. path .. ": " .. err
  end
  return setmetatable({_f=f, path=path}, FileHandle)
end

function FileHandle:read(fmt) return self._f:read(fmt) end
function FileHandle:close() self._f:close() end

local fh, err = FileHandle.new("data.txt")
if not fh then print("Error:", err)
else print(fh:read("l")); fh:close()
end

快速检查

构造函数(new())在 Lua 面向对象编程中的作用是什么?

回顾:构造函数

总结:

  • 构造函数负责验证、初始化并调用 setmetatable
  • 使用默认值表来简洁地处理可选参数
  • 针对不同创建场景使用多个工厂方法
  • 使用复制构造函数创建模板对象
  • 通过 __index 对计算成本高的字段进行延迟初始化
  • 构造失败(资源分配失败)时从构造函数返回 nil+err

常见问题解答

「构造函数和 new()」课时是免费的吗?

是的 — 「构造函数和 new()」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Lua Academy 课程的其余内容,请升级到 CoddyKit PRO。 Lua Academy 课程共包含 4 节课。

「构造函数和 new()」这节课中我会学到什么?

编写用于创建和初始化实例的构造函数。 你通过在浏览器中直接运行的动手代码来练习 Lua Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Lua Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Lua Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「构造函数和 new()」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Lua Academy 课中编写并运行代码吗?

能。每节 Lua Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 通过元表实现类
  2. 构造函数和 new()
  3. 实例方法和 self
  4. 使用闭包实现封装
← 返回 Lua Academy