🎓 A1Learner
🔍
0 of 28 lessons completed

Kotlin Interface Delegation

Interface delegation lets a class hand an interface to another object with the by keyword. Calls the class does not override forward to that object, and calls it does override stay its own.

A robot that borrows a voice

Robot delegates Speaker to its voice, a Dog. speak forwards and prints woof, while the name override in Robot answers robot.

interface Speaker {
    fun speak(): String
    fun name(): String
}

class Dog : Speaker {
    override fun speak(): String {
        return "woof"
    }

    override fun name(): String {
        return "dog"
    }
}

class Robot(private val voice: Speaker) : Speaker by voice {
    override fun name(): String {
        return "robot"
    }
}

fun main() {
    val robot = Robot(Dog())
    println(robot.speak())
    println(robot.name())
}

Forwarding one method, owning another

Loud delegates Formatter to a PlainFormatter. format forwards unchanged, and the label override reports loud instead of plain.

interface Formatter {
    fun format(text: String): String
    fun label(): String
}

class PlainFormatter : Formatter {
    override fun format(text: String): String {
        return text
    }

    override fun label(): String {
        return "plain"
    }
}

class Loud(private val base: Formatter) : Formatter by base {
    override fun label(): String {
        return "loud"
    }
}

fun main() {
    val loud = Loud(PlainFormatter())
    println(loud.format("hello"))
    println(loud.label())
}

Two delegates side by side

Host delegates Greeter to one object and Farewell to another. Each call travels to its own delegate.

interface Greeter {
    fun greet(): String
}

interface Farewell {
    fun bye(): String
}

class Friendly : Greeter {
    override fun greet(): String {
        return "hello"
    }
}

class Polite : Farewell {
    override fun bye(): String {
        return "goodbye"
    }
}

class Host(greeter: Greeter, farewell: Farewell) : Greeter by greeter, Farewell by farewell

fun main() {
    val host = Host(Friendly(), Polite())
    println(host.greet())
    println(host.bye())
}

Example

interface Speaker {
    fun speak(): String
    fun name(): String
}

class Cat : Speaker {
    override fun speak(): String {
        return "meow"
    }

    override fun name(): String {
        return "cat"
    }
}

class Parrot(private val voice: Speaker) : Speaker by voice {
    override fun name(): String {
        return "parrot"
    }
}

fun main() {
    val parrot = Parrot(Cat())
    println(parrot.speak())
    println(parrot.name())
    val cat = Cat()
    println(cat.speak())
    println(cat.name())
}

Key points

  • The shape is a class header that names the interface, then by, then the object that does the work.
  • Every interface method without an override forwards to the delegate object.
  • An override in the class wins over the forwarded call.
  • The delegate object is often stored as a private constructor property.
  • One class can delegate two interfaces to two different objects side by side.

In this example, Parrot delegates Speaker to a Cat held as its voice. speak forwards to the cat and prints meow. The name override in Parrot prints parrot, while a plain Cat still answers cat for both calls.

Note

Delegation reuses behavior by composition, so the helper class needs no shared parent with its user.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Kotlin Playground

Exercise

In class Robot(voice) : Speaker by voice, what happens to a Speaker method that Robot does not override?