Lua本身不支持传统的面向对象编程,但通过表和元表可以完美模拟。核心机制是使用__index元方法实现方法查找和继承。当访问对象的方法时,Lua首先在对象自身查找,如果找不到则查找元表的__index字段。通过将__index设置为父类,可以实现继承链。冒号语法(obj:method())是语法糖,等价于obj.method(obj),自动传递self参数。
-- 使用table和元表实现类
Person = {}
Person.__index = Person
function Person:new(name, age)
local obj = {}
setmetatable(obj, Person)
obj.name = name
obj.age = age
return obj
end
function Person:sayHello()
print("你好,我是" .. self.name .. ",今年" .. self.age .. "岁")
end
-- 使用类
local p1 = Person:new("张三", 25)
p1:sayHello() -- 输出: 你好,我是张三,今年25岁
-- 理解冒号语法
p1:sayHello() -- 等价于 Person.sayHello(p1)
p1.sayHello(p1) -- 同样的效果
-- 更完善的类定义
function Person:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
return o
end
-- 使用
local p = Person:new{name = "李四", age = 30}
p:sayHello()
-- 父类
Animal = {}
Animal.__index = Animal
function Animal:new(name)
local obj = {name = name}
setmetatable(obj, Animal)
return obj
end
function Animal:speak()
print(self.name .. " 发出声音")
end
-- 子类继承
Dog = {}
setmetatable(Dog, {__index = Animal})
Dog.__index = Dog
function Dog:new(name, breed)
local obj = Animal:new(name)
setmetatable(obj, Dog)
obj.breed = breed
return obj
end
function Dog:speak()
print(self.name .. " 汪汪叫")
end
function Dog:fetch()
print(self.name .. " 去捡球")
end
-- 使用
local dog = Dog:new("旺财", "金毛")
dog:speak() -- 输出: 旺财 汪汪叫
dog:fetch() -- 输出: 旺财 去捡球
-- 定义多个子类
Cat = {}
setmetatable(Cat, {__index = Animal})
Cat.__index = Cat
function Cat:new(name, color)
local obj = Animal:new(name)
setmetatable(obj, Cat)
obj.color = color
return obj
end
function Cat:speak()
print(self.name .. " 喵喵叫")
end
-- 多态演示
local animals = {
Dog:new("旺财", "金毛"),
Cat:new("咪咪", "白色"),
Dog:new("大黄", "土狗")
}
for _, animal in ipairs(animals) do
animal:speak() -- 根据实际类型调用不同方法
end
-- 使用闭包实现私有成员
function BankAccount(initialBalance)
local balance = initialBalance -- 私有变量
local obj = {}
function obj:deposit(amount)
if amount > 0 then
balance = balance + amount
return true
end
return false
end
function obj:withdraw(amount)
if amount > 0 and amount <= balance then
balance = balance - amount
return true
end
return false
end
function obj:getBalance()
return balance
end
return obj
end
-- 使用
local account = BankAccount(1000)
account:deposit(500)
print(account:getBalance()) -- 1500
-- print(account.balance) -- nil,无法直接访问
-- 使用元方法控制属性访问
function createClass()
local private = {}
local public = {}
public.__index = function(t, k)
return private[k] or public[k]
end
public.__newindex = function(t, k, v)
if k:sub(1, 1) == "_" then
error("不能设置私有属性: " .. k)
end
private[k] = v
end
return setmetatable({}, public)
end
local obj = createClass()
obj.name = "张三" -- 正常
-- obj._private = "test" -- 报错
Calculator = {}
Calculator.__index = Calculator
Calculator.count = 0 -- 类变量
function Calculator:new()
local obj = {}
setmetatable(obj, Calculator)
Calculator.count = Calculator.count + 1
return obj
end
-- 实例方法
function Calculator:add(a, b)
return a + b
end
-- 类方法
function Calculator.getCount()
return Calculator.count
end
-- 使用
local c1 = Calculator:new()
local c2 = Calculator:new()
print(Calculator.getCount()) -- 2
print(c1:add(5, 3)) -- 8
-- 定义接口
Drawable = {}
function Drawable:draw()
error("必须实现draw方法")
end
-- 实现接口
Circle = {}
setmetatable(Circle, {__index = Drawable})
Circle.__index = Circle
function Circle:new(radius)
local obj = {radius = radius}
setmetatable(obj, Circle)
return obj
end
function Circle:draw()
print("绘制半径为" .. self.radius .. "的圆")
end
-- 使用
local circle = Circle:new(5)
circle:draw()
Vector = {}
Vector.__index = Vector
function Vector:new(x, y)
local obj = {x = x, y = y}
setmetatable(obj, Vector)
return obj
end
-- 重载加法
function Vector.__add(v1, v2)
return Vector:new(v1.x + v2.x, v1.y + v2.y)
end
-- 重载减法
function Vector.__sub(v1, v2)
return Vector:new(v1.x - v2.x, v1.y - v2.y)
end
-- 重载乘法(标量)
function Vector.__mul(v, scalar)
if type(v) == "number" then
v, scalar = scalar, v
end
return Vector:new(v.x * scalar, v.y * scalar)
end
-- 重载tostring
function Vector.__tostring(v)
return string.format("Vector(%d, %d)", v.x, v.y)
end
-- 使用
local v1 = Vector:new(3, 4)
local v2 = Vector:new(1, 2)
local v3 = v1 + v2
print(v3) -- Vector(4, 6)
-- 定义混入
Serializable = {}
function Serializable:serialize()
local result = {}
for k, v in pairs(self) do
if type(v) ~= "function" then
table.insert(result, k .. "=" .. tostring(v))
end
end
return table.concat(result, ",")
end
-- 应用混入
function mixin(target, source)
for k, v in pairs(source) do
if target[k] == nil then
target[k] = v
end
end
end
-- 使用
User = {}
User.__index = User
function User:new(name, email)
local obj = {name = name, email = email}
setmetatable(obj, User)
return obj
end
mixin(User, Serializable)
local user = User:new("张三", "zhang@example.com")
print(user:serialize()) -- name=张三,email=zhang@example.com
-- 单例实现
Singleton = {}
local instance = nil
function Singleton:getInstance()
if not instance then
instance = {}
setmetatable(instance, {__index = Singleton})
instance:init()
end
return instance
end
function Singleton:init()
self.data = {}
end
function Singleton:setData(key, value)
self.data[key] = value
end
function Singleton:getData(key)
return self.data[key]
end
-- 使用
local s1 = Singleton:getInstance()
local s2 = Singleton:getInstance()
s1:setData("name", "张三")
print(s2:getData("name")) -- 张三
print(s1 == s2) -- true
-- 产品类
Button = {}
Button.__index = Button
function Button:new(text)
local obj = {text = text}
setmetatable(obj, Button)
return obj
end
function Button:render()
print("渲染按钮: " .. self.text)
end
Input = {}
Input.__index = Input
function Input:new(placeholder)
local obj = {placeholder = placeholder}
setmetatable(obj, Input)
return obj
end
function Input:render()
print("渲染输入框: " .. self.placeholder)
end
-- 工厂
UIFactory = {}
function UIFactory.create(type, ...)
if type == "button" then
return Button:new(...)
elseif type == "input" then
return Input:new(...)
end
end
-- 使用
local btn = UIFactory.create("button", "提交")
local input = UIFactory.create("input", "请输入用户名")
btn:render()
input:render()