skip to content

Delegation

Kotlin has delegation built into the language, so composing behavior no longer means writing forwarding methods by hand. It is the practical lever behind 'favor composition over inheritance' advice.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

explore

questions

10

What does the `by` keyword do when a class declares `class MyList(impl: List<String>) : List<String> by impl`?

level: juniorimportance: must knowfreq 55%

answer

  1. `by impl` = compiler writes the forwarders
  2. Compile-time, no reflection
  3. Decorator pattern, zero boilerplate
  4. Override any member to customize
  5. Delegate is whatever expression follows `by`

basics

~10 s

It makes the class implement the List interface by automatically forwarding every interface method call to the impl object you pass in, so you don't write those methods by hand.

solid answer

~30 s

`by` is Kotlin's class (interface) delegation. Writing `: List<String> by impl` tells the compiler to generate forwarding implementations for every member of `List<String>`, each delegating to the `impl` instance. So `myList.size` calls `impl.size`, `myList.get(0)` calls `impl.get(0)`, etc. You satisfy the interface contract without manually writing dozens of boilerplate methods. The delegate (`impl`) is a constructor parameter here. You can still override any specific member to customize behavior; the generated forwarders cover the rest. This is implemented at compile time — the compiler emits real override methods, not runtime reflection. It's the Decorator/Wrapper pattern made first-class.

code

kotlin · 12 lines
kotlin
interface Printer { fun print(s: String) }

class ConsolePrinter : Printer {
    override fun print(s: String) = println(s)
}

class PrefixPrinter(impl: Printer) : Printer by impl

fun main() {
    val p = PrefixPrinter(ConsolePrinter())
    p.print("hello") // forwarded to ConsolePrinter.print -> prints hello
}

go deeper

for a junior

Knows by impl auto-implements the interface by forwarding to the delegate and removes boilerplate.

for a middle

Explains it's compile-time generated forwarders (no reflection) and that you can selectively override members.

for a senior

Frames it as first-class Decorator/Wrapper, notes delegate accessibility nuances and where it shines vs inheritance.

for a principal

Discusses API-design implications (forwarding fixed at construction, brittleness on interface evolution) and library patterns built on it.

## What `by` means on a class Kotlin lets a class implement an interface by **delegating** the implementation to another object instead of writing the methods yourself. The syntax is: ```kotlin class MyList(impl: List<String>) : List<String> by impl ``` The part `: List<String> by impl` reads as: "this class is a `List<String>`, and its `List<String>` members are provided by the object `impl`." ## What the compiler generates At **compile time** the Kotlin compiler synthesizes an override for **every member of `List<String>`** (`size`, `get`, `isEmpty`, `iterator`, `contains`, etc.). Each generated override simply calls the same member on `impl`. There is **no reflection** and **no runtime proxy** — these are ordinary forwarding methods. Conceptually it is the same as if you typed: ```kotlin class MyList(private val impl: List<String>) : List<String> { override val size get() = impl.size override fun get(index: Int) = impl.get(index) // ...and so on for every member } ``` but the compiler writes all of it for you. ## Why this is useful - **Eliminates boilerplate** when wrapping/decorating an existing implementation. - Implements the **Decorator pattern** as a first-class language feature. - You only write the members you actually want to change. ## Overriding selectively You can override any member; your override wins over the generated forwarder: ```kotlin class LoggingList(impl: List<String>) : List<String> by impl { override fun get(index: Int): String { println("get($index)") return impl.get(index) // 'impl' must be accessible to do this } } ``` ## Key terms - **Delegate**: the object (`impl`) that actually does the work. - **Interface delegation / class delegation**: this `by` feature (distinct from *property* delegation, which also uses `by` but for `val/var`). - **Forwarding**: each generated method just calls the same method on the delegate.

  • Does `by` use runtime reflection or dynamic proxies?
    No. The compiler statically generates forwarding override methods at compile time; it is plain method calls with zero reflection overhead.
  • Can you override one of the delegated members?
    Yes. Any member you explicitly override replaces the generated forwarder for that member; the rest still forward to the delegate.

