Delegation vs Inheritance
Choose composition.
Delegation vs Inheritance is a free Kotlin Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Kotlin Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Two Ways to Reuse
Inheritance and delegation both reuse behavior. Inheritance says a class is a kind of another. Delegation says a class has a collaborator it forwards to.
The classic advice: prefer composition over inheritance.
Inheritance Example
With inheritance, a subclass extends a base and overrides members. The base must be open in Kotlin.
open class Animal {
open fun sound() = "..."
}
class Dog : Animal() {
override fun sound() = "woof"
}
fun main() {
println(Dog().sound())
}Delegation Example
The same reuse with delegation: the behavior lives in a member that the class forwards to.
interface Sound { fun sound(): String }
class Bark : Sound { override fun sound() = "woof" }
class Dog(s: Sound) : Sound by s
fun main() {
println(Dog(Bark()).sound())
}The Fragile Base Class
Inheritance tightly couples a subclass to its parent's internals. A change in the base can silently break subclasses — the fragile base class problem. Delegation only depends on the interface.
Encapsulation
A subclass can access protected members and override hooks, peeking into the parent. A delegating class only sees the public interface, preserving encapsulation.
interface Counter { fun increment(): Int }
class SimpleCounter : Counter {
private var n = 0
override fun increment(): Int { n++; return n }
}
class GuardedCounter(private val c: Counter) : Counter by c {
override fun increment(): Int {
println("incrementing")
return c.increment()
}
}
fun main() {
val g = GuardedCounter(SimpleCounter())
println(g.increment())
println(g.increment())
}Flexibility at Runtime
Inheritance fixes the parent at compile time. Delegation lets you inject different implementations at runtime, supporting easy testing with fakes.
interface Clock { fun now(): Int }
class RealClock : Clock { override fun now() = 100 }
class FakeClock : Clock { override fun now() = 0 }
class Timer(c: Clock) : Clock by c
fun main() {
println(Timer(RealClock()).now())
println(Timer(FakeClock()).now()) // easy to test
}Multiple Behaviors
A class can extend only one parent, but it can delegate to many interfaces. Delegation scales to combining several capabilities.
interface Walk { fun walk() = "walking" }
interface Swim { fun swim() = "swimming" }
class Walker : Walk
class Swimmer : Swim
class Amphibian : Walk by Walker(), Swim by Swimmer()
fun main() {
val a = Amphibian()
println(a.walk() + ", " + a.swim())
}When Inheritance Fits
Inheritance is still right for a true is-a relationship with a stable, designed-for-extension hierarchy: sealed type families, framework base classes, and polymorphism over a shared abstract type.
sealed class Shape
class Circle(val r: Double) : Shape()
class Square(val s: Double) : Shape()
fun area(shape: Shape) = when (shape) {
is Circle -> 3.14 * shape.r * shape.r
is Square -> shape.s * shape.s
}
fun main() {
println(area(Circle(2.0)))
}When Delegation Fits
Choose delegation when you want to:
- Reuse behavior across unrelated types
- Combine multiple capabilities
- Keep coupling low and testing easy
- Wrap and decorate existing implementations
Side-by-Side
Inheritance: one parent, tight coupling, fixed at compile time, is-a. Delegation: many collaborators, loose coupling, swappable at runtime, has-a. Kotlin makes delegation cheap with by, tilting the balance toward composition.
A Practical Default
A good rule: start with delegation. Switch to inheritance only when a genuine, stable is-a hierarchy emerges. This keeps designs flexible and resilient to change.
Quick Check
Test your understanding of delegation vs inheritance.
Recap
You compared the two approaches:
- Inheritance: is-a, single parent, tight coupling
- Delegation: has-a, many collaborators, loose coupling
- Prefer composition; use inheritance for stable is-a hierarchies
Next: practical decorator patterns with delegation.
Frequently asked questions
Is the “Delegation vs Inheritance” lesson free?
Yes — the full text of “Delegation vs Inheritance” is free to read here on the web, and the Kotlin Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Kotlin Academy course, upgrade to CoddyKit PRO.
What will I learn in “Delegation vs Inheritance”?
Choose composition. You practise Kotlin Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Kotlin Academy?
No prior experience is required. Kotlin Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Delegation vs Inheritance” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Kotlin Academy lesson?
Yes. Every Kotlin Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- The by Keyword
- Delegating to Members
- Delegation vs Inheritance
- Practical Patterns