Lua没有类与对象的概念,但是可以用下面的方法来模拟出面向对象的效果来实现封装、继承、多态的面向对象特点

模拟类和对象,构造函数

--初始化类
Person = {name,age,gender,address = "china"}
--模拟方法
function Person:Show()
    print(self.name,self.age,self.gender,self.address)
end
--模拟构造方法
function Person:New(name,age,gender,address)
    --初始化一个新的表
    local obj = {}
    --把当前的表设置为新表的元表
    --setmetatable(obj,Person)
    setmetatable(obj,self)
    --指定元表的index索引
    --Person.__index = Person
    self.__index = self
    self.name = name
    self.age = age
    self.gender = gender
    self.address = address
    return obj
end
--实例化对象
--xiaoming = Person.New(Person)
xiaoming = Person:New()
xiaoming.name = "xiaoming"
xiaoming.age = 10
xiaoming:Show()
xiaohong = Person:New("xiaohong",20,"female","china")
xiaohong:Show()
print(xiaoming == xiaohong)

--输出
--[[
xiaoming    10  nil nil
xiaohong    20  female  china
false
--]]
  • Person:New()和Person.New(Person)效果一致,使用:是指第一个参数指定为:前的类
  • setmetatable(obj,Person)和setmetatable(obj,self)效果相同,self是指是指第一个参数指定为:前的类,类似于其他语言中的this关键字
  • Person.__index = Person是当查询主表不存在时,会调用元表的index参数,在指定的表中继续查询
  • local obj = {}中obj只会在构造方法中使用,lua作用域默认为公开所以最好将其私有化

模拟继承、多态

--创造一个父类
Animal = {name}
--父类构造方法
function Animal:New(name)
    local obj = {}
    setmetatable(obj,self)
    self.__index = self
    self.name = name
    return obj
end
--父类普通方法
function Animal:Sleep()
    print(self.name.."睡觉")
end
--子类继承父类
Cat = Animal:New()
--子类构造方法
function Cat:New(name)
    --子类构造方法和父类构造方法的不同点
    local obj  = Animal:New(name)
    setmetatable(obj,self)
    self.__index = self
    self.name = name
    return obj
end
--子类普通方法
function Cat:Eat(food)
    print(self.name.."要吃"..food)
end
--子类多态
function Cat:Sleep()
    print(self.name.."不睡觉")
end
--通过父类创建对象
tiger = Animal:New("老虎")
tiger:Sleep()
--通过子类创建对象
jjc = Cat:New("金渐层")
jjc:Eat("猫粮")
jjc:Sleep()

--输出
--[[
老虎睡觉
金渐层要吃猫粮
金渐层不睡觉
--]]

代码分离

--Animal.lua
Animal = {name,typeName}
function Animal:New(name,typeName)
    local obj = {}
    setmetatable(obj,self)
    self.__index = self
    self.name = name
    self.typeName = typeName
    return obj
end

function Animal:ToString()
    print(self.name,self.typeName)
end

--Peson.lua
Person = {name,age,gender,address}
function Person:New(name,age,gender,address)
    local obj = {}
    setmetatable(obj,self)
    self.__index = self
    self.name = name
    self.age = age
    self.gender = gender
    self.address = address
    return obj
end

function Person:ToString()
    print(self.name,self.age,self.gender,self.address)
end

--Person.lua
dofile("Animal.lua"
dofile("Person.lua")
jjc = Animal:New("金渐层","猫科动物")
jjc:ToString()
xiaoming = Person:New("小明",20,"男","中国")
xiaoming:ToString()
    
--输出
--[[
    金渐层 猫科动物
    小明  20  男   中国
--]]
  • dofile若在同一个文件夹下,直接写需要加载的文件即可
  • 不在同一个文件夹下,需要写相对路径或者绝对路径,绝对路径如果改变项目位置就会失效
    • 相对路径:
    • class\\Animal.lua 
      ..\\class\\Animal.lua
      
    • 前者表示在当前路径下的class文件夹内
    • 后者表示在当前路径上一级路径的class文件夹内
  • require和dofile的区别
    • 在加载一个.lua文件的时候,require会先在package.loaded中查找此模块是否存在,如果存在则直接返回模块,如果不存在,则加载此模块。
    • dofile会对读入的模块编译执行,每调用dofile一次,都会重新编译执行一次。
    • require的参数只是文件名,而dofile要求参数必须带上文件名的后缀。