ProgrammingMiddle/Senior Kotlin Developer

How does behavior delegation through interfaces work in Kotlin (delegation by interface)? When should it be used, how does it differ from property delegation and classical inheritance?

Pass interviews with Hintsage AI assistant

Answer.

In Kotlin, behavior delegation is implemented via the language mechanism of the keyword by, directly in the class signature. This allows method calls of an interface (or multiple interfaces) to be automatically passed to another object with the implementation, reducing boilerplate and facilitating composition.

Background

The emergence of interface delegation is an attempt to eliminate the limitations and drawbacks of multiple inheritance. This is the idea of "composition over inheritance" — delegating behavior without resorting to class hierarchy. It is borrowed from languages where composition is more popular (e.g., Go, Scala).

Problem

In Java and other languages, it is often necessary to create an interface and manually implement each method, passing the logic to another field (Object Adapter pattern), which quickly becomes outdated as the number of methods grows.

Solution

Kotlin allows declaratively delegating an interface using by:

interface Logger { fun log(msg: String) } class ConsoleLogger: Logger { override fun log(msg: String) = println(msg) } class Service(logger: Logger): Logger by logger { fun doWork() { log("Work started") // ... } } val service = Service(ConsoleLogger()) service.doWork()
  • All Logger methods are implemented through the provided logger object, and the Service class does not explicitly need to override or proxy methods.

Key features:

  • Allows separating the implementation of the interface from its usage, reducing code duplication
  • Delegation is more flexible than inheritance and works with multiple behaviors
  • Supports best SOLID practices

Trick Questions.

What happens if a Service class adds its own method of the interface with the same signature?

The class's own implementation "overrides" the delegated one — that is, the method defined explicitly in the class takes precedence:

class Service(logger: Logger): Logger by logger { override fun log(msg: String) = println("PREFIX: $msg") }

Can one class delegate multiple interfaces to different objects?

Yes, a class can implement and delegate multiple interfaces to different objects, but each interface is delegated to one object:

class Service( logger: Logger, tracker: Tracker ): Logger by logger, Tracker by tracker

How does interface delegation differ from property delegation via by?

  • Interface delegation passes the entire implementation of the interface functions to another object.
  • Property delegation (property delegation) delegates the work with get/set to a delegated object of a specific type (ReadOnlyProperty, ReadWriteProperty).

Common Mistakes and Anti-Patterns

  • Delegating excessively large interfaces (violating ISP)
  • Simultaneously applying explicit implementation and delegation (unexpected behavior)
  • Attempting to combine interface delegation and inheritance from a parent class, ignoring the order of method resolution

Real-Life Examples

Negative Case

A class manually implements an interface, each method calls a delegate, and when new methods are added, the proxying is forgotten, leading to errors.

Pros:

  • Logic is explicitly controlled

Cons:

  • High risk of errors, boilerplate
  • Poor scalability as the interface grows

Positive Case

Language delegation is used; only non-standard methods are implemented inside the class, and new functionality is added without major changes.

Pros:

  • Minimal code
  • Clear control of extension points

Cons:

  • Requires attention during combined implementation (can easily overshadow a delegated method with one’s own implementation)