Duck Typing
Part of the Object Oriented Programming section of Coddy's Lua journey — lesson 39 of 70.
Lua doesn't care about an object's class—it only cares about what the object can do. This concept is called duck typing: "If it walks like a duck and quacks like a duck, it's a duck." In programming terms, if an object has the method you're calling, it works.
This means completely unrelated classes can be used together, as long as they share the same method name:
local Circle = {}
Circle.__index = Circle
function Circle:new()
local obj = {}
setmetatable(obj, Circle)
return obj
end
function Circle:draw()
print("Drawing a circle")
end
local Square = {}
Square.__index = Square
function Square:new()
local obj = {}
setmetatable(obj, Square)
return obj
end
function Square:draw()
print("Drawing a square")
endThese classes have no inheritance relationship—they're completely independent. Yet we can treat them identically:
local shapes = {Circle:new(), Square:new(), Circle:new()}
for _, shape in ipairs(shapes) do
shape:draw()
end
-- Output:
-- Drawing a circle
-- Drawing a square
-- Drawing a circleThe loop doesn't check what type each object is. It simply calls :draw() and trusts that the method exists. This flexibility is powerful—you can add new shape types without modifying the loop code at all. As long as the new class implements :draw(), it just works.
Challenge
EasyLet's demonstrate the power of duck typing by building a notification system with completely unrelated classes that can all be processed the same way!
You'll create three independent notification classes—none of them inherit from each other—but they all implement a :send() method. This means you can loop through a mixed collection and call :send() on each one without caring about its actual type.
You'll organize your code across four files:
Email.lua: A class with a:new(recipient)constructor that stores who the email goes to. Its:send()method should printEmailing {recipient}.SMS.lua: A completely separate class with a:new(phoneNumber)constructor. Its:send()method should printTexting {phoneNumber}.PushNotification.lua: Another independent class with a:new(deviceId)constructor. Its:send()method should printPushing to {deviceId}.main.lua: Bring everything together! Read three inputs, create one instance of each notification type, put them all in a single table, then loop through and call:send()on each one.
You will receive three inputs:
- An email recipient (e.g.,
alice@example.com) - A phone number (e.g.,
555-1234) - A device ID (e.g.,
device_99)
In your main file, create all three notification objects, store them in a table, and iterate through the table calling :send() on each. The order should be: Email first, then SMS, then PushNotification.
For example, if the inputs are bob@mail.com, 555-9876, and phone_42, the output should be:
Emailing bob@mail.com
Texting 555-9876
Pushing to phone_42Notice how your loop doesn't need to check what type each object is—it just trusts that every object in the collection has a :send() method. That's duck typing in action! If it can :send(), it's a notification.
Try it yourself
-- main.lua
-- Bring all notification types together and demonstrate duck typing
-- Import the notification classes
local Email = require('Email')
local SMS = require('SMS')
local PushNotification = require('PushNotification')
-- Read inputs
local recipient = io.read()
local phoneNumber = io.read()
local deviceId = io.read()
-- TODO: Create one instance of each notification type
-- TODO: Put all notifications in a single table (Email first, then SMS, then PushNotification)
-- TODO: Loop through the table and call :send() on each notification
-- This demonstrates duck typing - we don't check the type, we just call :send()
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 ClassCircle ClassPerimeter MethodShape CollectionTotal AreaFilter Shapes2Class 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