Instance Variables
Part of the Object Oriented Programming section of Coddy's Lua journey — lesson 12 of 70.
Welcome to a new chapter focused on how objects manage their internal data. You've learned to create classes and instances—now it's time to explore how each instance stores and manages its own unique state.
Instance variables are fields that belong exclusively to a single object. When you assign a value to self.something inside a method or constructor, that data lives only in that particular instance. Other objects of the same class have their own separate copies.
This becomes especially important when storing complex data like tables. Consider an inventory system where each character carries different items:
local Inventory = {}
Inventory.__index = Inventory
function Inventory:new()
local instance = {}
setmetatable(instance, self)
instance.items = {} -- Each instance gets its own empty table
return instance
end
function Inventory:addItem(item)
table.insert(self.items, item)
endThe key line is instance.items = {}. By creating a new empty table inside the constructor, every inventory object receives its own distinct list. If you instead defined items in the class table itself, all instances would accidentally share the same table—adding a sword to one character would give it to everyone!
local bag1 = Inventory:new()
local bag2 = Inventory:new()
bag1:addItem("Sword")
bag2:addItem("Potion")
print(#bag1.items) -- Output: 1
print(#bag2.items) -- Output: 1Each inventory maintains its own collection. This pattern applies whenever an instance needs to track data that changes independently—lists, counters, or any mutable state unique to that object.
Challenge
EasyLet's build a ShoppingCart system where each cart maintains its own separate list of products. This will demonstrate how instance variables keep data isolated between objects—adding items to one cart won't affect another!
You'll organize your code across two files:
ShoppingCart.lua: Define yourShoppingCartclass where each instance has its ownproductstable. Include:- A
:new()constructor that initializes an empty products list for each cart - An
:addProduct(productName)method that adds a product to this cart's list - A
:listProducts()method that prints each product on a separate line
- A
main.lua: Require your ShoppingCart module and create two separate carts. Add different products to each cart to prove they maintain independent inventories.
You will receive four inputs:
- First product to add to cart A
- Second product to add to cart A
- First product to add to cart B
- Second product to add to cart B
In your main file:
- Create two shopping carts:
cartAandcartB - Add the first two products to
cartA - Add the last two products to
cartB - Print
Cart A:then list cart A's products - Print
Cart B:then list cart B's products
For example, if the inputs are Apple, Bread, Milk, and Eggs, the output should be:
Cart A:
Apple
Bread
Cart B:
Milk
EggsRemember: the key is initializing products as a new empty table inside the constructor. This ensures each cart instance gets its own separate list rather than sharing one table across all carts.
Cheat sheet
Instance variables are fields that belong exclusively to a single object. When you assign a value to self.something inside a method or constructor, that data lives only in that particular instance.
To create instance variables, initialize them inside the constructor using a new table for each instance:
local Inventory = {}
Inventory.__index = Inventory
function Inventory:new()
local instance = {}
setmetatable(instance, self)
instance.items = {} -- Each instance gets its own empty table
return instance
end
function Inventory:addItem(item)
table.insert(self.items, item)
endThe key is instance.items = {}—creating a new empty table inside the constructor ensures every object receives its own distinct data. If you defined items in the class table itself, all instances would share the same table.
local bag1 = Inventory:new()
local bag2 = Inventory:new()
bag1:addItem("Sword")
bag2:addItem("Potion")
print(#bag1.items) -- Output: 1
print(#bag2.items) -- Output: 1Each instance maintains its own collection independently. This pattern applies to any mutable state unique to an object—lists, counters, or other data that changes per instance.
Try it yourself
-- Require the ShoppingCart module
local ShoppingCart = require('ShoppingCart')
-- Read inputs
local product1 = io.read()
local product2 = io.read()
local product3 = io.read()
local product4 = io.read()
-- TODO: Create two shopping carts: cartA and cartB
-- TODO: Add product1 and product2 to cartA
-- TODO: Add product3 and product4 to cartB
-- TODO: Print "Cart A:" then list cartA's products
-- TODO: Print "Cart B:" then list cartB's products
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1The 'Self' Concept
Tables with FunctionsExplicit 'self'The Colon SyntaxDot vs ColonRecap - Moving Point4Project: Digital Bank
Project SetupDeposit Method7Polymorphism & Overriding
Overriding MethodsCalling Parent MethodsDuck TypingCommon InterfaceChecking TypeRecap - Employee Roles10Project: Shape Manager
Project SetupRectangle Class2Class Prototype Pattern
The Prototype ConceptLinking with __indexThe :new() ConstructorInitializing AttributesIndependent InstancesRecap - Car Factory5Operator Overloading in OOP
Adding ObjectsSubtracting ObjectsConcatenating ObjectsComparing Objects (<, >)Recap - Wallet Math8Encapsulation
Naming ConventionsClosures for PrivacyAccess via ClosuresRead-Only TablesValidation LogicRecap - Secure Vault11Design Patterns (Lite)
Factory FunctionsSingleton TableIterator PatternObserver (Listener)Recap - Logger Factory3Object State and Behavior
Instance VariablesGetter MethodsSetter MethodsCalculated PropertiesFormatting StringsEquality ChecksRecap - Student Grade6Inheritance Basics
The Inheritance SetupInheriting MethodsExtending the ConstructorAdding Child MethodsShared vs UniqueRecap - Shape Hierarchy