lua学习笔记 - 面向对象+元表

101 阅读4分钟

1.元表

通过设置元表可以让我们对两个table进行特定操作,如(_add 可以定义两个table的相加操作)。

  • setmetatable(table,metatable):  对指定 table 设置元表(metatable),如果元表(metatable)中存在 __metatable 键值,setmetatable 会失败。
  • getmetatable(table):  返回对象的元表(metatable)。
  • _index元方法: 当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键。
  • _newindex元方法: 当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值操作。

[参考](Lua 元表(Metatable) | 菜鸟教程)

-- 设置元表
mytable = {}                          -- 普通表 
mymetatable = {}                      -- 元表
setmetatable(mytable,mymetatable)     -- 把 mymetatable 设为 mytable 的元表

-- __index元方法测试
mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
print(mytable.key1,mytable.key2) -- value1  metatablevalue

-- __newindex元方法测试
mymetatable = {}  
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })  
print(mytable.key1)  -- value1
mytable.newkey = "新值2"  
print(mytable.newkey,mymetatable.newkey) -- nil 新值2
mytable.key1 = "新值1"  
print(mytable.key1,mymetatable.key1) -- 新值1 nil

2.lua面向对象

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

[参考](Lua 面向对象(实现类的创建和实例化、封装、继承、多态) - Fflyqaq - 博客园)

2.1 lua类创建与实例化

person.lua

-- 字段
person = {name = 'kk',age = 18}
-- 方法
function person:run()
    print('kk 移动')
end
-- 实例化
function person:new()
    local self = {}
    -- 使用元表,将_index赋值为person类
    setmetatable(self,{__index = person})
    return self
end

test.lua

require 'person'
-- 实例化person类
p1 = person:new()
-- 输出
p1.run()

2.2 lua封装

-- 实现对字段进行封装,使其只能通过方法进行访问
function person(name,age)
    -- 初始化
    local self = {
        _name = name,
        _age = age
    }
    
    -- 封装方法
    local getName = function()
        return self._name
    end
    
    local getAge = function()
        return self._age
    end
    
    local setAge = function(age)
        self._age = age
    end
    
    local printInfo = function()
        print('name='..self._name..' age='..self._age)
    end
    
    -- 只返回方法
    return{
        getName = getName,
        getAge = getAge,
        setAge = setAge,
        printInfo = printInfo
    }
end

p1 = person('kk',10)

print(p1._age) -- nil
print(p1.getAge()) -- 10
p1.setAge(20)
print(p1:getAge()) -- 20
p1:printInfo() -- name=kk age=20

2.3 lua继承

-- 基类 person 子类 boy

person = { name ='kk',age = 10}

function person:new(t)
	-- t 为空时,返回一个新表
	t = t or {}
	setmetatable(t,self)
	self.__index = self
	return t
end

function person:say()
	print('my name is '..self.name)
end

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

-- boy中找不到会去person中找
boy:say() -- my name is kk
-- 重新赋值
boy.name = 'kangkang'
boy:say() -- my name is kangkang

-- 修改boy中的值不会影响person
person:say() -- my name is kk

2.4 lua多态

person = {
	name = 'kk',
	age = 10
}

-- 重载:lua会自动适应传入参数的个数
function person:getInfo(name,age)
	if name == nil then
		name = self.name
	end
	if age == nil then
		age = self.age
	end
	print('name:',name,age)
end

function person:new(o)
	o = o or {}
	setmetatable(o,self)
	self.__index = self
	return o
end

person:getInfo() -- name:   kk      10
person:getInfo('KK',100) -- name:   KK      100

boy = person:new()
--调用基类方法
boy:getInfo('boy',100) -- name:   boy     100 
-- 重写
function boy:getInfo(name,age)
	if name == nil then
		name = self.name
	end
	if age == nil then
		age = self.age
	end
	print('my name is '..name..' age is '..age)
end

boy:getInfo() -- my name is kk age is 10

2.5 class.lua 实现类

-- 表_class的key为类,value作为类的虚表,存储类的字段、方法
local _class = {}

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

function class(super)
	-- 要创建的类class_type
	local class_type = {}

	-- 构造函数 ctor-是否存在构造函数 super-记录父类,基类是nil
	class_type.ctor = false
	class_type.super = super

	-- class_type类型的虚表,虚表中包含class_type中的元素  
	local vtb1 = {}
	_class[class_type] = vtb1

	-- 设置元表
	-- 给表添加新元素时,会在虚表中也添加
	setmetatable(class_type,{
		-- __newindex:用于对表更新,当对当前表缺少的索引赋值时,解释器会查找__newindex元方法,如果存在则会调用这个函数
		__newindex = function(t,k,v)
			vtb1[k] = v
		end,
		-- __index:用于查找,当查找当前表不存在的索引时,解释器会查找__index元方法,如果存在则会调用这个函数
		__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 o = {}

		do
			-- 递归调用构造函数
			local create
			create = function(c,...)
				-- super不为空说明有基类
				if c.super then
					create(c.super,...)
				end
				-- 调用构造函数
				if c.ctor then
					c.ctor(o,...)
				end
			end
			create(class_type,...)
		end

		-- 设置o的__index为class_type的虚表(给新对象赋值类的属性)
		setmetatable(o,{ __index = _class[class_type] })
		return o
	end

	return class_type
end

-- 创建基类
person = class()

person.name = 'zz'
person.age = 10
print(person.name) -- zz

-- 设置person类的构造函数
function person:ctor()
	print('person ctor')
end

function person:run()
	print('person run')
end

-- 创建派生类boy,基类为person
boy = class(person)
boy:run() -- person run
function boy:ctor()
	print('boy ctor')
end

function boy:run()
	print('boy run')
end

-- 创建对象
local boy1 = boy.new() -- person ctor -- boy ctor
boy1:run() -- boy run

2.6 单例模式

apple.lua

require "class"

apple = class()
apple.color = 'empty'
-- 单例
apple.Instance = function ()
	if nil == apple.m_instance then
		apple.m_instance = apple.new()
	end
	return apple.m_instance
end

function apple:ctor()
	print('apple ctor')
end

local a1 = apple.Instance()
a1.color = 'red'
local a2 = apple.Instance()
a2.color = 'green'

print('a1 color is '..a1.color) -- a1 color is green
print('a2 color is '..a2.color) -- a2 color is green