Delegating to Members
Compose behavior.
Delegating to Members is a free Kotlin Academy lesson on CoddyKit — lesson 2 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.
Composing Behavior
Real objects often need to combine several capabilities. Instead of inheriting from many sources, you can delegate each capability to a dedicated member object.
Two Roles, One Class
Suppose a class should both read and write. Define two interfaces and delegate each to its own implementation.
interface Reader { fun read(): String }
interface Writer { fun write(s: String): String }
class FileReader : Reader { override fun read() = "contents" }
class FileWriter : Writer { override fun write(s: String) = "wrote " + s }
class FileIO(r: Reader, w: Writer) : Reader by r, Writer by w
fun main() {
val io = FileIO(FileReader(), FileWriter())
println(io.read())
println(io.write("data"))
}Multiple Delegates
A class can delegate several interfaces at once, each to a different member. The class composes their behaviors without inheritance.
interface Logger { fun log(m: String): String }
interface Validator { fun valid(m: String): Boolean }
class ConsoleLogger : Logger { override fun log(m: String) = "LOG: " + m }
class LengthValidator : Validator { override fun valid(m: String) = m.isNotEmpty() }
class Service(l: Logger, v: Validator) : Logger by l, Validator by v
fun main() {
val s = Service(ConsoleLogger(), LengthValidator())
println(s.valid("hi"))
println(s.log("started"))
}Delegate Stored as a Member
When you need to reference the delegate inside overrides, keep it as a constructor property. The same instance backs both the forwarding and your custom code.
interface Cache { fun get(k: String): String }
class MapCache : Cache {
override fun get(k: String) = "value-of-" + k
}
class TracingCache(private val inner: Cache) : Cache by inner {
override fun get(k: String): String {
println("lookup " + k)
return inner.get(k)
}
}
fun main() {
println(TracingCache(MapCache()).get("id"))
}Mixing Delegation and Own State
A delegating class can add its own properties and methods alongside the forwarded ones, enriching the composed object.
interface Engine { fun start(): String }
class V8 : Engine { override fun start() = "vroom" }
class Car(e: Engine) : Engine by e {
var doors = 4
fun honk() = "beep"
}
fun main() {
val c = Car(V8())
println(c.start())
println(c.honk())
println(c.doors)
}Swapping Implementations
Because the delegate is just a member, you can construct the same wrapper with different backing implementations, choosing behavior at runtime.
interface Notifier { fun send(msg: String): String }
class EmailNotifier : Notifier { override fun send(msg: String) = "email: " + msg }
class SmsNotifier : Notifier { override fun send(msg: String) = "sms: " + msg }
class Alerts(n: Notifier) : Notifier by n
fun main() {
println(Alerts(EmailNotifier()).send("hi"))
println(Alerts(SmsNotifier()).send("hi"))
}Delegating to a Property
The expression after by can be any expression evaluated once, including a constructor call. It does not have to be a parameter.
interface Greeter { fun greet() = "hi" }
class DefaultGreeter : Greeter
class Page : Greeter by DefaultGreeter()
fun main() {
println(Page().greet())
}Avoiding Diamond Trouble
Multiple inheritance of behavior is impossible with classes, but delegating several interfaces to separate members sidesteps the classic diamond problem cleanly.
interface A { fun a() = "A" }
interface B { fun b() = "B" }
class AImpl : A
class BImpl : B
class Combined : A by AImpl(), B by BImpl()
fun main() {
val c = Combined()
println(c.a() + c.b())
}Designing for Composition
To compose well:
- Define small, focused interfaces
- Provide reusable implementations
- Delegate each role in the composing class
This keeps classes flexible and testable.
Member Delegation Recap
Delegating to member objects lets a class assemble behavior from parts. You can combine many interfaces, override selectively, swap implementations, and add your own state — all without inheritance.
Next: A Direct Comparison
You have seen how flexible delegation is. The next lesson contrasts delegation with inheritance directly, so you know when to pick each.
Quick Check
Test your understanding of delegating to members.
Recap
You learned member delegation:
- Delegate several interfaces to separate objects
- Override selectively and add own state
- Swap implementations at construction
- Avoid multiple-inheritance pitfalls
Next: delegation vs inheritance.
Frequently asked questions
Is the “Delegating to Members” lesson free?
Yes — the full text of “Delegating to Members” 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 “Delegating to Members”?
Compose behavior. 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 2 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Delegating to Members” 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