Bird
Raised Fist0
Kotlinprogramming~15 mins

Class delegation with by keyword in Kotlin - Deep Dive

Choose your learning style10 modes available
Overview - Class delegation with by keyword
What is it?
Class delegation with the 'by' keyword in Kotlin lets one class hand over some of its work to another class automatically. Instead of writing all the code again, you can reuse an existing class's behavior by delegating. This makes your code shorter and easier to manage. It works by telling Kotlin to forward calls to another object behind the scenes.
Why it matters
Without class delegation, programmers often repeat code or write complex inheritance chains that are hard to maintain. Delegation solves this by allowing flexible reuse of behavior without inheritance. This leads to cleaner, simpler code and fewer bugs. Imagine having to rewrite the same instructions for every new gadget; delegation is like sharing a trusted helper instead.
Where it fits
Before learning class delegation, you should understand basic classes, interfaces, and how inheritance works in Kotlin. After mastering delegation, you can explore advanced design patterns like the decorator pattern or composition over inheritance. Delegation is a key step toward writing modular and maintainable Kotlin code.
Mental Model
Core Idea
Class delegation with 'by' means one class automatically passes certain tasks to another class, so it doesn't have to do everything itself.
Think of it like...
It's like a manager who delegates specific tasks to a trusted team member instead of doing all the work personally, trusting the team member to handle those tasks well.
╔════════════════════════════╗
║ Class A (Delegator)        ║
║ ┌──────────────────────┐  ║
║ │ delegates to Class B  │◄─┤
║ └──────────────────────┘  ║
╚════════════════════════════╝
          │
          ▼