Like hiring a stand-in who does every part of the job exactly as the original would, while you step in only for the few tasks you care to do differently.

saying these in an interview costs you the question

  • Claiming `by` works for extending classes (it only works for interfaces)
  • Saying it uses reflection or a runtime proxy
  • Confusing it with property delegation (`val x by lazy {}`)
  • Thinking you must implement every interface method yourself anyway
  • Believing the delegate field is always accessible by default

context

open as a page

In Kotlin, what does it mean to implement an interface 'by' delegating to another object, and why might you prefer that over extending an open base class?

level: juniorimportance: must knowfreq 65%

basics

~20 s

The 'by' keyword lets a class implement an interface by handing every method call to another object you give it. You reuse behavior by wrapping an object instead of subclassing it, so you don't depend on a parent class's internals.

open as a page

When you override a member in a class that uses `by` delegation, do the OTHER auto-forwarded members start calling your override or the delegate's own implementation? Explain the trap.

level: middleimportance: must knowfreq 55%

basics

~20 s

Other forwarded methods still call the delegate's own versions, not your override. The delegate doesn't know about your wrapper, so any internal calls it makes go to itself. Your override only affects calls made directly on the wrapper.

open as a page

When you write `class W(impl: Foo) : Foo by impl`, is the `impl` value stored and accessible inside the class? How do you access the delegate if you need it?

level: middleimportance: should knowfreq 35%

basics

~10 s

The compiler stores the delegate in a hidden field, but a plain constructor parameter is not a property, so you can't reference it later. To use it inside the class, declare it as val.

open as a page

In a class using interface delegation, if you both delegate to an object and explicitly override one method, which implementation runs — and does the delegate still see the override?

level: middleimportance: should knowfreq 45%

basics

~10 s

Your explicit override always runs for that method. But the delegate object never knows about your override — if it calls another of its own methods internally, it calls its own version, not yours.

open as a page

What is the fragile base class problem, and how does Kotlin's class delegation help you avoid it compared to using `open` inheritance?

level: middleimportance: should knowfreq 45%

basics

~20 s

The fragile base class problem is when a subclass quietly breaks because the parent class changed how it works inside, even if the parent's public methods look the same. Delegation avoids it by only using the parent's public interface, not its internals.

open as a page

Can a class delegate multiple interfaces, each to a different object, with `by`? What happens if two delegated interfaces declare a member with the same signature?

level: seniorimportance: should knowfreq 28%

basics

~10 s

Yes, you can delegate several interfaces, each to its own object. If two of them declare the same method, the compiler forces you to override that method yourself to remove the ambiguity.

open as a page

When you write `class W(val d: I) : I by d`, when is the delegate expression evaluated, and what surprising behavior arises if you pass a property versus a fresh expression? Contrast with overriding the property.

level: seniorimportance: should knowfreq 30%

basics

~20 s

The delegate is evaluated once when the object is built, and that exact value is stored and used for all forwarding. If you later change a property, the forwarders keep using the original delegate they captured, which can surprise you.

open as a page

How can you compose behavior from multiple interfaces using `by`, and what are the design and equals/hashCode/identity caveats versus a single inheritance chain?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

A class can delegate several interfaces to several different objects at once, mixing in behaviors. Unlike single inheritance you can combine many sources. But the wrapper is a new object, so identity and equals/hashCode are its own unless you forward them too.

open as a page

What are the design limitations and risks of interface delegation via `by` — especially around interface evolution and which members are NOT forwarded?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

by only forwards members declared by the interface. It can't delegate classes, won't auto-forward new collaborators, and the forwarding is fixed at construction. New interface methods get auto-forwarders too, which can silently expose unintended behavior.

open as a page