背景

lua是类是借助表的来实现的, 类被定义后, 在使用场景下, 不希望被修改。如果被修改, 则影响的类的原始定义, 影响所有使用类的地方。

例如:

--- router.lua class file
router = class()
router.xxx = function xxx end --- app.lua
router.xxx = function yyy end

故提出新的要求:

1、 对于类在应用场景下,不能修改属性。

2、 对于类在应用场景下, 不能添加新的属性。

类的实现代码:

local _M = {}

-- Instantiates a class
local function _instantiate(class, ...)
-- 抽象类不能实例化
if rawget(class, "__abstract") then
error("asbtract class cannot be instantiated.")
end -- 单例模式,如果实例已经生成,则直接返回
if rawget(class, "__singleton") then
-- _G[class]值为本class的实例
if _G[class] then
return _G[class]
end
end local inst = setmetatable({__class=class}, {__index = class})
if inst.__init__ then
inst:__init__(...)
end --单例模式,如果实例未生成,则将实例记录到类中
if rawget(class, "__singleton") then
if not _G[class] then
_G[class] = inst -- 对类对象增加实例获取接口
class.getInstance = function ( self )
return _G[class]
end -- 销毁单例,为后续建立新单例准备
class.destroyInstance = function ( self )
_G[class] = nil
end
end
end return inst
end -- LUA类构造函数
function _M.class(base)
local metatable = {
__call = _instantiate,
__index = base
} -- __parent 属性缓存父类,便于子类索引父类方法
local _class = {__parent = base} -- 在class对象中记录 metatable ,以便重载 metatable.__index
_class.__metatable = metatable return setmetatable(_class, metatable)
end --- Test whether the given object is an instance of the given class.
-- @param object Object instance
-- @param class Class object to test against
-- @return Boolean indicating whether the object is an instance
-- @see class
-- @see clone
function _M.instanceof(object, class)
local meta = getmetatable(object)
while meta and meta.__index do
if meta.__index == class then
return true
end
meta = getmetatable(meta.__index)
end return false
end return _M

他山之石

http://www.lua.org/pil/13.4.5.html

    function readOnly (t)
local proxy = {}
local mt = { -- create metatable
__index = t,
__newindex = function (t,k,v)
error("attempt to update a read-only table", )
end
}
setmetatable(proxy, mt)
return proxy
end

应用

    days = readOnly{"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"} print(days[]) --> Sunday
days[] = "Noday"
stdin:: attempt to update a read-only table

评价:

此方法虽然实现的 新需求两则, 但是不能够, 其借助有新增一级metatable, 破坏了现有的 原型链表继承结构。

__index & __newindex

local tab = {,  , aa="bb"}

setmetatable(tab, {__index={bb="cc"}, __newindex = function (t,k,v)
error("attempt to update a read-only table", )
end}) for k,v in pairs(tab) do
print(k, v)
end print(tab.bb) tab[] = tab["aa"] = tab["bb"] = tab["aabbb"] =

LOG

>lua -e "io.stdout:setvbuf 'no'" "luatest.lua"
1    1
2    6
aa    bb
cc
lua: luatest.lua:110: attempt to update a read-only table
stack traceback:
    [C]: in function 'error'
    luatest.lua:97: in function <luatest.lua:96>
    luatest.lua:110: in main chunk
    [C]: ?
>Exit code: 1

__index 释义

http://www.lua.org/pil/13.4.1.html

I said earlier that, when we access an absent field in a table, the result is nil. This is true, but it is not the whole truth. Actually, such access triggers the interpreter to look for an __index metamethod: If there is no such method, as usually happens, then the access results in nil; otherwise, the metamethod will provide the result.

    Window.mt.__index = function (table, key)
return Window.prototype[key]
end
    Window.mt.__index = Window.prototype

__newindex 释义

http://www.lua.org/pil/13.4.2.html

The __newindex metamethod does for table updates what __index does for table accesses. When you assign a value to an absent index in a table, the interpreter looks for a __newindex metamethod: If there is one, the interpreter calls it instead of making the assignment. Like __index, if the metamethod is a table, the interpreter does the assignment in that table, instead of in the original one.

