Lua 面向对象,实现类的创建和实例化、封装、继承、多态

目录

面向对象三大特性包括:封装、继承、多态。

还有在Lua中如何创建类和实例化,这里一一介绍

1.1、Lua类的创建和实例化

Test1.lua

--name,age就相当于字段。eat就相当于方法
person = {name = 'Ffly',age = 20}
function person:eat()
        print(self.name .. '该吃饭饭了,饿死了')
end

--这个方法用于实例化使用
function person:new()
        local self = {}
        --使用元表,并把__index赋值为person类
        setmetatable(self,{__index = person})
        
        return self
end

Test2.lua

--加载模块Test1.lua(类似于C#中的using引用)
--LuaStudio默认从软件根目录下加载
require "Test1"

--实例化person类
person1 = person:new()

person1:eat() --正常输出

1.2、Lua封装

--对age字段进行封装,使其只能用get方法访问
function newPerson(initAge)
        local self = {age = initAge};
        
        --三个方法
        local addAge = function(num)
                self.age = self.age + num;
        end
        local reduceAge = function(num)
                self.age = self.age - num;
        end
        local getAge = function(num)
                return self.age;
        end
        
        --返回时只返回方法
        return {
                addAge = addAge,
                reduceAge = reduceAge,
                getAge = getAge,
        }
end

person1 = newPerson(20)

--没有使用额外的参数self,用的是那里面的self表
--所以要用.进行访问
person1.addAge(10)
print(person1.age)        --输出nil
print(person1.getAge())   --输出30

1.3、Lua继承


--基类person,boy类继承于person
person = {name = "default",age = 0}
function person:eat()
        print(self.name .. '该吃饭饭了,饿死了')
end

--使用元表的 __index完成继承(当访问不存在的元素时,会调用)
function person:new(o)
        --如果o为false或者o为nil,则让o为{}
        o = o or {}
        setmetatable(o,self)
        --设置上面self的__index为表person
        self.__index = self
        
        return o
end

--相当于继承
boy = person:new()

--name在boy里找不到会去person里面找
print(boy.name)   --输出default
--修改了person里的值,并不会影响boy里面的值
boy.name = 'feifei'

print(person.name)   --输出default
print(boy.name)      --输出feifei

1.4、Lua多态

person = {name = "default",age = 0}

--重载
--简单方法:lua中会自动去适应传入参数的个数,所以我们可以写在一个方法里面

function person:eat(food)
        if food == nil then
                print(self.name .. '该吃饭饭了,饿死了')
        else
                print(self.name .. '喜欢吃:' .. food)
        end
end

function person:addAge(num)
        if num == nil then
                self.age = self.age + 1
        else
                self.age = self.age + num
        end
end

print(person:eat())
print(person:eat("大西瓜"))

person:addAge()
print(person.age)
person:addAge(5)
print(person.age)

--重写

function person:new(o)
        --如果o为false或者o为nil,则让o为{}
        o = o or {}
        setmetatable(o,self)
        --设置上面self的__index为表person
        self.__index = self
        
        return o
end

boy = person:new()
boy.name = "Ffly"
boy:eat()   --调用基类eat方法

--相当于重写eat方法
function boy:eat()
        print('小男孩' .. self.name .. '快要饿死了')
end
boy:eat()  --调用派生类eat方法

2、Lua面向对象进阶

2.1、class.lua的实现

class代码参考于云风大大的博客。

class.lua

--表_class的key为类,value为类的虚表
local _class={}

--为什么要使用虚表呢?
--[[
使用虚表的话,那么类本身的元素会是稳定的,
所有的变化都在虚表中进行,
这样 封装了变化、也便于继承的实现
]]


function class(super)
        
        --要创建的类class_type
        local class_type={}
        
        --构造函数,基类
        class_type.ctor=false
        class_type.super=super
        
        --class_type类型的虚表,虚表中包含class_type中的元素
        local vtb1={}
        _class[class_type]=vtb1
 
        --给类设置元表
        --在给表添加新元素时,会在虚表中也添加
        setmetatable(class_type,{
                __newindex = function(t,k,v) vtb1[k] = v end,
                __index = function(t,k) return vtb1[k] end,
        })
        

        --super不为空,表示为继承
        if super then
                setmetatable(vtb1,{__index=
                        function(t,k)
                                --从基类找要找的元素,找到就放入派生类虚表中
                                local ret=_class[super][k]
                                vtb1[k]=ret
                                
                                return ret
                        end
                })
        end
        
        --给类型class_type创建实例对象
        --1、先依次从最顶层基类中调用构造方法
        --2、然后设置元表
        class_type.new=function(...) 
                
                --生成这个类对象
                local obj={}
                
                do
                        local create
                        
                        --递归调用构造函数
                        create = function(c,...)
                                --super不为空,表示有基类
                                if c.super then
                                        create(c.super,...)
                                end

                                --调用构造函数
                                if c.ctor then
                                        c.ctor(obj,...)
                                end
                        end
                        
                        create(class_type,...)
                end
                
                --设置obj的 __index为class_type的虚表
                setmetatable(obj,{ __index=_class[class_type] })
                
                return obj
        end
 
        return class_type
end

person.lua

require "class"

--创建基类person
person = class()
person.name = "Ffly"
person.age = 20

--设置person类的构造函数
function person:ctor()
        print("person:ctor 调用");
end

function person:eat()
        print(self.name .. "很饿,想吃东西")
end


--创建派生类boy,基类为person
boy = class(person)

function boy:ctor()
        print("boy:ctor 调用");
end

function boy:eat()
        print("boy " .. self.name .. "很饿,想吃东西")
end


--创建完两个类后,就可以使用了。
--创建boy类的实例boy1
boy1 = boy.new()
boy1:eat()


--[[
输出:
person:ctor 调用
boy:ctor 调用
boy Ffly很饿,想吃东西
]]

2.2、单例模式的实现

Boy.lua

require "class"

boy = class()

--单例模式的实现
boy.Instance = function()
        if (nil == boy.m_instance) then
                boy.m_instance = boy.new();
        end
        return boy.m_instance
end

function boy:ctor()
end

Singleton.lua

require "boy"

local b1 = boy.Instance()
local b2 = boy.Instance()

if b1==b2 then
        print("相等")
else
        print("不相等")
end

--输出相等