╔════════════════════════════╗
║ Class B (Delegatee)        ║
║ Implements interface       ║
╚════════════════════════════╝
Build-Up - 7 Steps
1
FoundationUnderstanding interfaces in Kotlin
🤔
Concept: Interfaces define a contract of functions that classes can implement.
In Kotlin, an interface is like a list of tasks that a class promises to do. For example: interface Printer { fun printMessage(message: String) } Any class that implements Printer must provide the printMessage function.
Result
You can create classes that share the same set of functions without sharing code yet.
Knowing interfaces is essential because delegation works by forwarding interface functions to another class.
2
FoundationBasic class implementation of interfaces
🤔
Concept: Classes can implement interfaces by writing the required functions themselves.
For example: class ConsolePrinter : Printer { override fun printMessage(message: String) { println(message) } } This class fulfills the Printer contract by printing messages to the console.
Result
You get a working class that does the tasks defined by the interface.
Implementing interfaces manually is the starting point before delegation automates this process.
3
IntermediateManual delegation without 'by' keyword
🤔Before reading on: do you think manually forwarding all interface calls is easy or error-prone? Commit to your answer.
Concept: You can delegate by writing functions that call another object's functions explicitly.
Example: class DelegatingPrinter(private val printer: Printer) : Printer { override fun printMessage(message: String) { printer.printMessage(message) // forwarding call } } Here, DelegatingPrinter passes the printMessage call to the printer object it holds.
Result
DelegatingPrinter behaves like printer but requires writing all forwarding functions manually.
Understanding manual delegation shows why Kotlin's 'by' keyword is a powerful shortcut.
4
IntermediateUsing 'by' keyword for automatic delegation
🤔Before reading on: do you think Kotlin's 'by' keyword can replace all manual forwarding code? Commit to yes or no.
Concept: The 'by' keyword lets Kotlin generate all forwarding functions automatically for you.
Example: class DelegatingPrinterBy(private val printer: Printer) : Printer by printer This means DelegatingPrinterBy implements Printer by forwarding all calls to printer without writing any function.
Result
DelegatingPrinterBy behaves exactly like printer but with much less code.
Knowing 'by' keyword saves time and reduces errors by automating delegation.
5
IntermediateCombining delegation with additional behavior
🤔Before reading on: can you add extra code in a class that delegates some functions automatically? Commit to yes or no.
Concept: You can delegate some functions and still add or override others in the same class.
Example: class LoggingPrinter(private val printer: Printer) : Printer by printer { override fun printMessage(message: String) { println("Logging: $message") printer.printMessage(message) } } Here, printMessage is overridden to add logging before delegating.
Result
You get a class that reuses existing behavior but customizes some parts.
Delegation with 'by' is flexible, letting you mix reuse and customization easily.
6
AdvancedDelegation with multiple interfaces
🤔Before reading on: do you think Kotlin supports delegating multiple interfaces in one class? Commit to yes or no.
Concept: A class can delegate different interfaces to different objects using 'by' multiple times.
Example: interface Printer { fun printMessage(message: String) } interface Scanner { fun scanDocument(): String } class MultiFunctionDevice( private val printer: Printer, private val scanner: Scanner ) : Printer by printer, Scanner by scanner This class delegates Printer and Scanner separately.
Result
MultiFunctionDevice behaves like both printer and scanner by forwarding calls appropriately.
Delegating multiple interfaces allows composing complex behaviors from simple parts.
7
ExpertPerformance and memory implications of delegation
🤔Before reading on: do you think delegation adds significant runtime overhead compared to inheritance? Commit to your answer.
Concept: Delegation uses an extra object reference and function call forwarding, which has minor runtime cost compared to inheritance but offers more flexibility.
At runtime, calls to delegated functions are forwarded to the delegate object. This means an additional pointer dereference and call. However, Kotlin compiles delegation efficiently, and the cost is usually negligible. Delegation also avoids tight coupling of inheritance, making code easier to change.
Result
Delegation trades a tiny performance cost for better design and maintainability.
Understanding the tradeoff helps decide when delegation is worth using in performance-critical code.
Under the Hood
When you write 'class A : Interface by b', Kotlin generates code that forwards every call of Interface's functions from A to the object b. This means A holds a reference to b and calls b's methods internally. The compiler creates these forwarding methods automatically, so you don't write them yourself.
Why designed this way?
Delegation was designed to simplify code reuse without the problems of inheritance like tight coupling and fragile base classes. Kotlin chose the 'by' keyword to make delegation explicit and concise, improving readability and reducing boilerplate. Alternatives like manual forwarding were error-prone and verbose.
╔════════════════════════════╗
║ Class A (Delegator)        ║
║ ┌──────────────────────┐  ║
║ │ holds reference to b  │  ║
║ │ forwards calls to b   │  ║
║ └──────────────────────┘  ║
╚════════════════════════════╝
          │ calls
          ▼
╔════════════════════════════╗
║ Object b (Delegatee)       ║
║ Implements Interface       ║
╚════════════════════════════╝
Myth Busters - 4 Common Misconceptions
Quick: Does delegation with 'by' create a subclass of the delegate object? Commit to yes or no.
Common Belief:Delegation with 'by' means the delegating class inherits from the delegate class.
Tap to reveal reality
Reality:Delegation does not create inheritance; it forwards calls to a separate object held inside the delegator.
Why it matters:Confusing delegation with inheritance can lead to wrong assumptions about object behavior and lifecycle, causing bugs.
Quick: Can you override delegated functions without writing them yourself? Commit to yes or no.
Common Belief:Once you delegate with 'by', you cannot customize or override any delegated function.
Tap to reveal reality
Reality:You can override any delegated function by writing your own implementation in the delegating class.
Why it matters:Believing you cannot override limits flexibility and prevents adding important custom behavior.
Quick: Does delegation always improve performance compared to inheritance? Commit to yes or no.
Common Belief:Delegation is always faster than inheritance because it avoids virtual calls.
Tap to reveal reality
Reality:Delegation adds a small overhead due to forwarding calls through an extra object reference, while inheritance uses direct calls.
Why it matters:Ignoring performance tradeoffs can cause unexpected slowdowns in critical code.
Quick: Is delegation only useful for small interfaces? Commit to yes or no.
Common Belief:Delegation is only practical for interfaces with few functions.
Tap to reveal reality
Reality:Delegation works well for interfaces of any size and is especially helpful for large interfaces to avoid repetitive code.
Why it matters:Underestimating delegation's power limits its use in complex systems where it shines.
Expert Zone
1
Delegation can be combined with Kotlin's property delegation to create powerful reusable components.
2
Delegation allows mixing behaviors from multiple sources without the diamond problem of multiple inheritance.
3
The delegate object can be changed at runtime if held as a mutable property, enabling dynamic behavior changes.
When NOT to use
Avoid delegation when performance is critical and the small overhead of forwarding calls matters; inheritance or inline functions might be better. Also, if the delegate object is large or complex to manage, delegation can add unnecessary complexity.
Production Patterns
In production, delegation is used to implement decorators, proxies, and adapters cleanly. It's common in Android development for UI components to delegate event handling. Also, libraries use delegation to provide default implementations while allowing customization.
Connections
Composition over Inheritance
Class delegation is a practical way to implement composition instead of inheritance.
Understanding delegation clarifies how composition can replace inheritance to build flexible, maintainable code.
Proxy Design Pattern
Delegation with 'by' can implement proxies by forwarding calls and adding extra behavior.
Knowing delegation helps implement proxies without boilerplate, improving code clarity.
Organizational Management
Delegation in programming mirrors how managers assign tasks to team members in organizations.
Seeing delegation as task assignment helps grasp its purpose: sharing work to improve efficiency and focus.
Common Pitfalls
#1Forgetting to override delegated functions when customization is needed.
Wrong approach:class MyPrinter(private val printer: Printer) : Printer by printer // No override, but needs logging
Correct approach:class MyPrinter(private val printer: Printer) : Printer by printer { override fun printMessage(message: String) { println("Log: $message") printer.printMessage(message) } }
Root cause:Assuming delegation means no need to write any function, missing the chance to customize behavior.
#2Delegating to a null or uninitialized object.
Wrong approach:class MyPrinter(private val printer: Printer?) : Printer by printer!!
Correct approach:class MyPrinter(private val printer: Printer) : Printer by printer
Root cause:Not ensuring the delegate object is properly initialized leads to runtime crashes.
#3Trying to delegate to a class that does not implement the interface.
Wrong approach:class MyPrinter(private val obj: Any) : Printer by obj
Correct approach:class MyPrinter(private val printer: Printer) : Printer by printer
Root cause:Delegation requires the delegate to implement the interface; otherwise, compilation fails.
Key Takeaways
Class delegation with 'by' in Kotlin lets one class automatically forward interface calls to another object, reducing boilerplate.
Delegation improves code reuse and flexibility compared to inheritance by composing behavior from multiple sources.
You can override delegated functions to add custom behavior while still reusing existing implementations.
Delegation has minor runtime overhead but offers better design and maintainability in most cases.
Understanding delegation unlocks advanced design patterns and cleaner Kotlin code architecture.

Practice

(1/5)
1. What does the by keyword do in Kotlin class delegation?
easy
A. It overrides all methods of a class.
B. It creates a new instance of a class.
C. It marks a class as abstract.
D. It lets a class reuse another class's implementation automatically.

Solution

  1. Step 1: Understand the role of by keyword

    The by keyword in Kotlin is used for class delegation, which means one class can reuse the implementation of another class without rewriting the code.
  2. Step 2: Identify what by does

    It automatically forwards calls to the delegated class, so the delegating class behaves like the delegated one.
  3. Final Answer:

    It lets a class reuse another class's implementation automatically. -> Option D
  4. Quick Check:

    Delegation = reuse implementation [OK]
Hint: Remember: by means reuse another class's code [OK]
Common Mistakes:
  • Thinking by creates a new instance
  • Confusing by with inheritance
  • Assuming by overrides methods automatically
2. Which of the following is the correct syntax to delegate interface Printer to class ConsolePrinter in Kotlin?
easy
A. class MyPrinter : Printer by ConsolePrinter()
B. class MyPrinter implements Printer by ConsolePrinter()
C. class MyPrinter extends Printer by ConsolePrinter()
D. class MyPrinter delegate Printer to ConsolePrinter()

Solution

  1. Step 1: Recall Kotlin delegation syntax

    In Kotlin, delegation uses the syntax: class ClassName : InterfaceName by instance.
  2. Step 2: Match the correct syntax

    class MyPrinter : Printer by ConsolePrinter() matches this pattern exactly: class MyPrinter : Printer by ConsolePrinter().
  3. Final Answer:

    class MyPrinter : Printer by ConsolePrinter() -> Option A
  4. Quick Check:

    Syntax = class X : Interface by instance [OK]
Hint: Use colon and 'by' for delegation syntax [OK]
Common Mistakes:
  • Using 'implements' or 'extends' keywords (Java style)
  • Writing 'delegate' keyword which doesn't exist
  • Missing colon before interface name
3. What is the output of this Kotlin code?
interface Logger {
    fun log(msg: String)
}

class ConsoleLogger : Logger {
    override fun log(msg: String) {
        println("Console: $msg")
    }
}

class AppLogger(logger: Logger) : Logger by logger

fun main() {
    val logger = AppLogger(ConsoleLogger())
    logger.log("Hello")
}
medium
A. No output
B. Console: Hello
C. Error: log() not implemented
D. Hello

Solution

  1. Step 1: Understand delegation in AppLogger

    The class AppLogger delegates Logger interface to the passed logger instance, which is ConsoleLogger.
  2. Step 2: Trace the log call

    Calling logger.log("Hello") calls ConsoleLogger.log, which prints "Console: Hello".
  3. Final Answer:

    Console: Hello -> Option B
  4. Quick Check:

    Delegation calls delegated method output [OK]
Hint: Delegated methods run from the delegated instance [OK]
Common Mistakes:
  • Expecting just 'Hello' without prefix
  • Thinking delegation requires manual override
  • Assuming no output because of delegation
4. Identify the error in this Kotlin code using class delegation:
interface Speaker {
    fun speak()
}

class Human : Speaker {
    override fun speak() {
        println("Hello")
    }
}

class Robot(speaker: Speaker) : Speaker {
    speak()
}
medium
A. Missing delegation with 'by' keyword in Robot class.
B. Robot class should not implement Speaker interface.
C. Human class must not override speak().
D. speak() method call is correct as is.

Solution

  1. Step 1: Check Robot class declaration

    Robot implements Speaker but does not delegate or implement speak() method itself.
  2. Step 2: Identify missing delegation

    Robot should delegate Speaker to the passed speaker instance using by speaker to avoid error.
  3. Final Answer:

    Missing delegation with 'by' keyword in Robot class. -> Option A
  4. Quick Check:

    Delegation needs 'by' keyword [OK]
Hint: Delegate with 'by' or implement all methods [OK]
Common Mistakes:
  • Calling method directly without delegation
  • Forgetting to use 'by' keyword
  • Assuming interface methods auto-implement
5. You want to create a class SmartSpeaker that delegates Speaker interface to a Human instance but adds a new method playMusic(). Which is the best way to do this?
hard
A. class SmartSpeaker : Human() { fun playMusic() = println("Playing music") }
B. class SmartSpeaker : Speaker { val human = Human() override fun speak() = human.speak() fun playMusic() = println("Playing music") }
C. class SmartSpeaker(human: Human) : Speaker by human { fun playMusic() { println("Playing music") } }
D. class SmartSpeaker(human: Human) : Speaker { fun playMusic() = println("Playing music") }

Solution

  1. Step 1: Understand delegation with added methods

    Using by human delegates all Speaker methods to Human automatically.
  2. Step 2: Add new method in delegating class

    We can add playMusic() directly in SmartSpeaker without affecting delegation.
  3. Final Answer:

    class SmartSpeaker(human: Human) : Speaker by human { fun playMusic() { println("Playing music") } } -> Option C
  4. Quick Check:

    Delegate with 'by' and add methods [OK]
Hint: Delegate with 'by' and add new methods in class body [OK]
Common Mistakes:
  • Not delegating interface methods properly
  • Extending class instead of delegating interface
  • Forgetting to implement interface methods
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard