ProgrammingKotlin Developer

How does the 'by' operator work when delegating interfaces in Kotlin? What is the difference between interface delegation and property delegation, what are the pros and cons, provide an example of code.

Pass interviews with Hintsage AI assistant

Answer.

Interface delegation using the by operator allows a class to redirect all interface calls to a specific delegate object. This reduces code duplication and implements the composition pattern.

Example:

interface Logger { fun log(message: String) } class ConsoleLogger: Logger { override fun log(message: String) = println("LOG: $message") } class Service(logger: Logger): Logger by logger { fun doWork() { log("Service is working") } } val service = Service(ConsoleLogger()) service.doWork() // LOG: Service is working

Differences from property delegation:

  • Interface delegation applies to classes and all members of the interface.
  • Property delegation (val/var x by ...) applies to a specific property and requires the implementation of the delegate's interface (e.g., ReadWriteProperty).
  • Interface delegation provides compact implementation but exposes the entire API of the delegate interface.

Pros:

  • Significantly simplifies the implementation of the decorator and delegation patterns.
  • Allows changing the behavior of standard interfaces through composition.

Cons:

  • All interface methods are always delegated, it is impossible to "intercept" individual calls without explicit overriding.
  • Ambiguities may arise when inheriting multiple interfaces with the same methods.

Trick question.

How does interface delegation (by) differ from implementing an interface by passing an object through a field?

Answer: Delegation (via by) automatically implements all interface methods through the delegate object. If you simply store the delegate object as a field and call its methods manually, you need to explicitly implement each interface method — which leads to duplication and errors. Furthermore, delegation via by provides more readability and less boilerplate code:

// Without delegation class Service2(private val logger: Logger): Logger { override fun log(message: String) = logger.log(message) }

Examples of real errors due to ignorance of the topic nuances:


Story

In the project, they tried to implement the decorator pattern for the Logger interface manually, forgetting to implement an additional method of the interface that was later added to Logger. The project compiled, but the new functionality did not work, as the implementation was a "dummy". Interface delegation via by would have avoided this error: all new methods are automatically implemented by the delegate.


Story

When delegating an interface via by, the developer overridden one of the interface methods but forgot that the other methods are still routed through the delegate. As a result, part of the functionality worked "non-standardly" — the error was not caught for a long time in the business method logic.


Story

They tried to implement delegation of multiple interfaces with overlapping methods via by, causing a conflict — the compiler began to issue an ambiguity error, and they had to explicitly override the duplicate methods, otherwise, the project would not compile.