Polymorphism means calling the exact same method name on different objects and getting behavior specific to each one — the caller doesn't need to know or care which concrete type it's actually dealing with.
The previous lesson's Dog.describe() overriding Animal.describe() was already an example of this in miniature. This lesson is about the actual payoff: writing code that works against a whole family of related objects at once.
Learning Objectives
After completing this lesson, you'll be able to:
- Write several components that share a parent and override the same method differently.
- Loop over a mixed collection of those objects and call the shared method on each.
- Explain why this matters more as a hierarchy grows.
One Method Name, Different Behavior
component {
function area() {
return 0;
}
}component extends="Shape" {
property name="radius" type="numeric";
function init(required numeric radius) {
variables.radius = arguments.radius;
return this;
}
function area() {
return 3.14159 * variables.radius * variables.radius;
}
}component extends="Shape" {
property name="width" type="numeric";
property name="height" type="numeric";
function init(required numeric width, required numeric height) {
variables.width = arguments.width;
variables.height = arguments.height;
return this;
}
function area() {
return variables.width * variables.height;
}
}<cfcomponent>
<cffunction name="area" returnType="numeric">
<cfreturn 0>
</cffunction>
</cfcomponent><cfcomponent extends="Shape">
<cfproperty name="radius" type="numeric">
<cffunction name="init">
<cfargument name="radius" type="numeric" required="true">
<cfset variables.radius = arguments.radius>
<cfreturn this>
</cffunction>
<cffunction name="area" returnType="numeric">
<cfreturn 3.14159 * variables.radius * variables.radius>
</cffunction>
</cfcomponent><cfcomponent extends="Shape">
<cfproperty name="width" type="numeric">
<cfproperty name="height" type="numeric">
<cffunction name="init">
<cfargument name="width" type="numeric" required="true">
<cfargument name="height" type="numeric" required="true">
<cfset variables.width = arguments.width>
<cfset variables.height = arguments.height>
<cfreturn this>
</cffunction>
<cffunction name="area" returnType="numeric">
<cfreturn variables.width * variables.height>
</cffunction>
</cfcomponent>The Payoff: One Loop, Many Shapes
The loop calls shape.area() without ever checking "is this a Circle or a Rectangle?" — each object knows how to compute its own area. Adding a Triangle.cfc later wouldn't require changing this loop at all.
Common Beginner Mistakes
Checking the object's type before calling a method it already overrides correctly
if (isInstanceOf(shape, "Circle")) { ... } else if (...) { ... } defeats the point — that branching logic is exactly what polymorphism replaces. Just call shape.area() and let each object handle its own case.
Forgetting a shared method exists on the parent, so it can't be called polymorphically
For a loop like the one above to work on any Shape, area() has to be declared on Shape itself (even as a stub returning 0), so every subclass is guaranteed to have it.
Best Practices
- Design the parent's method signatures first, then build subclasses that fulfill them — polymorphism only works cleanly when every subclass honors the same method name and shape.
- Avoid type-checking branches (isInstanceOf, an if/else chain) where a polymorphic method call would do the same job more simply.
Interview Questions
What does polymorphism let you do that a plain if/else type check doesn't?
Call the same method name on objects of different concrete types and get each one's own correct behavior automatically, without the calling code needing to know which specific type it's dealing with.
Why does a shared method usually need to exist on the parent component for polymorphism to work in a loop?
So every subclass is guaranteed to implement it — calling code that loops over a mixed collection can then call that method safely on any item, without checking its concrete type first.
Summary
In this lesson, you built a small hierarchy of shapes and looped over a mix of them, calling the same method on each and letting each object supply its own behavior — the real payoff of inheritance.
What's Next?
The next lesson covers abstraction — hiding a complex operation behind a simple, named method, plus ColdFusion's abstract keyword for components that are never meant to be instantiated directly.