← Back to Kotlin Course | Chapter 7: Inheritance & Interfaces | Lesson 7 of 7

Delegation with by

The by keyword lets a class say 'for this ability, just forward all the work to this other object I already have', instead of writing the logic itself.
Syntax
kotlin
interface InterfaceName {
    fun method()
}

class ClassName(impl: InterfaceName) : InterfaceName by impl {
    // members forwarded to impl; override any you want to change
}

Delegating an Interface with by

Writing : Interface by someObject after a class header automatically forwards every call to that interface's members to someObject, without writing any forwarding code by hand.

Example: Delegating an Interface with by

markup
// Define an interface `Printer` describing required behavior
interface Printer {
    // Define the function `printMessage` taking `message`
    fun printMessage(message: String)
}

// Define a class `ConsolePrinter`
class ConsolePrinter : Printer {
    override fun printMessage(message: String) {
        // Print "Console: $message" to the console, with a trailing newline
        println("Console: $message")
    }
}

// Define a class `App`
class App(printer: Printer) : Printer by printer

// Entry point: execution of the program starts here
fun main() {
    // Declare a read-only variable `app`, set to `App(ConsolePrinter())`
    val app = App(ConsolePrinter())
    app.printMessage("Hello via delegation")
}

Overriding a Delegated Member

Even when using by to delegate an interface, a class can still override any specific member itself, and that override takes priority over the forwarded implementation.

Example: Overriding a Delegated Member

markup
// Define an interface `Printer` describing required behavior
interface Printer {
    // Define the function `printMessage` taking `message`
    fun printMessage(message: String)
}

// Define a class `ConsolePrinter`
class ConsolePrinter : Printer {
    override fun printMessage(message: String) {
        // Print "Console: $message" to the console, with a trailing newline
        println("Console: $message")
    }
}

// Define a class `LoudApp`
class LoudApp(printer: Printer) : Printer by printer {
    override fun printMessage(message: String) {
        // Print "LOUD: ${message.toUpperCase()}" to the console, with a trailing newline
        println("LOUD: ${message.toUpperCase()}")
    }
}

// Entry point: execution of the program starts here
fun main() {
    // Declare a read-only variable `app`, set to `LoudApp(ConsolePrinter())`
    val app = LoudApp(ConsolePrinter())
    app.printMessage("hello")
}

Why Use Delegation

Delegation avoids the boilerplate of manually forwarding every interface member, while still letting a class compose behavior from another object instead of relying purely on inheritance.

Example: Why Use Delegation

markup
// Define an interface `Repository` describing required behavior
interface Repository {
    // Define the function `getAll` with no parameters and returning a `List<String>`
    fun getAll(): List<String>
}

// Define a class `InMemoryRepository`
class InMemoryRepository(private val items: List<String>) : Repository {
    override fun getAll(): List<String> = items
}

// Define a class `CachedRepository`
class CachedRepository(repo: Repository) : Repository by repo

// Entry point: execution of the program starts here
fun main() {
    // Declare a read-only variable `repo`, set to `CachedRepository(InMemoryRepository(listOf("a", "b", "c")))`
    val repo = CachedRepository(InMemoryRepository(listOf("a", "b", "c")))
    // Print `repo.getAll()` to the console, with a trailing newline
    println(repo.getAll())
}

Property Delegation with by lazy

The same by keyword is used for property delegation, with lazy { ... } being the most common built-in delegate -- it computes and caches a value only the first time the property is accessed.

Note: Notice "Computing value..." prints only once, even though the property is read twice.

Example: Property Delegation with by lazy

markup
val expensiveValue: String by lazy {
    // Print "Computing value..." to the console, with a trailing newline
    println("Computing value...")
    "Computed Result"
}

// Entry point: execution of the program starts here
fun main() {
    // Print "Before access" to the console, with a trailing newline
    println("Before access")
    // Print `expensiveValue` to the console, with a trailing newline
    println(expensiveValue)
    // Print `expensiveValue` to the console, with a trailing newline
    println(expensiveValue)
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Reimplementing every method of an interface by hand to forward calls to an internal object, instead of using Kotlin's built-in by delegation.
  2. Forgetting that with by, overriding one specific method in the delegating class still lets the rest be forwarded automatically to the delegate.
  3. Confusing interface delegation (by after an interface in a class header) with property delegation (by on a property, like lazy), which are related but different features.
Chapter Summary
  • class MyClass(val impl: Interface) : Interface by impl forwards every interface member to impl automatically.
  • This avoids writing repetitive forwarding methods by hand, a pattern known as the delegation pattern.
  • Specific methods can still be overridden in the delegating class, taking priority over the forwarded implementation.
  • Property delegation, like val x by lazy { ... }, is a related but separate use of the by keyword.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.