lua的元表以及多继承

作者: 禹子歆 | 来源:发表于2017-01-07 16:43 被阅读263次

    最近做开发的时候,有个类想要继承自多个类,虽然这种情况基本说很少(好像也不少),但还是说一下如何在lua里实现多继承。
    其实很简单,首先要了解lua元表,以及lua类的实现方法。

    lua元表

    用lua的人都知道lua的table的强大,table有一套hashmap的查找机制,如果访问一个表中并不存在的字段,不会立即返回nil,而是先触发一套查找机制,也是我们用以实现面向对象的方法
    简单的描述:元表就是用于查找的备用表。
    比如:

    local me = {}
    print(me.money) -- me中没有money字段,所以打印出来的会是nil
    

    这里的执行结果很明显会是nil,没有疑问(因为我是个穷逼。
    但是如果我设置了元表:

    local fathermayun = {
        money = 13000000000
    }
    local me = {}
    setmetatable(me, fathermayun)
    print(me.money)
    

    这里的执行结果是!!!!!!!
    nil
    为什么我没有拿到马云爸爸的钱?因为没有设置__index

    __index

    简单的描述:是当table中一个元素不存在的时候,会触发寻找元表的__index元方法,如果不存在,则返回nil,如果存在,则返回结果。
    所以,把上述代码改成

    local fathermayun = {
        money = 13000000000
    }
    fathermayun.__index = fathermayun
    local me = {}
    setmetatable(me, fathermayun)
    print(me.money)
    

    这里的执行结果将是13000000000,我终于有钱了:)
    上面的执行过程是:
    访问me.money->发现没有这个字段->me有元表->查找元表fathermayun->lua不会直接找fathermayun里的money字段->lua发现fathermayun有元方法__index->调用元方法->发现元方法是个table,在元方法的table里寻找->获得money的值
    __index有以下取值

    表,会直接在表里找
    函数,返回函数的返回值
    

    继承

    利用上述知识随手写了个实现面向对象的方法,不是很完善

    local function class(super)
        local cls
        if super then
            cls = {}
            cls.super = super
            setmetatable(cls, {__index = super})
        else
            -- ctor是构造函数的命名
            cls = {ctor = function () end}
        end
    
        cls.__index = cls
        function cls.new(...)
            local instance = setmetatable({}, cls)
            instance:ctor(...)
            return instance
        end
        return cls
    end
    

    挺简单的,注释就不写了……让我们来测试一下

    local Test = class()
    function Test:doSomething()
        print("test doSomething")
    end
    local test = Test.new()
    test:doSomething()
    

    成功打印出"test doSomething"
    接下来试试继承

    local Test = class()
    function Test:doSomething()
        print("test doSomething")
    end
    local Test2 = class(Test)
    local test = Test2.new()
    test:doSomething()
    

    也可以成功打印出"test doSomething",继承成功
    为什么能继承成功?原因如下:

    在new的时候,创建一个table并返回,即创建一个实例,实例可以有自己的字段,比如Test类的doSomething,该字段是个函数,可以调用执行。实例的元表是cls,如果调用实例没有的字段,会去cls里找
    cls设置了元方法__index = cls
    如果没有super,则只有一个构造函数方法
    如果有super,cls的元表是super,元表的元方法也正确的设置了
    所以,在Test2是继承自Test的,它的实例test调用doSomething,找不到,去元表里找,元表发现自己有父类,去父类里找,成功找到。
    

    多继承

    如果我想要继承多个父类,怎么办?
    其实思路很简单,把元方法改成函数即可。
    举例子:

    local function search(key, tables)
        for _, super in ipairs(tables) do
            if super[key] then
                return super[key]
            end
        end
        return nil
    end
    
    local function class(...)
        local cls = { ctor = function () end}
        local supers = {...}
        setmetatable(cls, {__index = function (_, key)
            -- 在查找table的时候,会把table的key传进来
            return search(key, supers)
        end})
        
        function cls.new(...)
            local instance = {}
            setmetatable(instance, {__index = cls})
            instance:ctor(...)
            return instance
        end
        return cls
    end
    
    local Human = class()
    function Human:life()
        print("almost 100 years.")
    end
    local Programmer = class()
    function Programmer:coding()
        print("sub 1 year.")
    end
    local My = class(Human, Programmer)
    local yuzixin = My.new()
    yuzixin:life()
    yuzixin:coding()
    

    成功打印出结果
    almost 100 years.
    sub 1 year.
    为什么能继承成功?原因如下:

    在yuzixin里找不到life和coding字段,去找元表cls,调用元方法__index
    __index调用函数search,把所有的父类都找一遍
    成功找到
    

    多继承就这样成功啦!撒花!
    注意写一次代码减少一年的生命哦……(悲伤doge脸

    相关文章

      网友评论

        本文标题:lua的元表以及多继承

        本文链接:https://www.haomeiwen.com/subject/onufbttx.html