Object Orientation Tutorial

lua-users home
wiki

Representation of classes in Lua

Lua has no built-in notion of "classes" for use in object-oriented programming. However, we can simulate many features of the classes of other languages by using just the tables and metamethods discussed previously (MetamethodsTutorial). To declare a class in Lua, we will need (1) a constructor (String:new below), (2) a class method table (String), and (3) a ''class metatable' (mt).

The method table is an ordinary table containing functions. These functions constitute the methods of the class. The constructor is a function which, when called, sets up a new table (our instance object) and attaches the class metatable to it. Finally, the metatable is simply a metatable which redirects unrecognized events to the class method table (as well as possibly handling events itself).

The following is a rearranged version of the String example from the MetamethodsTutorial:

> String = {}
> mt = {}
>
> function String:new(s)
>>   return setmetatable({ value = s or '' }, mt)
>> end
>
> function String:print()
>>   print(self.value)
>> end
>
> mt.__add = function (a,b) return String:new(a.value..b.value) end
> mt.__mul = function (a,b) return String:new(string.rep(a.value, b)) end
> mt.__index = String -- redirect queries to the String table
>
> s = String:new('hello ')
> s:print()
hello
> a = ((String:new('hello ') + String:new('Lua user. '))*2)
> a:print()
hello Lua user. hello Lua user.
We create a table String to hold our String class methods. Instead of having a functional interface (e.g. String.print(s)) we would like to call the object, e.g. s:print().

It is also possible to eliminate mt and use String instead for all cases where mt is used.

Method calling conventions

Note in the example that we use the : operator to declare and call the class methods. This is syntactic sugar for String.print(s), or s.print(s), i.e. the colon makes Lua put the object we are calling as the first parameter in the function call. The following all have the same result by different methods:

> s.print(s)       -- use __index metamethod but no sugar
hello
> s:print()        -- use __index metamethod and sugar
hello
> String.print(s)  -- call String directly, no metamethod or sugar
hello

Method declarations

Likewise there is syntactic sugar for method declarations. Regardless how it is declared, a method expects that first argument passed in is the object to be acted on. If a dot is used (i.e. t.foo(self, args)) we declare self ourselves. If a colon is used (i.e. t:foo(args)) self will be declared automatically for us.

> t = {}
> function t.foo(self,x) print(self,x) end  -- no sugar, explicit self
> function t:foo2(x) print(self,x) end      -- sugar and magic self
>
> t.foo(t,1)         -- is the same as...
table: 0035D830 1
> t:foo2(1)          -- shorthand for above
table: 0035D830 1
And reversed:
> t:foo(1)           -- the same as...
table: 0035D830 1
> t.foo2(t,1)
table: 0035D830 1

Notes on the convention

The colon calling and method declaration styles are shortcuts, not a rigid style of programming; As the examples show, you can mix and match styles.

Explicitly declaring self

If you don't like the automatic appearance of the self argument in function declarations you might choose the following style, where self is explicitly declared:

> foo = { value = 0 }
> function foo.add(self, x) self.value = self.value+x end
> function foo.print(self) print (self.value) end
> foo:print()
0
> foo:add(123)
> foo:add(99)
> foo:print()
222

Defining functions in a table constructor

Please note that if you declare your class methods in a table constructor you'll have to declare self explicitly as using the colon is not an option.

> foo = {
>>     value = 0,
>>     print = function(self) print(self.value) end,
>>     add = function(self, x) self.value = self.value + x end
>> }
>
> foo:print()
0
> foo:add(77)
> foo:print()
77

See Also


FindPage · RecentChanges · preferences
edit · history
Last edited March 7, 2007 4:42 am GMT (diff)