这两个元表中的属性, 首先是以元方法的形式出现的, 然后做了易用性接口适配, 可以是表。

不引入metatable情况下,实现类属性只读

在类表中引入 __prototype 表, 专门用于存储 建立的 类field

__index 从__prototype中查找

__newindex 设置field到 __prototype 表中

oopclass

local _M = {}

-- Instantiates a class
local function _instantiate(class, ...)
-- 抽象类不能实例化
if rawget(class, "__abstract") then
error("asbtract class cannot be instantiated.")
end -- 单例模式,如果实例已经生成,则直接返回
if rawget(class, "__singleton") then
-- _G[class]值为本class的实例
if _G[class] then
return _G[class]
end
end local inst = setmetatable({__class=class}, {__index=class})
if inst.__init__ then
inst:__init__(...)
end --单例模式,如果实例未生成,则将实例记录到类中
if rawget(class, "__singleton") then
if not _G[class] then
_G[class] = inst -- 对类对象增加实例获取接口
class.getInstance = function ( self )
return _G[class]
end -- 销毁单例,为后续建立新单例准备
class.destroyInstance = function ( self )
_G[class] = nil
end
end
end return inst
end -- LUA类构造函数
function _M.class(base)
local metatable = {
__call = _instantiate,
} -- 先查原型表,然后查父亲类
metatable.__index=function(t, k)
local v = t.__prototype[k]
if v then
return v
end local parent = t.__parent
if parent then
return parent[k]
end return nil
end -- 缓存类的field
metatable.__newindex=function (t,k,v)
rawset(t.__prototype, k, v)
end local _class = {}
-- __parent 属性缓存父类
_class.__parent = base or {}
-- 存储此类的所有field
_class.__prototype = {} -- 在class对象中记录 metatable ,以便重载 metatable.__index
_class.__metatable = metatable -- 将类冷冻,不允许新建删除修改
_class.freeze = function ( self )
local mt = getmetatable(self) mt.__newindex=function (t,k,v)
error("class is frozen, cannot revise")
end
end return setmetatable(_class, metatable)
end --- Test whether the given object is an instance of the given class.
-- @param object Object instance
-- @param class Class object to test against
-- @return Boolean indicating whether the object is an instance
-- @see class
-- @see clone
function _M.instanceof(object, class)
local objClass = object.__class
if not objClass then
return false
end while objClass do
if objClass == class then
return true
end
objClass = objClass.__parent
end return false
end return _M

app

local oopclass = require("oopclass")

local class = oopclass.class
local instanceof = oopclass.instanceof local superTab = class()
superTab.test = function ( self )
print("superTab test")
end superTab:freeze() superTab.test2 = function ( self )
print("superTab test2")
end local tab = class(superTab) local tabObj = tab()
tabObj:test() print( instanceof(tabObj, tab) ) print( instanceof(tabObj, superTab) )

LOG:

>lua -e "io.stdout:setvbuf 'no'" "luatest.lua"
lua: .\oopclass.lua:85: class is frozen, cannot revise
stack traceback:
    [C]: in function 'error'
    .\oopclass.lua:85: in function <.\oopclass.lua:84>
    luatest.lua:17: in main chunk
    [C]: ?
>Exit code: 1

去掉freeze语句 superTab:freeze()

LOG:

>lua -e "io.stdout:setvbuf 'no'" "luatest.lua"
superTab test
true
true
>Exit code: 0

