Lua Metatable类实现代码及相关技术疑问
Lua 类实现代码与问题解答
完整实现代码
local Class = {} Class.__index = Class -- 默认构造实现 function Class:new() print("Bye!!") end -- 从基类派生新类的方法 function Class:derive(type) local cls = {} cls.type = type cls.__index = cls cls.super = self setmetatable(cls, self) return cls end -- 允许把类本身当构造函数调用的元方法 function Class:__call(...) local inst = setmetatable({},self) inst:new(...) return inst end -- 获取类类型的方法 function Class:get_type() return self.type end -- 派生Player类 local Player = Class:derive("Player") function Player:new(name) print("hello " .. name) end -- 创建Player实例 plyr1 = Player('Dylan')
问题解答
1. 基类默认new方法的设计作用
这个打印"Bye!!"的默认new本质是占位兜底实现,作用有两个:
- 防止运行时报错:如果某个派生类没有自定义
new构造方法,创建实例时顺着元表查找至少能找到这个可执行的默认实现,不会触发"尝试调用空值"的错误。示例里打印内容只是演示用,实际开发中你完全可以把它替换成空函数。 - 统一父类构造调用入口:如果子类重写了
new需要调用父类构造逻辑时,不需要额外判断父类是否实现了构造方法,直接通过类.super.new(self, 参数)的方式就能调用到上层构造,整个继承体系的构造调用规则是统一的。
2. 为什么创建Player实例时调用的是Player自定义的new而非基类默认new
你误解了__call方法里self的指向,以及Lua的元表查找规则:
- 首先,
__call虽然定义在基类Class上,但当你执行Player('Dylan')时,触发元方法的主体是Player表本身,Lua会把Player作为第一个参数传给__call,也就是说__call函数内部的self根本不是基类Class,而是Player。 - 接下来
setmetatable({}, self)创建新实例时,给实例设置的元表就是Player,而Player的__index字段指向Player自身。 - 执行
inst:new(...)时,Lua会按顺序查找new方法:先找实例inst自身的表,找不到就找inst的元表也就是Player,Player里明确定义了自定义的new方法,就会直接调用这个方法打印"hello Dylan",不会继续往元表上游的Class里查找。 - 只有当派生类没有自定义
new方法时,Lua在派生类表里找不到对应方法,才会继续顺着元表链向上找到Class里的默认new实现。
内容的提问来源于stack exchange,提问作者Dylariant
相关产品推荐
相关产品推荐

