Lua 面向对象编程概述
1 ) 基础概念
-
对象:用表(table)表示,包含属性(数据)和方法(函数)
-
类:通过元表(metatable)模拟类的行为,实现方法共享
-
继承:利用元表的 index 元方法实现属性和方法的继承链
-
封装:通过闭包或局部变量实现数据隐藏(非强制)
-
多态:动态函数调用,同一方法在不同对象中表现不同行为
面向对象特性 Lua 实现方式 关键机制 封装 使用 table 存储属性和方法,通过函数控制访问 闭包、局部变量、getter/setter 继承 通过元表的 index 元方法实现方法和属性的继承 metatable、index 多态 通过动态的函数调用实现多态行为 函数重写、动态绑定
2 ) 实现机制
- 元表(Metatable):核心机制,控制表的查找、操作行为
- index 元方法:实现继承和方法共享的关键
- newindex 元方法:控制新增属性的赋值逻辑
3 ) 高级特性
- 操作符重载(如 add、call)
- 多重继承与 Mixin 模式
- 私有性模拟(闭包 + 局部表)
核心实现细节与代码示例
1 ) 类与对象创建
lua
-- 定义基类
Person = { name = "Unnamed" }
function Person:new(name)
local obj = {}
setmetatable(obj, self) -- 绑定元表
self.index = self -- 设置 index 指向自身
obj.name = name or self.name
return obj
end
function Person:speak()
print("I am " .. self.name)
end
-- 创建对象
local p = Person:new("Alice")
p:speak() -- 输出: I am Alice
关键点:
- new 方法创建新表并关联元表
- index 确保对象可访问类的方法
2 ) 单继承实现
lua
--- 子类 Student 继承 Person
Student = Person:new() -- 继承基类方法
function Student:new(name, grade)
local obj = Person:new(name) -- 调用父类构造函数
setmetatable(obj, self) -- 绑定子类元表
self.index = self
obj.grade = grade or "Unknown"
return obj
end
function Student:study()
print(self.name .. " is studying in grade " .. self.grade)
end
--- 使用子类对象
local s = Student:new("Bob", 10)
s:speak() -- 继承自 Person → "I am Bob"
s:study() -- 子类方法 → "Bob is studying in grade 10"
关键点:
- 子类通过 父类:new() 初始化父类属性
- 子类元表覆盖父类,实现方法扩展
3 )封装与私有性模拟
lua
function createAccount(balance)
local privateData = { balance = balance } -- 私有数据
local obj = {}
obj.deposit = function(amount)
privateData.balance = privateData.balance + amount
end
obj.getBalance = function()
return privateData.balance
end
return obj
end
local acc = createAccount(100)
acc.deposit(50)
print(acc.getBalance()) -- 输出: 150
--- 无法直接访问 privateData.balance
再来看一个封装例子
lua
local function createPerson(name, age)
local privateData = { name = name, age = age } -- 私有数据
local obj = {}
---- 公共方法
function obj.getName()
return privateData.name
end
function obj.getAge()
return privateData.age
end
function obj.setAge(newAge)
if newAge > 0 then
privateData.age = newAge
end
end
return obj
end
local person = createPerson("Alice", 30)
print(person.getName()) -- 输出: Alice
print(person.getAge()) -- 输出: 30
person.setAge(31)
print(person.getAge()) -- 输出: 31
-- print(privateData.name) -- 错误!无法直接访问 privateData
关键点:
- 闭包 + 局部变量隐藏数据,外部仅通过公开方法访问
4 ) 继承(Inheritance)
允许一个类(子类)继承另一个类(父类)的属性和方法
实现方式:通过元表和元方法来模拟
一个子类可以定义其自己的元表,该元表将父类对象作为其元方法的查找表
使用 setmetatable 和 index 来实现继承
示例代码(继承):
lua
--- 父类
Animal = {}
function Animal:new(name)
local obj = { name = name }
setmetatable(obj, self)
self.index = self
return obj
end
function Animal:speak()
print(self.name .. " makes a sound.")
end
--- 子类
Dog = {}
setmetatable(Dog, {index = Animal}) -- Dog 继承 Animal
function Dog:new(name, breed)
local obj = Animal.new(self, name) -- 调用父类构造函数
obj.breed = breed
setmetatable(obj, self)
self.index = self
return obj
end
function Dog:speak() -- 重写父类方法
print(self.name .. " barks! Woof!")
end
local myDog = Dog:new("Buddy", "Golden Retriever")
myDog:speak() -- 输出: Buddy barks! Woof!
print(myDog.name) -- 输出: Buddy (从父类继承)
print(myDog.breed) -- 输出: Golden Retriever (子类特有)
5 ) 多态(Polymorphism)
允许不同类的对象对同一消息做出响应的能力
实现方式:当一个函数或方法被调用时,它接收的对象类型可以是任何类的实例,这为多态提供了基础
通过函数的灵活性和方法重写来实现
示例代码(多态):
lua
--- 假设已定义了 Animal, Dog, Cat 类(Cat 类类似 Dog)
--- Cat 类可能有 speak() 方法输出 "meows"
local animals = {myDog, myCat} -- 假设 myCat 也已创建
for _, animal in ipairs(animals) do
animal:speak() -- 同一个方法调用,根据实例类型产生不同行为
end
--- 输出:
--- Buddy barks! Woof!
--- Whiskers meows! (假设 myCat 名字是 Whiskers)
6 ) 多继承(Mixin 模式)
lua
--- 定义多个基类
Flyer = { canFly = true }
--- Swimmer = { canSwim = true }
--- 多重继承函数
function multiInherit(...)
local classes = { ... }
local combined = {}
for _, cls in ipairs(classes) do
for k, v in pairs(cls) do
combined[k] = v
end
end
return combined
end
--- 创建多继承类
Duck = multiInherit(Flyer, Swimmer)
Duck.quack = function() print("Quack!") end
local duck = setmetatable({}, { index = Duck })
duck.quack() -- Quack!
print(duck.canFly) -- true
关键点:
- 合并多个基类的字段到新表
需手动处理冲突方法
7 ) 操作符重载(元方法)
lua
Vector = {}
function Vector:new(x, y)
return setmetatable({ x = x, y = y }, self)
end
Vector.add = function(a, b)
return Vector:new(a.x + b.x, a.y + b.y)
end
local v1 = Vector:new(1, 2)
local v2 = Vector:new(3, 4)
local v3 = v1 + v2 -- 调用 add
print(v3.x, v3.y) -- 输出: 4, 6
关键点:
- 通过 add、sub 等元方法重载运算符
最佳实践与注意事项
1 ) 性能优化
- 避免过度嵌套元表(影响查找效率),优先组合而非深继承链
- 预缓存常用方法(如 local pairs = pairs)
2 ) 私有性限制:
- Lua 无强制私有机制,依赖命名约定(如 _privateVar)或闭包
3 ) self 参数:
- 用 obj:method() 语法(自动传递 self),代替 obj.method(obj)
4 ) 工厂模式:
统一入口创建对象,隐藏构造细节:
lua
function CreateShape(type, ...)
if type == "circle" then return Circle:new(...) end
if type == "rect" then return Rectangle:new(...) end
end
完整代码库参考:云风 Lua OOP 实现,或 LuaRocks 模块 middleclass
补充说明
1 ) 冒号操作符 :
- 在 Lua 中,
obj:method(args)等价于obj.method(obj, args) - 冒号会自动将调用对象
(obj)作为第一个参数(通常命名为self)传递给方法。这使得方法内部可以访问对象自身的属性和方法
2 ) __index 元方法
- 当访问一个表中不存在的键时,Lua 会查找该表的元表中的
__index元方法。如果__index是一个表,Lua 会去该表中查找; - 如果
__index是一个函数,则调用该函数。这正是实现继承查找链的关键机制 - 构造函数:通常约定使用
new作为构造函数名,用于创建类的新实例并初始化其属性
要点回顾
- Lua 通过其核心数据结构 table 和元表机制,巧妙地模拟了面向对象编程的三大特性
- 虽然语法上不如 Java、C++ 等原生 OOP 语言直观,但其灵活性和简洁性提供了强大的抽象能力
- 掌握
table、metatable、__index以及冒号调用语法是实现 Lua OOP 的关键