lua 类支持属性不能被修改的更多相关文章

  1. python添加、修改、删除、访问类对象属性的2种方法

    1.直接添加.修改.删除.访问类对象属性 class Employee (object): empCount = 0 def __init__(self, name, salary) : self.n ...

  2. Selectivizr-让IE6~8支持CSS3伪类和属性选择器

    一.下载和demo 下载: 您可以狠狠地点击这里:selectivizr-1.0.0.zip(右键-[目标|链接另存为] 9.24K) demo: 您可以狠狠地点击这里:Selectivizr部分属性 ...

  3. Python小白学习之如何添加类属性和类方法,修改类私有属性

    如何添加类属性和类方法,修改类私有属性 2018-10-26  11:42:24 类属性.定义类方法.类实例化.属性初始化.self参数.类的私有变量的个人学习笔记 直接上实例: class play ...

  4. Python 类中__init__()方法中的形参与如何修改类中属性的值

    一.__init__()方法 如果__init__()方法为 class Cat(): def __init__(self,num) : self.num=num Python中类的__init__( ...

  5. XML之自动生成类,添加,修改,删除类的属性

    1. class ClassHelperDemo { public static void Main() { #region 演示一:动态生成类. //生成一个类t. Type t = ClassHe ...

  6. ibernate学习笔记5---实体类或属性名与数据库关键字冲突、hql命名参数、hql实现通用分页

    一.实体类或属性名与数据库关键字冲突问题1.实体类名与数据库中的关键字冲突比如:实体表User与oracle中的系统表冲突解决方式1:在xml中添加table属性,指定表名,使其不与name默认相等 ...

  7. 解决和排查 "必须使用适当的属性和方法修改 User-Agent" 错误时遇到的一些坑

    解决 必须使用适当的属性和方法修改 User-Agent 错误 问题描述:近在项目中有一个需求为需要在 Http 的Header里面添加一个User-Agent参数,当请求时.项目本身的目标框架是 . ...

  8. guxh的python笔记六:类的属性

    1,私有属性 class Foo: def __init__(self, x): self.x = x 类的属性在实例化之后是可以更改的: f = Foo(1) print(f.x) # 1 f.x ...

  9. c#自定义ORM框架---(泛型&反射&实体类扩展属性<附带通用增、删、查、改>)

    该教材主要是运用到泛型.反射和实体类扩展属性 步骤一.建立扩展属性类 实体类扩展属性要继承Attribute基类完成 [AttributeUsage(AttributeTargets.Property ...

随机推荐

  1. Vue#表单控件绑定

    使用v-model 在表单控件上实现数据双向绑定. 单选:https://jsfiddle.net/miloer/bs49p0fx/ <input type="checkbox&quo ...

  2. 使用Flexible实现手淘H5页面的终端适配

    拿到设计师给的设计图之后,剩下的事情是前端开发人员的事了.而手淘经过多年的摸索和实战,总结了一套移动端适配的方案--flexible方案. 这种方案具体在实际开发中如何使用,暂时先卖个关子,在继续详细 ...

  3. eclipse运行速度优化(解决狂读盘、发布慢、CPU100%等问题)

    转自:http://blog.csdn.net/wyp_810618/article/details/8953921 我的ECLIPSE运行时速度奇慢,具体表现为: 1.只要ECLIPSE启动后,硬盘 ...

  4. Js-知识小总结

    1.给标签价 class属性的时候,记住是 ClassName, 例如:var oImg = document.getElementById('img1'); 要给这个对象价class属性 oimg. ...

  5. Java基础知识梳理《一》

    一.Java数据类型(简单称之为“四类八种”) java 基本的数据类型长度都是固定的,好处是在实现跨平台时就统一了. 1.整型 byte short int long (分别是1,2,4,8个字节) ...

  6. Qt quick 编程

    greaterThan(QT_MAJOR_VERSION,4):QT += widgets.在Qt 5之前,没有独立的QtWidgets模块,Qt Widgets包含在QtGui模块中. TARGET ...

  7. 【Ajax 基础学习】

    http://www.cnblogs.com/guduoduo/p/3681296.html 今天简单的学习了 Ajax 的基础知识,总结在这里.部分代码不是原创,特此说明. [Ajax 简介] AJ ...

  8. JavaScript创建对象

    最佳方式 原型模式与构造模式组合使用 先上代码: function Person(name,age,salary){ this.name = name; this.age = age; this.sa ...

  9. java实现a+b的多重输入

    import java.util.Scanner; public class hello { public static void main(String[] args) { Scanner cin ...

  10. C# DataTable 行转列

    #region 根据datatable获得列名 /// <summary> /// 根据datatable获得列名 /// </summary> /// <param n ...