继承教程

lua-users home
wiki

本教程演示了一种在 Lua 中实现面向对象继承的技术。在继续之前,建议您熟悉 面向对象教程元方法教程

简单类

以下示例实现了一个没有继承的类

SimpleClass = {}
SimpleClass_mt = { __index = SimpleClass }

-- This function creates a new instance of SimpleClass
--
function SimpleClass:create()
    local new_inst = {}    -- the new instance
    setmetatable( new_inst, SimpleClass_mt ) -- all instances share the same metatable
    return new_inst
end

-- Here are some functions (methods) for SimpleClass:

function SimpleClass:className()
    print( "SimpleClass" )
end

function SimpleClass:doSomething()
    print( "Doing something" )
end

在上面的示例中,SimpleClass 代表一个包含类所有方法的表,就像类声明一样。SimpleClass_mt 是我们将附加到每个创建的类实例上的元表。函数 SimpleClass:create() 创建 SimpleClass 类的实例。类实例的构造涉及创建一个空表,然后将我们的 SimpleClass 元方法附加到它上面。附加元方法的结果是,新实例会查找我们附加的元表以获取其自定义行为。

对实例的方法调用将触发实例上的“索引”事件,导致查找实例元表的“__index”成员。__index 成员仅仅是对 SimpleClass 的引用。因此,对实例的方法调用将导致在 SimpleClass 表中查找。

这是一个例子

> simple = SimpleClass:create()
> 
> simple:className()
SimpleClass
> 
> simple:doSomething()
Doing something

实现继承

现在我们要创建一个新类 SubClass,它继承并(可选地)覆盖 SimpleClass 中的函数。

-- Create a new class that inherits from a base class
--
function inheritsFrom( baseClass )

    -- The following lines are equivalent to the SimpleClass example:

    -- Create the table and metatable representing the class.
    local new_class = {}
    local class_mt = { __index = new_class }

    -- Note that this function uses class_mt as an upvalue, so every instance
    -- of the class will share the same metatable.
    --
    function new_class:create()
        local newinst = {}
        setmetatable( newinst, class_mt )
        return newinst
    end

    -- The following is the key to implementing inheritance:

    -- The __index member of the new class's metatable references the
    -- base class.  This implies that all methods of the base class will
    -- be exposed to the sub-class, and that the sub-class can override
    -- any of these methods.
    --
    if baseClass then
        setmetatable( new_class, { __index = baseClass } )
    end

    return new_class
end

函数 inheritsFrom(baseClass) 接受一个参数,即我们要从中继承的类声明。该函数返回一个类声明,然后我们可以对其进行定制。new_class 是要返回的新类声明。嵌套函数 new_class:create() 是返回的类声明的一部分,它将创建我们正在创建的子类的实例。此函数创建一个 newinst 表,该表使用我们的新类表来保存其方法。新类表反过来在找不到所需方法时查找 baseClass,从而继承其方法。

继承示例

SimpleClass 的基础上,我们现在创建一个名为 SubClass 的类,该类继承自 SimpleClass 并覆盖 className()

> -- Create a new class that inherits from SimpleClass
> SubClass = inheritsFrom( SimpleClass )
>
> -- override className() function
> function SubClass:className() print( "SubClass" ) end
>
> -- Create an instance of SimpleClass
> simple = SimpleClass:create()
> 
> simple:className()
SimpleClass



> 
> simple:doSomething()
Doing something
> 
> -- Create an instance of SubClass
> sub = SubClass:create()
> 
> sub:className()  -- Call overridden method
SubClass
> 
> sub:doSomething()  -- Call base class method
Doing something
> 

OO 属性

现在我们可以扩展我们的继承结构并添加其他语言中常见的特性,例如访问类的超类以及提供类型标识符功能的 isa() 方法。

