Kotlin: Delegation Pattern and the by Keyword

Photo by Celine Ylmz on Unsplash
Photo by Celine Ylmz on Unsplash
Kotlin’s by keyword allows us to easily implement the Delegation Pattern. When we use Kotlin’s by keywords, we don’t need to implement these delegation code. This makes the code quite compact.

Kotlin’s by keyword allows us to easily implement the Delegation Pattern. First of all, let us take a look at a general way to implement the Delegation Pattern. We can see Window.area() delegates the call to bound.area(), doesn’t do anything else. When Shape has many methods, Window will have to implement many such codes.

interface Shape {
    fun area(): Int
}

class Rectangle(private val width: Int, private val height: Int) : Shape {
    override fun area(): Int = width * height
}

class Window(private val bound: Shape) : Shape {
    override fun area(): Int = bound.area()
}

fun main() {
    val shape = Window(Rectangle(6, 5))
    println("area is ${shape.area()}")
}

// Output:
// area is 30

When we use Kotlin’s by keywords, we don’t need to implement these delegation code. This makes the code quite compact as below.

interface Shape {
    fun area(): Int
}

class Rectangle(private val width: Int, private val height: Int) : Shape {
    override fun area(): Int = width * height
}

class Window(private val bound: Shape) : Shape by bound

fun main() {
    val shape = Window(Rectangle(6, 5))
    println("area is ${shape.area()}")
}

// Output:
// area is 30

The by keyword also allows us to use our own implementation.

interface Shape {
    fun area(): Int
    fun printArea()
}

class Rectangle(private val width: Int, private val height: Int) : Shape {
    override fun area(): Int = width * height

    override fun printArea() {
        println("Rectangle area is ${area()}")
    }
}

class Window(private val bound: Shape) : Shape by bound {
    override fun printArea() {
        println("Window area is ${area()}")
    }
}

fun main() {
    Window(Rectangle(6, 5)).printArea()
}

// Output:
// Window area is 30

However, unlike inheritance, delegated objects cannot call methods of our own implementation. In the following code, when we call Window.printArea(), it will delegate to Rectangle.printArea(), and then call Rectangle.area() instead of Window.area().

interface Shape {
    fun area(): Int
    fun printArea()
}

class Rectangle(private val width: Int, private val height: Int) : Shape {
    override fun area(): Int = width * height

    override fun printArea() {
        println("Rectangle area is ${area()}")
    }
}

class Window(private val bound: Shape) : Shape by bound {
    override fun area(): Int = bound.area() * 10
}

fun main() {
    Window(Rectangle(6, 5)).printArea()
}

// Output:
// Rectangle area is 30
Leave a Reply

Your email address will not be published. Required fields are marked *

You May Also Like