Inherit metatet (class) and use its required constructor parameter

I found this tutorial: http://lua-users.org/wiki/InheritanceTutorial

I have a metatet called Creature. The entity requires an argument in its constructor. The required argument is a string that represents the name.

local creature = Creature(name)

      

  • The creature has many other methods, for example getDescription()

    .
  • The creature getDescription ()

    returns the string: "This creature."
  • The creature getName ()

    returns the string: name

I want to create a new metatable (class) called Player

and inherit from its Creature

metatable (class)

  • The Player class will only override the getDescription () method.
  • The Player class also inherits the creature's getName () method.
  • The getDescription () player returns the string: "This is the player".

I want to be able to do the following:

local creature = Creature("Bob")
print(creature:getDescription())
print(creature:getName())

local player = Player("Joey")
print(player:getDescription())
print(player:getName())

      

Should print:

This creature
Bob
This is
Joey's player

Basically my problem is that the entity class requires an argument to identify someone, a name. Its function getName ()

takes the value in the argument and prints it. If I'm going to use Player to inherit all of the Entity's functions (and override if necessary), how do I change the code to make sure the Player receives the argument it needs?

Code taken from the tutorial:

-- 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 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

      

+3


source to share


1 answer


I want to be able to do the following:

local creature = Creature("Bob")
print(creature:getDescription())
print(creature:getName())
-- ...

      

Supporting this kind of syntax for using a class is certainly possible in lua - it's just a matter of how to use language mechanisms and tools to achieve this. Some important questions to be addressed:

  • How will the construction project be built? What function should the call of the class call to initialize the instance?
  • What will the instance object look like from the client code?
  • How is method overriding done?

From the tutorial, the wiki new_class:create()

creates a new instance, but doesn't call any build function. Thus, in your OOP system, you will need to select a constructor-like function that the client code will provide and will trigger the creation of the class. For example:

function new_class:create(...)
    local instance = setmetatable( {}, class_mt )
    if new_class.__init__ then new_class.__init__(instance, ...) end
    return instance
end

      

Here I am just using __init__

as a constructor name, similar to python, but in fact any name will work as long as the code and create code match.

To maintain the syntax of creating an object, for example Creature("Bob")

, Player("Joey")

you can either make their actual function calls or use a metathet __call

. Using the latter is a simple assignment __call

:

function inheritsFrom( baseClass )
  local new_class = setmetatable( {}, { __index = baseClass } )
  -- ...
  getmetatable(new_class).__call = new_class.create
  return new_class
end

      

In the last question, you are overriding an existing method by simply assigning a new function to it in a derived class. eg. So, to override getDescription

in Player

, you can do:



function Player:getDescription()
    return "Is a player"
end

      

Putting it all together inheritsFrom

function inheritsFrom( baseClass )
    local new_class = setmetatable( {}, { __index = baseClass } )

    local class_mt = { __index = new_class }
    function new_class:create(...)
        local instance = setmetatable( {}, class_mt )
        if new_class.__init__ then new_class.__init__(instance, ...) end
        return instance
    end

    getmetatable(new_class).__call = new_class.create
    return new_class
end

      

Class definition Creature

+ one instance of a creature:

local Creature = inheritsFrom
{
  __init__ = function(self, name) self.name = name end;
  getDescription = function(self) return "Is a creature" end;
  getName = function(self) return self.name end;
}

local bob = Creature "Bob"
print(bob:getDescription())
print(bob:getName())

      

Subclassing Player

from Creature

and overriding getDescription

:

local Player = inheritsFrom(Creature)
function Player:getDescription()
    return "Is a player"
end

local joey = Player "Joey"
print(joey:getDescription())
print(joey:getName())

      

As a final note, the lua Penlight library already implements a class system similar to the one I described above, but much more functional and complete. If this is not an exercise, consider using this instead of reinventing another lua OOP system.

+3


source







All Articles