3

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

I've got a metatable called Creature. Creature requires an argument in its constructor. The required argument is a string that represents the name.

local creature = Creature(name)
  • Creature has a lot of other methods, like getDescription().
  • Creature's getDescription () returns a string: "This is a creature".
  • Creature's getName () returns a string: the name

I want to create a new metatable (class) called Player and have it inherit the Creature metatable (class)

  • The Player class will override only the getDescription () method.
  • The Player class will also inherit the Creature's getName () method.
  • Player's getDescription () returns a string: "This is a 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 is a creature
Bob
This is a player
Joey

Basically, my issue is that the Creature class requires an argument to identify someone, a name. Its getName () function uses the value in the argument and prints it. If I am going to use Player to inherit all of the functions of Creature (and override if necessary), how do I change the code to make sure Player gets 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'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
greatwolf
  • 20,287
  • 13
  • 71
  • 105
Evan
  • 67
  • 4

1 Answers1

3

I want to be able to do the following:

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

Supporting that kind of class usage syntax is certainly possible in lua -- it's just a question of how to use the language mechanisms and tools to achieve that. Some important issues to decide on:

  • How will object construction happen? What function should the class call to initialize the instance?
  • What will object instantiation look like from client-code?
  • How is method overriding accomplished?

From the wiki tutorial, new_class:create() creates a new instance but doesn't call any construction function. So in your OOP system, you'll have to decide on a constructor-like function that the client-code provides and the class creation will call. 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 just use __init__ as the constructor name, similar to python, but really any name will work as long as the using code and class creation code agrees.

In order to support object creation syntax like Creature("Bob"), Player("Joey"), you can either make them actual function calls or use the __call metamethod. Using the latter is a simple assignment to __call:

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

For the last question, you override an existing method by just assigning it a new function in the derived class. eg. So to override getDescription in Player you can do:

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

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

Defining class Creature + one creature instance:

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 remark, the lua Penlight library already implements a class system similar to what I described above but much more featureful and complete. Unless this is an exercise, consider using that instead of reinventing another lua OOP system.

greatwolf
  • 20,287
  • 13
  • 71
  • 105