Computer scienceProgramming languagesKotlinObject-oriented programmingObject-oriented programming features

Class delegation

7 minutes read

Class delegation is a mechanism in Kotlin that allows you to delegate the implementation of an interface or functionality of one class to another class. This provides flexibility and prevents problems associated with multiple inheritance.

Delegation: example

Below, you can see an example of class delegation. To implement class delegation, the by keyword is used (we will talk about the keyword in a short while).

interface Drawable {
    fun draw()
}

class Circle : Drawable {
    override fun draw() {
        println("Drawing a circle")
    }
}

class DrawingBoard(private val drawable: Drawable) : Drawable by drawable

fun main() {
    val circle = Circle()
    val drawingBoard = DrawingBoard(circle)
    drawingBoard.draw() // "Drawing a circle"
}

Advantages of using class delegation

  • It simplifies code: class delegation avoids code duplication by delegating the implementation of an interface or functionality to another class.
  • It improves composition and modularity: instead of using inheritance, you can combine different objects to create new functionality.
  • It prevents multiple inheritance problems: unlike inheritance, delegation does not lead to problems related to multiple inheritance, such as the "diamond problem".
  • It provides flexibility to change the behavior of objects: you can easily replace a delegate to change the behavior of an object without changing the source code.

Here's an example of changing the behavior of an object using class delegation:

interface Greeting {
    fun greet()
}

class EnglishGreeting : Greeting {
    override fun greet() {
        println("Hello!")
    }
}

class FrenchGreeting : Greeting {
    override fun greet() {
        println("Bonjour!")
    }
}

class Greeter(private val greeting: Greeting) : Greeting by greeting

fun main() {
    val englishGreeting = EnglishGreeting()
    val frenchGreeting = FrenchGreeting()
    
    val greeter1 = Greeter(englishGreeting)
    val greeter2 = Greeter(frenchGreeting)
    
    greeter1.greet() // "Hello!"
    greeter2.greet() // "Bonjour!"
}

In the above example, the Greeter class delegates the implementation of the Greeting interface to different delegates to change the behavior of the object without changing the source code.

Keyword "by"

The keyword by is used in Kotlin to delegate the implementation of an interface or functionality of one class to another class.

Below is an example of using the by keyword:

interface Printable {
    fun print()
}

class Printer : Printable {
    override fun print() {
        println("Printing a document")
    }
}

class OfficePrinter(private val printer: Printer) : Printable by printer

fun main() {
    val printer = Printer()
    val officePrinter = OfficePrinter(printer)
    officePrinter.print() // "Printing a document"
}

In this example, the OfficePrinter class delegates the Printable interface implementation to the Printer class using the by keyword.

Differences between inheritance and delegation

Inheritance:

  • A class inherits the functionality and properties of the parent class.
  • There is a hard link between the classes.
  • There is a possibility of multiple inheritance problems, such as the "diamond problem".

Here's an example of inheritance:

open class Vehicle {
    fun move() {
        println("Vehicle is moving")
    }
}

class Car : Vehicle()

fun main() {
    val car = Car()
    car.move() // "Vehicle is moving"
}

Delegation:

  • The implementation of an interface or functionality is passed to another class.
  • It provides flexibility and modularity and helps avoid multiple inheritance problems.

Here's an example of delegation:

interface Movable {
    fun move()
}

class Vehicle : Movable {
    override fun move() {
        println("Vehicle is moving")
    }
}

class Car(private val movable: Movable) : Movable by movable

fun main() {
    val vehicle = Vehicle()
    val car = Car(vehicle)
    car.move() // "Vehicle is moving"
}

In the above example, instead of relying on inheritance, the Car class delegates the implementation of the Movable interface to the Vehicle class, which provides flexibility and modularity of code.

Implementing interfaces using class delegation

1. Creating an interface

You must first define the interface to be used for delegation. In this example, let's create an interface named Eatable:

interface Eatable {
    fun eat()
}

2. Creating a delegate class

Next, we create a class that will implement the Eatable interface. In this example, let's create an Apple class, which implements the Eatable interface:

class Apple : Eatable {
    override fun eat() {
        println("Eating an apple")
    }
}

3. Creating a class that uses delegation

Now, let's create a class named Person, which will use delegation to implement the Eatable interface:

class Person(private val eatable: Eatable) : Eatable by eatable

Here is an example of using the Person class and Apple's delegate class:

class Banana : Eatable {
    override fun eat() {
        println("Eating a banana")
    }
}

fun main() {
    val banana = Banana()
    val person = Person(banana)
    person.eat() // "Eating a banana"
}

Thus, with the help of Kotlin's class delegation, you can easily and flexibly implement interfaces and change the behavior of objects.

Conclusion

Class delegation plays an important role in Kotlin, as it provides a flexible and efficient way to implement interfaces and functionality. It is one of the key mechanisms of the language, which helps to create clean, modular, and easily supported code. It removes inheritance restrictions and provides developers with more options to adapt and extend their code.

26 learners liked this piece of theory. 3 didn't like it. What about you?
Report a typo