-- A new inheritsFrom() function
--
function inheritsFrom( baseClass )

    local new_class = {}
    local class_mt = { __index = new_class }

    function new_class:create()
        local newinst = {}
        setmetatable( newinst, class_mt )
        return newinst
    end

    if nil ~= baseClass then
        setmetatable( new_class, { __index = baseClass } )
    end

    -- Implementation of additional OO properties starts here --

    -- Return the class object of the instance
    function new_class:class()
        return new_class
    end

    -- Return the super class object of the instance
    function new_class:superClass()
        return baseClass
    end

    -- Return true if the caller is an instance of theClass
    function new_class:isa( theClass )
        local b_isa = false

        local cur_class = new_class

        while ( nil ~= cur_class ) and ( false == b_isa ) do
            if cur_class == theClass then
                b_isa = true
            else
                cur_class = cur_class:superClass()
            end
        end

        return b_isa
    end

    return new_class
end

以及使用示例

> SimpleClass = inheritsFrom( nil )  -- pass nil because SimpleClass has no super class
> 
> SubClass = inheritsFrom( SimpleClass )
> 
> FinalClass = inheritsFrom( SubClass )
> 
> sub = SubClass:create()
> fc = FinalClass:create()
> 
> print( fc:isa( SubClass ) )
true
> print( fc:isa( FinalClass ) )
true
> print( sub:isa( SubClass ) )
true
> print( sub:isa( FinalClass ) )
false

替代方法:基于原型的

基于原型的编程是一种面向对象编程风格,其中不存在类,行为重用(在基于类的语言中称为继承)通过克隆现有对象(作为原型)的过程来完成。此模型也可以称为无类、原型定向或基于实例的编程。

[维基百科关于基于原型的编程的文章]

大部分代码与上面基本相同,但只简化到使“基于原型的编程”正常工作所必需的部分。更确切地说,它允许通过克隆和原型委托进行原型编程。对对象中未设置的属性的访问会委托给其原型。此代码使用 table 表作为非常基础的原型,而 object 作为 table 的特化。函数 object.isa 对于原型范式来说并非严格必需,更多的是一种便利。

函数 clone(base_object[, clone_object]) -> table

参数

返回值

如果 new_object 的类型不是 table,则返回 new_object(如果它不是 nil),在这种情况下返回 base_objectnew_object 的元表被设置为它自己,并且它的 __index 现在指向它的原型 base_objectclone 也可作为 object.clone 使用。

它在将布尔值作为任一参数传递时也可能存在问题,因为……呃……留给读者作为练习!? ;-)

函数 isa( clone_object, base_object) -> bool

参数

返回值

如果任一参数不是 table,则 isa 会回退到返回类型的比较。它也可作为 object.isa 使用。

此函数在深度原型层次结构上性能会较差。

代码

function clone( base_object, clone_object )
  if type( base_object ) ~= "table" then
    return clone_object or base_object 
  end
  clone_object = clone_object or {}
  clone_object.__index = base_object
  return setmetatable(clone_object, clone_object)
end

function isa( clone_object, base_object )
  local clone_object_type = type(clone_object)
  local base_object_type = type(base_object)
  if clone_object_type ~= "table" and base_object_type ~= table then
    return clone_object_type == base_object_type
  end
  local index = clone_object.__index
  local _isa = index == base_object
  while not _isa and index ~= nil do
    index = index.__index
    _isa = index == base_object
  end
  return _isa
end

object = clone( table, { clone = clone, isa = isa } )

示例

-- testing "isa"
foo = object:clone()
bar = object:clone()
baz = foo:clone()

print( foo:isa(object) )
print( bar:isa(foo) )
print( baz:isa(foo) )

--[[ output:
true
false
true
]]

--testing prototype delegation

foo = object:clone()
bar = foo:clone()

function foo:speak()
  print(self.thoughts or "foo has no thoughts")
end

bar:speak()

--[[ output:
foo has no thoughts
]]

bar.thoughts = "I may be a clone, but I'm an individual!"
bar:speak()

--[[ output:
I may be a clone, but I'm an individual!
]]


贡献者: KevinBaca

另请参阅


RecentChanges · preferences
编辑 · 历史
最后编辑于 2011 年 11 月 18 日 上午 6:09 GMT (差异)