Lua 面向对象基础
Lua 本身没有内置的面向对象(OOP)特性,但可以通过表和元表(metatable)模拟实现。核心是通过 __index
元方法实现继承和多态,通过函数重定义实现方法重写。
继承的实现
Lua 的继承基于原型链。通过将父类作为子类元表的 __index
字段,实现属性查找的链式传递。
-- 父类
local Parent = {}
function Parent:new(name)
local obj = { name = name }
setmetatable(obj, { __index = Parent })
return obj
end
function 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 obj
end
-- 设置子类的父类(原型链)
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: Alice
child: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 obj
end
function 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 obj
end
setmetatable(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 obj
end
这种方式利用局部变量保持私有状态,外部无法直接访问privateData
。
表结合元表的封装
local Account = {}
Account.__index = Account
function Account.new(balance)
local obj = {
_balance = balance or 0 -- 约定下划线开头表示私有
}
setmetatable(obj, Account)
return obj
end
function Account:deposit(amount)
self._balance = self._balance + amount
end
尽管Lua无法强制私有,但通过命名约定(如_
前缀)提示字段不应被外部直接访问。
抽象的实现方法
抽象指隐藏复杂实现细节,仅暴露必要接口。Lua中通过模块化设计实现:
模块作为抽象单元
local abstractModule = {}
function abstractModule.complexOperation(a, b)
local result = a + b -- 隐藏具体计算过程
-- 其他内部处理...
return result
end
return 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
end
end
通过运行时检查确保对象实现了必要方法。
实际应用建议
对于需要更严格封装的场景,可结合闭包和表:
function StrictObject()
local data = {}
local methods = {}
methods.get = function(key) return data[key] end
methods.set = function(key, value) data[key] = value end
return methods
end
面向对象设计在Lua中是灵活的,应根据项目需求选择合适的实现方式。游戏开发中常用表+元表的方式,而需要严格隐藏细节时更适合闭包方案。