I am very new with metatables so please bear with me. Why is the line “Object.__index = Object”
placed where it is? From my understanding, the __index method fires when a metatable is indexed. In the provided example script, how is the metatable being indexed? Any help would be greatly appreciated!
local Object = {} --Constructor
Object.ClassName = "Object"
Object.__index = Object
function Object:GetName()
return self.Name
end
return {
new = function(name)
local newObject = {
Name = name or Object.ClassName
}
return setmetatable(newObject, Object)
end
}
Close, __index fires when the table in question doesn’t already have what’s wanted. You can think of it as a backup.
In this case, if newObject doesn’t have say .ClassName, it will default to "Object". The __index method is just telling whatever table it’s being linked to to use what it has, which is why it’s setting itself as the metamethod.
This is a pretty common structure used in Lua to simulate object-oriented-programming.
local Dog = {}
function Dog:Bark()
print(self.name .. " barked!")
end
function Dog.new(name)
local self = setmetatable({}, {__index = Dog})
self.name = name
return self
end
local buddy = Dog.new("Buddy")
local skippy = Dog.new("Skippy")
buddy:Bark() --> Buddy barked!
skippy:Bark() --> Skippy barked!
The __index metamethod tells a table what to do when it is indexed and the value is nil. It can be either a table or a function. This is useful because it allows you to create a “fallback” table that will return values at indices that are not in the “main” table. This allows you to to create a bunch of different objects that all have different values, yet share the same functions.
Here’s an simpler example of how __index can be used:
local tableA = {a = 42}
local tableB = {b = 100}
setmetatable(tableB, {__index = tableA})
-- first checks if index "b" is in tableB. it is! so 100 is returned
print(tableB.b) --> 100
-- first checks if index "a" is in tableB. it isn't. instead of returning nil,
-- it will first check if tableB has a metatable with a __index metamethod.
-- it does. so, it will check to see if "a" is in tableA and return 42
print(tableB.a) --> 42
-- in this case, we fall through both the main table & the metatable to return nil:
print(tableB.c) --> nil
You can also use a function for __index, which will be called when a table index is nil:
local tab = {a = 42}
setmetatable(tab, {
__index = function(self, key)
return string.format("The key '%s' is not in that table!", tostring(key))
end
})
print(tab.a) --> 42
print(tab.b) --> The key 'b' is not in that table!
Edit:
Lua also has functions rawget and rawset that allow you to get/set a value from a table without invoking any metamethods:
print(rawget(tab, "a")) --> 42
print(rawget(tab, "b")) --> nil (__index is not invoked and nil is returned)