Inheritance
An apple tree is a crop too: it grows day by day, and you can describe it like any other crop. But it also has something more: it gives fruit. You could copy the whole Crop class and add to it, but then every change to crops would have to be made twice. Instead, a class can inherit from another class: it gets all of the other class's methods, adds its own, and replaces the ones that should work differently:
local Crop = {}
Crop.__index = Crop
function Crop.new(name, days)
local crop = {name = name, days = days, age = 0}
setmetatable(crop, Crop)
return crop
end
function Crop:grow()
self.age = self.age + 1
end
function Crop:describe()
return self.name .. ", day " .. self.age .. " of " .. self.days
end
local Tree = {}
Tree.__index = Tree
setmetatable(Tree, {__index = Crop})
function Tree.new(name, days, fruit)
local tree = Crop.new(name, days)
setmetatable(tree, Tree)
tree.fruit = fruit
return tree
end
function Tree:describe()
return Crop.describe(self) .. ", gives " .. self.fruit
end
local wheat = Crop.new("Wheat", 2)
local apple = Tree.new("Apple tree", 30, "apples")
wheat:grow()
apple:grow()
print(wheat:describe())
print(apple:describe())This prints:
Wheat, day 1 of 2
Apple tree, day 1 of 30, gives applesWhat's new:
local Tree = {}andTree.__index = TreemakeTreea class, just likeCrop.setmetatable(Tree, {__index = Crop})gives the classTreea metatable: whatTreedoesn't have, Lua looks for inCrop. This line is the inheritance.Treeis a subclass ofCrop, andCropis its parent class.Tree.newdoesn't repeat whatCrop.newdoes. It callsCrop.newto make a crop with a name, days and age, then turns it into a tree withsetmetatable(tree, Tree)(a table has only one metatable, so this replacesCrop), and adds the tree's own key,fruit.
When you call apple:grow(), Lua looks for grow step by step: in apple itself (no), in Tree through the apple's metatable (no), and in Crop through the metatable of Tree (yes). It's a chain: apple → Tree → Crop.
Overriding a method. Tree has its own describe. Lua stops looking at the first describe it finds, and for a tree, that's the one in Tree. So it replaces (overrides) the one in Crop. Wheat is a plain crop, so it still uses the describe from Crop. The tree's version doesn't have to repeat the parent's work: Crop.describe(self) calls the parent's method on the same tree. Notice the dot and self passed by hand. self:describe() would call the tree's own describe again, which would call itself again and again, until the program ends with stack overflow.
Typical mistakes:
- Forgetting
setmetatable(Tree, {__index = Crop}): trees don't find the methods of crops, andapple:grow()ends withattempt to call a nil value (method 'grow'). - Forgetting
setmetatable(tree, Tree)inTree.new: the new tree stays an ordinary crop.apple:describe()then uses the version fromCropand says nothing about fruit, and the tree's own methods aren't found. - Expecting metamethods to be inherited. Lua looks for
__tostring,__lt,__addand the others right in the object's metatable, not through__index. IfCrophas__tostring, trees still print astable: 0x…until you addTree.__tostring = Crop.__tostring.
Instructions
Below Animal:speak, start a class Cow that inherits from Animal: create the table, set its __index, and give it a metatable whose __index is Animal.
Add a constructor Cow.new(name). It creates the animal with Animal.new and the sound "moo", turns it into a cow with setmetatable, adds a key milk with the value 0, and returns the cow. Add a method Cow:giveMilk(liters) that adds liters to milk, too. At the end of the program, create Bella with Cow.new("Bella"), let her give 8 liters of milk and then 6, and print what she says. The program prints:
Rex says woof
Bella says mooOverride speak for cows: Cow:speak() returns what Animal.speak returns, followed by how much milk the cow has given. Rex is a plain animal, so he still just says woof. The program prints:
Rex says woof
Bella says moo and has given 14 liters of milkStart programming for free
8/9