zoukankan      html  css  js  c++  java
  • lua实现面向对象(一)

    c++和java语言机制中本身带有面向对象的内容,而lua设计的思想是元编程,没有面向对象的实现。

    但是利用lua的元表(matetable)机制,可以实现面向对象。要讲清楚怎样实现lua面向对象,需要讲清楚以下内容。

    1.lua元表 2.类和对象 3.继承

     

    1.lua元表

    lua里的所有数据结构都是表。metatable可以改变table的行为。例如加法行为,table本身没有加法行为。

    可以通过修改元表中的__add域,来提供表的加法行为。__add叫做元方法(metamethod)。

    一个表默认是不带元表的。getmetatable可以获取元表。

    t = {}
    print(getmetatable(t)) --nil

    setmetatable可以设置元表。

    t = {}
    setmetatable(t, t) --设置自己是自己的元表
    print(t == getmetatable(t)) --true

    任何一个表可以其他表的元表。一个表也可以是自己的元表。

    来看一个展示__add元方法的例子。实现一个集合的并集运算。

    Set = {}
    
    Set.mt = {}
    
    Set.new = function (t)
        local res = {}
        setmetatable(res, Set.mt)
        for k, v in pairs(t) do    --用原始集合的value作为新集合的key
            res[v] = true            --新集合的value值 可以是任意值
        end
        return res
    end
    
    Set.union = function (a, b)
        local res = Set.new{}    --res将Set.mt作为元表
        for k, v in pairs(a) do res[k] = true end    --res的所有key值作为新的并集,value值可以是任意值
        for k, v in pairs(b) do res[k] = true end
        return res
    end
    
    Set.mt.__add = Set.union
    
    Set.print = function (t)
        local s = "{ "
        for k, v in pairs(t) do
            s = s .. tostring(k) .. " "    --所有key值作为新的并集
        end
        s = s .. "}"
        print(s)
    end
    
    s1 = Set.new{1, 2, 3, "hello"}
    s2 = Set.new{2, 3, 4, "world"}
    
    s3 = s1 + s2
    
    Set.print(s3)   --{ 1 2 3 4 hello world}

    在new集合时将一个公共的mt表作为了集合的元表,那么通过new创建的集合都会有相同的元表,也就有了相同的行为。

    再通过定义__add域,则改变了表的“+”行为,使得“+”变成了集合的并集运算。

    可以重定义的元方法如下:

    __add --加法
    __sub --剑法
    __mul --乘法
    __div --除法
    __unm --
    __pow --
    __concat --连接
    __eq    --等于
    __lt    --小于
    __le    --小于等于
    __tostring --字符串输出
    __index    --访问表的域
    __newindex --更新表的域
    __metatable --使元表不能被修改

    其中最重要的__index,__newindex是用来实现面向对象的关键。下面一个对表的监控例子,可以看出__index,__newindex的作用。

    original = {} --原始表
    
    mt = {}
    
    mt.__index = function (t, k) --此表的访问操作,都会访问original表
        print("access table element " .. tostring(k) .. " : " .. tostring(original[k]))
        return original[k]
    end
    
    mt.__newindex = function (t, k, v) --此表的赋值操作,都会操作original表
        print("update table element " .. tostring(k) .. " : " .. tostring(original[k]) .. " to " .. tostring(v))
        original[k] = v
    end
    
    t = {} --监控表 用来监控original
    
    setmetatable(t, mt)
    t[1] = "hello"            --update table element 1 : nil to hello
    str = t[1] .. "world"    --access table element 1 : hello
    print(str)                --helloworld

     当我们访问一个表的不存在的域时,会触发访问__index方法。当表缺少一个赋值域时,会触发访问__newindex方法。

    这两个重要的元方法是实现面向对象的关键。

    2.类和对象

    我们创建一个对象作为其他对象的原型,当调用不属于该对象的方法时,会去原型中查找。

    setmetatable(a, {__index = b})

    则b是原型,a是原型的对象。概念上称b是类,a是b的实例对象。调用a中不存在的对象时,会去b中查找。

    Cal = {}
    
    function Cal:New(o)
        o = o or {}
        setmetatable(o, self)
        self.__index = self
        return o
    end
    
    function Cal:Add(a, b)
        print("Cal Add")
        return a + b
    end
    
    a = Cal:New()
    print(a:Add(5, 6)) --11

    调用Cal:New实际会返回一个以Cal自己为元表的新对象。而a中没有Add方法,则会在Cal中查找Add方法。

    3.继承

    有了类与对象,我们需要在此基础上实现继承。

    Cal = {}
    
    function Cal:New(o)
        o = o or {}
        setmetatable(o, self)
        self.__index = self
        return o
    end
    
    function Cal:Add(a, b)
        print("Cal Add")
        return a + b
    end
    
    SubCal = Cal:New()
    
    function SubCal:Add(a, b)
        print("SubCal Add")
        return a + b
    end
    
    a = SubCal:New()
    print(a:Add(5, 6)) --11

    一个SubCal子类,从基类Cal中继承了Add方法。a对象会首先在SubCal中查询方法,如果有则调用,因此子类可以复写父类的方法。如果没有则去到父类中查找方法。

  • 相关阅读:
    Lock接口与等待唤醒机制
    线程同步(线程安全处理Synchronized)与死锁
    关于线程安全的例子(电影票出售)
    线程池
    多线程
    commons-IO工具包
    打印流
    关于web的安全问题
    防止手机页面软键盘调出时布局被挤压
    css3新特性归总笔记
  • 原文地址:https://www.cnblogs.com/yao2yaoblog/p/6433553.html
Copyright © 2011-2022 走看看