> 技术文档 > Lua(面向对象)

Lua(面向对象)


Lua 面向对象基础

Lua 本身没有内置的面向对象(OOP)特性,但可以通过表和元表(metatable)模拟实现。核心是通过 __index方法实现继承和多态,通过函数重定义实现方法重写


继承的实现

Lua 的继承基于原型链。通过将父类作为子类元表的 __index 字段,实现属性查找的链式传递。

-- 父类local Parent = {}function Parent:new(name) local obj = { name = name } setmetatable(obj, { __index = Parent }) return objendfunction Parent:say() print(\"Parent says:\", self.name)end-- 子类继承local Child = {}function Child:new(name, age) local obj = Parent:new(name) -- 调用父类构造函数 obj.age = age setmetatable(obj, { __index = Child }) return objend-- 设置子类的父类(原型链)setmetatable(Child, { __index = Parent })

关键点

  • 子类通过 Parent:new() 初始化父类属性。
  • 子类元表的 __index 指向自身,而子类本身的元表 __index 指向父类,形成链式查找。

多态的实现

多态通过方法重写和动态查找实现。子类可以覆盖父类方法,调用时根据实际对象的元表决定执行哪个方法。

-- 子类重写父类方法function Child:say() print(\"Child says:\", self.name, \"age:\", self.age)end-- 测试多态local parent = Parent:new(\"Alice\")local child = Child:new(\"Bob\", 10)parent:say() -- 输出: Parent says: Alicechild:say() -- 输出: Child says: Bob age: 10

说明

  • child:say() 优先调用子类的方法,若子类未定义则通过 __index 查找父类方法。

方法重写

直接覆盖父类方法即可实现重写。若需调用父类方法,需显式通过父类引用。

function Child:say() -- 调用父类方法 Parent.say(self) -- 扩展子类逻辑 print(\"Extra info:\", self.age)end

注意

  • 使用 Parent.say(self) 而非 Parent:say(),需手动传入 self 以保持对象上下文。

完整示例代码

-- 父类local Parent = {}function Parent:new(name) local obj = { name = name } setmetatable(obj, { __index = Parent }) return objendfunction Parent:say() print(\"Parent says:\", self.name)end-- 子类local Child = {}function Child:new(name, age) local obj = Parent:new(name) obj.age = age setmetatable(obj, { __index = Child }) return objendsetmetatable(Child, { __index = Parent })-- 重写方法function Child:say() Parent.say(self) -- 调用父类方法 print(\"Child added:\", self.age)end-- 测试local child = Child:new(\"Tom\", 8)child:say()

封装实现方式

封装的核心在于将数据和操作数据的方法绑定,并控制外部访问权限。Lua中通过闭包或表结合元表实现:

闭包实现私有性

function createObject() local privateData = 0 local obj = {} function obj:getData() return privateData end function obj:setData(value) privateData = value end return objend

这种方式利用局部变量保持私有状态,外部无法直接访问privateData

表结合元表的封装

local Account = {}Account.__index = Accountfunction Account.new(balance) local obj = { _balance = balance or 0 -- 约定下划线开头表示私有 } setmetatable(obj, Account) return objendfunction Account:deposit(amount) self._balance = self._balance + amountend

尽管Lua无法强制私有,但通过命名约定(如_前缀)提示字段不应被外部直接访问。

抽象的实现方法

抽象指隐藏复杂实现细节,仅暴露必要接口。Lua中通过模块化设计实现:

模块作为抽象单元

local abstractModule = {}function abstractModule.complexOperation(a, b) local result = a + b -- 隐藏具体计算过程 -- 其他内部处理... return resultendreturn abstractModule

接口模拟

local interface = { requiredMethod = function() error(\"Not implemented\") end}function interface.check(obj) for k, v in pairs(interface) do if type(v) == \"function\" and not obj[k] then error(\"Missing required method: \"..k) end endend

通过运行时检查确保对象实现了必要方法。

实际应用建议

对于需要更严格封装的场景,可结合闭包和表:

function StrictObject() local data = {} local methods = {} methods.get = function(key) return data[key] end methods.set = function(key, value) data[key] = value end return methodsend

面向对象设计在Lua中是灵活的,应根据项目需求选择合适的实现方式。游戏开发中常用表+元表的方式,而需要严格隐藏细节时更适合闭包方案。