Classes
In the previous exercise, crops needed two tables: cropMethods with the methods, and a metatable {__index = cropMethods}, a new one for every crop. Lua programmers usually let one table do both jobs: it holds the methods, it's the metatable of every crop, and its __index points to itself. Such a table is called a class:
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:isReady()
return self.age >= self.days
end
local wheat = Crop.new("Wheat", 2)
local carrot = Crop.new("Carrot", 3)
wheat:grow()
wheat:grow()
carrot:grow()
print(wheat.name, wheat:isReady())
print(carrot.name, carrot:isReady())This prints:
Wheat true
Carrot falseLine by line:
local Crop = {}creates the class, an ordinary table. Class names start with a capital letter:Cropis the class,wheatandcarrotare crops.Crop.__index = Cropis the trick.Cropwill be the metatable of every crop, and its__indexisCropitself. So when a crop doesn't have a key, Lua looks for it inCrop, where the methods are.Crop.newis the constructor. It creates a table with the crop's own data, setsCropas its metatable, and returns it.newis the usual name for a constructor in Lua. Call it with a dot,Crop.new("Wheat", 2): you call it on the class, before any crop exists, so there's noself.function Crop:grow()defines a method on the class with a colon, and the crops call it with a colon:wheat:grow().
An object made by a class is called an instance of that class: wheat and carrot are instances of Crop. Each holds only its own data; the methods are in Crop, once. Unlike many other languages, Lua has no special keyword for classes. A class is this pattern of a table, __index and a constructor, and you'll find it in almost every bigger Lua program.
setmetatable returns the table it gets, so you'll often see a shorter constructor: local crop = setmetatable({name = name, days = days, age = 0}, Crop). It does the same thing.
The classic mistake: forgetting Crop.__index = Crop. Without that line, everything looks fine until you call a method:
local 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
local wheat = Crop.new("Wheat", 2)
wheat:grow()The program ends with an error:
main.lua:14: attempt to call a nil value (method 'grow')Crop is the wheat's metatable, but without __index, Lua doesn't know that it should look for missing keys there. So wheat.grow is nil, and calling nil is an error. (method 'grow') means that a method wasn't found: check __index, and check the method's name for a typo.
The mistakes from the previous exercises can happen here too. Without return crop, Crop.new returns nil (attempt to index a nil value (local 'wheat')), and wheat.grow() with a dot leaves self empty (attempt to index a nil value (local 'self')).
Instructions
Run the program: it ends with attempt to call a nil value (method 'lay'). Fix the class, so that the program prints:
Goldie: 2 eggsAdd a method Hen:report() that prints the hen's line the way the last line of the program does now. Then replace that last line with goldie:report(). The program prints the same.
Create two more hens with Hen.new: Pearl, who lays 3 eggs, and Rosie, who lays 4. Print their reports below Goldie's, and then the number of eggs of all three hens together:
Goldie: 2 eggs
Pearl: 3 eggs
Rosie: 4 eggs
Total: 9 eggsStart programming for free
5/9