Lua实现继承

原文链接:https://www.jianshu.com/p/fefe11d4544e

Lua元表使用 中的__index元方法可以实现面向对象和继承关系:

lua中没有类的概念,只有table,但可以用__index模拟类和对象:

local A = {}

function A:new(name)
    self.__index = self
    
    return setmetatable({ 
        name = name 
    }, self)
end

function A:print()
    print("name is ", self.name)
end

local a = A:new("Tony")
a:print()
-- output: name is Tony

A和a两个表之间的关联是这样的:

这里name是表a的key,print是表A的函数,当用a调用print时,找到的元表A中的__index(指向A自己)中的print方法,而方法内的self是调用者a,所以self.name是a的name。 (Lua中的self)

两个类之间同样可以用__index实现继承关系:

local Person = {}
function Person:new(name)
    self.__index = self
    return setmetatable({
        name = name
    }, self)
end
function Person:print()
    print("name is ", self.name)
end

local Student = setmetatable({}, Person)
Student.super = Person
function Student:new(name, score)
    self.__index = self

    local student = self.super:new(name)
    student.score = score

    setmetatable(student, self)
    return student
end
function Student:print()
    self.super.print(self)
    print("score is ", self.score)
end


local s = Student:new("Tony", 98)
s:print()

-- output :
-- name is Tony
-- score is 98

用图表示这三个table的关系:

在调用s:print()时,由于s没有print这个函数,找到的s的元表Student的__index(指向Student自己)中的print函数,

首先执行self.super.print(self),这里self是调用者s,s没有super这个属性,同样是找到元表Student中的super即Person,执行Person的print打出name is Tony(这里用点调用函数传入的第一个参数self还是s(Lua中的self)

然后第二行打印score即s.score;

当继承关系比较复杂时,这种调用显得比较混乱且容易出问题,可以封装一个Object基类,实现继承关系链,方便方法调用且减少出问题的几率。

实现面向对象的Object基类:

将设置__index和setmetatable的操作统一写在Object类里,方便使用和减少出错,一共有两处:

实现继承关系时:在Object的方法中实现继承关系(设置__index和元表的一系列操作)

local Object = {}
Object.__index = Object
function Object:new()
end

function Object:extend()
    local SubClass = {}
    for k, v in pairs(self) do
        if k:find("__") == 1 then
            SubClass[k] = v
        end
    end
    SubClass.__index = SubClass
    SubClass.super = self
    setmetatable(SubClass, self)
    return SubClass
end

创建实例对象时:用__call实现构造方法(new方法中的设置元表)

function Object:__call(...)
    local object = setmetatable({}, self)
    object:new(...)
    return object
end

上面Student的例子可以写成:

local Person = Object:extend()
function Person:new(name)
    Person.super.new(self)
    self.name = name
end
function Person:print()
    print("name is ", self.name)
end

local Student = Person:extend()
function Student:new(name, score)
    Student.super.new(self, name)
    self.score = score
end
function Student:print()
    Student.super.print(self)
    print("score is ", self.score)
end

local s = Student("Tony", 98)
s:print()
-- output :
-- name is Tony
-- score is 98

ps. 要注意的是,在子类调用父类方法时,尽量都是用 ClassName.super 而不要用self.super,因为lua里的self是不确定的。(当传入self调用的父类方法中也有self.super时会进入死循环) metatable:表示文件的元表 元表里记录了 函数、table访问、操作符行为

local Base = {
--定义要使用的成员变量
_arrData = {}
}

function Base:new(oMataTable )
    --实例化后的对象
    oMataTable = oMataTable or {}

    --将Base的元表 也就是函数说明、操作符、table访问都复制到新对象上 
    --简单的解释就是 oMataTable 表有了Base的所有函数和table操作
    setmetatable(oMataTable, self)

    --这句很重要 因为self会根据table自身变化
    self.__index = self

    return oMataTable
end

function Base:SetData(key, value)
    self.arrData[key] = value
    print(key, self.arrData[key])
end

function Child:GetData(key)
    return self.arrData[key]
end 

return Base
local Child = {
--定义要使用的成员变量
_iData = 11
}

--这句作用就是将Child定义的成员保留下来_iData 然后再继承
Child = require("class.Base"):new(Child)

--覆盖基类函数
function Child:SetData(key, value)
    self.arrData[key] = value + self._iData 
    print(key, self.arrData[key])
end

return Child
function TestClass()
    local base = require("class.Base"):new()
    base:SetData(1, 10)
    --输出 1,10
    local childTest = require("class.Child"):new()
    childTest :SetData(1, 10)
    --输出 1,21 父类方法被覆盖了
    print(childTest :GetData(1))
    --输出 20 子类继承了父类方法
end

分析: childTest 又是由Child创建而来,local childTest = require(“class.Child”):new() 这个时候内部的 self 代表的又是 Child 本身而并不是 Base self.__index = self 就指明访问表数据的时候查找 Child 本身 这样一直包装下去就实现了类继承和覆盖

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Lua函数的冒号调用和点调用

    冒号定义函数中的self指向函数所属表对象,即self是table类型,通过self表可以:访问挂载在该表下的所有冒号定义函数 如,有定义A={},A:b()...

    bering
  • Python文件

    遍历文件列表返回一个元组,元组内容为(dirpath, dirnames, filenames)

    bering
  • Cocos Creator创建客户端和服务端的连接

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明...

    bering
  • Python要self的理由

    Python的类的方法和普通的函数有一个很明显的区别,在类的方法必须有个额外的第一个参数 (self ),但在调用这个方法的时候不必为这个参数赋值 (显胜于隐 ...

    py3study
  • Python 面向对象 组合-多态与多态

      通过为某一个对象添加属性(属性的值是另外一个类的对象)的方式,可以间接地将两个类关联/整合/组合到一起

    py3study
  • 测试开发进阶(五)

    输入与次对象相关的运行时上下文。如果存在的话,with语句将绑定该方法的返回值到该语句的as子语句中指定的目标

    zx钟
  • 组合、封装、多态

    继承:一种类与类的关系,一种什么是什么的关系,子类是父类的从属关系。 组合:对象与对象的关系,一种什么有什么的关系,一个对象拥有另一个对象。 组合优点:让类...

    GH
  • python开发第六篇--递归函数和面

    面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。

    py3study
  • day25-python之继承组合

    py3study
  • python_面向对象笔记

    py3study

扫码关注云+社区

领取腾讯云代金券