Now we can discuss how to implement inheritance using Lua’s tables. Let’s start with a sample class with the information from the previous post:
Foo = {} Foo_mt = { __index = Foo } function Foo.create() local instance = {} setmetatable( instance , Foo_mt ) return instance end function Foo.Work() print("hi") end
With this setup, you can create instances of the Foo class by doing the following:
local fooInstance = Foo.create() fooInstance.Work()
Now what if we define a function on one of these instances?
Bar = Foo.create() function Bar.Work() print("hi bar") end
At this point, if we call Work() on Bar, it will use the version in the Bar instance, rather than go look at its metatable and find Foo’s version. This is the idea we will use to get inheritance to work in Lua – by defining a new function with the same key as a parent table, you will override it. Now we just have to create a new metatable for Bar, and add a Bar.create() function to generate new tables that use Bar’s metatable:
Bar = Foo.create() Bar_mt = { __index = Bar} function Bar.create() local instance = {} setmetatable( instance , Bar_mt ) return instance end function Bar.Work() print("hi bar") end
This code actually looks a lot like how we declared Foo in the first place, except we are starting with a instance of Foo rather than a blank table. I wrote a helper function to automate all of this.
function CreateClass(base_class) local new_class = {} if base_class ~= nil then new_class = base_class.new() end local class_mt = { __index = new_class } function new_class.new() local newinst = {} setmetatable( newinst, class_mt ) return newinst end return new_class end
And then you can use it like this:
Foo = CreateClass() function Foo:Work() print("Working foo") end function Foo:Work2() print("Working foo2") end Bar = CreateClass(Foo) function Bar:Work() print("Working bar") end local FooInstance = Foo.new() local BarInstance = Bar.new() FooInstance:Work() BarInstance:Work() FooInstance:Work2() BarInstance:Work2()
Things do get a little tricker when you want to try to call into your superclass’s function in your override. There is no special keyword you can use, so you have to do it like C++ and call the superclass specifically. Since you don’t want to actually call the function on the main class table (Foo) and you want to call it on your own table instance, you can’t use Foo:Work(). You have to call Foo.Work(self), which calls Foo’s Work() function, but in the context of the current instance.
Bar = CreateClass(Foo) function Bar:Work() Foo.Work(self) print("Working bar") end
Now you know everything I know about Lua. I’ll be learning more as I go, and try to take notes on this blog. Next up I’ll go over some basic helper classes I wrote to manage resources and screens.