Lua: 面向对象编程详解之类、继承、封装与多态实现

Lua 面向对象编程概述


1 ) 基础概念

  • 对象:用表(table)表示,包含属性(数据)和方法(函数)

  • 类:通过元表(metatable)模拟类的行为,实现方法共享

  • 继承:利用元表的 index 元方法实现属性和方法的继承链

  • 封装:通过闭包或局部变量实现数据隐藏(非强制)

  • 多态:动态函数调用,同一方法在不同对象中表现不同行为

    面向对象特性Lua 实现方式关键机制
    封装使用 table 存储属性和方法,通过函数控制访问闭包、局部变量、getter/setter
    继承通过元表的 index 元方法实现方法和属性的继承metatable、index
    多态通过动态的函数调用实现多态行为函数重写、动态绑定

2 ) 实现机制

  • 元表(Metatable):核心机制,控制表的查找、操作行为
  • index 元方法:实现继承和方法共享的关键
  • newindex 元方法:控制新增属性的赋值逻辑

3 ) 高级特性

  • 操作符重载(如 add、call)
  • 多重继承与 Mixin 模式
  • 私有性模拟(闭包 + 局部表)

核心实现细节与代码示例


1 ) 类与对象创建

-- 定义基类
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 ) 单继承实现

--- 子类 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 )封装与私有性模拟

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 

再来看一个封装例子

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 来实现继承

示例代码(继承):

--- 父类
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)

允许不同类的对象对同一消息做出响应的能力
实现方式:当一个函数或方法被调用时,它接收的对象类型可以是任何类的实例,这为多态提供了基础
通过函数的灵活性和方法重写来实现

示例代码(多态):

--- 假设已定义了 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 模式)

--- 定义多个基类 
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 ) 操作符重载(元方法)

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 ) 工厂模式:

统一入口创建对象,隐藏构造细节:

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 语言直观,但其灵活性和简洁性提供了强大的抽象能力
  • 掌握 tablemetatable__index 以及冒号调用语法是实现 Lua OOP 的关键
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值