Delegation vs Inheritance in Kotlin

Daniel Rendox - Jul 5 '23 - - Dev Community

Hello, everyone! Welcome to the article about class delegation in Kotlin. This tutorial will help you understand how to use class delegation in Kotlin and when to use it instead of inheritance.

The examples in this article are not mine and were taken from this article with some changes applied. It provides a good explanation but, to my mind, lacks some things crucial for understanding. So I brought them up here.

Why use delegation instead of inheritance

Let’s say we want a list’s item to be recoverable after deletion. To achieve this, we need to implement MutableList, override the remove function, and create our own recover function. But to do this, we need to override other methods that the MutableInterface tells us. So we essentially need to create our own implementation of MutableList. That’s not a cool thing to do as all the code is already written for us.

To avoid creating our own list, we can extend ArrayList instead. But this approach is not good for abstraction. We don’t want this list to be an ArrayList. We don’t need ArrayList's specific methods like trimToSize, ensureCapacity, grow, etc. We just want to have an additional function.

So there is a better way to achieve this. And this is delegation. We want to have only MutableList's functions overridden by ArrayList and our own implementation of remove.

How the delegation pattern works

We need to create a new variable of the ArrayList type and, at the same time, implement MutableList. And then in all the overridden functions’ bodies, respective ArrayList's methods should be called. For example,



class ListWithTrash <T>(override val size: Int) : MutableList<T> {

    private val innerList: MutableList<T> = ArrayList()

    // the variable for storing the last deleted item
    private var deletedItem : T? = null

    override fun remove(element: T): Boolean {
        deletedItem = element
        // calling ArrayList's method, not our implementation
        return innerList.remove(element)
    }

    // here is our own function for recovering elements
    fun recover(): T? {
        return deletedItem
    }

    override fun add(element: T): Boolean {
        // calling ArrayList's method, not our implementation
        innerList.add(element)
    }
}


Enter fullscreen mode Exit fullscreen mode

In this example, we override only add and remove MutableList's methods. But we have a bunch of other methods we have to override for this to compile. That’s a lot of boilerplate code!

How to delegate using by keyword

Luckily, we don’t need to do this. That’s how it works internally. Kotlin provides first-party support for delegation, and all this is done automatically under the hood. A developer's task is to use the simple by keyword:



class ListWithTrash <T>(
    private val innerList: MutableList<T> = ArrayList()
) : MutableList<T> by innerList {
    private var deletedItem : T? = null
    override fun remove(element: T): Boolean {
        deletedItem = element
        return innerList.remove(element)
    }
    fun recover(): T? {
        return deletedItem
    }
}


Enter fullscreen mode Exit fullscreen mode

In this example, the innerList variable was also moved into the constructor to let the users of ListWithTrash provide their own ArrayList. This allows them to specify the desired list’s properties, for example, its size.

This is called delegation because along with our own implementations, the rest of the functions are delegated to the other classes. In our case, we delegate MutableList implementation to ArrayList. The necessary functions are “provided by” ArrayList.

Again, ArrayList's specific functions won’t work:



val listWithTrash = ListWithTrash<String>()
//    listWithTrash.trimToSize() // → exception! (ListWithTrash doesn't have this function)


Enter fullscreen mode Exit fullscreen mode

If you need them, extend from ArrayList directly.

Multiple delegation is… allowed

Also remember that you're not restricted to just one delegate. Kotlin's way of implementing delegation is similar to traits implementation in languages like Groovy. You can compose different functionality via delegates. Kotlin's way can also be considered more powerful because you can "plug in" different implementations too.

interface Marks {

Delegation vs Inheritance

Inheritance allows to get a ready implementation by extending from the respective class. Whereas class delegation allows making a custom inheritant without lowering the class in the hierarchy. This is done by “copying” the ready implementation from the delegate. Here is the scheme for our situation:

Scheme with squares and arrows that demonsrates inheritance on the left side and delegation on the right side. Inheritance: ListWithTrash extends from ArrayLIst, which in turn implements MutableList. Delegation: both ArrayList and ListWithTrash implement MutableList, and there is a connection between ArrayList and ListWithTrash.

This scheme is not entirely correct because ArrayList doesn’t extend from MutableList in Kotlin. But the idea stays the same — our class (ListWithTrash) and the delegate (ArrayList) are on the same level in the hierarchy. Check out the cover of this article.

When to use which? Use inheritance only when you are sure that your class and the class you inherit from share all the common traits. If you think your class doesn’t need some variables or methods from its possible parent, and “is a” relationship doesn’t apply, it’s better to use delegation.

That’s it! If this article was helpful, please, like it and follow for more. If you have any questions or corrections, feel free to write in the comments.

. . . . . . . . . . . . . . . . . . . .