A Gentle Introduction to Delegation in Kotlin
In object-oriented programming, delegation is an object-oriented design pattern that works as an alternative to inheritance. In delegation, an object handles a request by delegating it to another object. This other object, that does the real work, is called the delegate.
As delegation is a design pattern, it means that we can implement it in any object-oriented language. Some object-oriented languages like Kotlin support it natively meaning that there will be no boilerplate code.
An Example
Let’s go through a rudimentary example to get a basic idea about delegation. First, we define the Software
interface.
Then we define two implementations for the interface we just defined.
Now we define a class called WordProcessor
that implements the Software
interface.
WordProcessor
receives an instance of Software
and when its getLicense
is called, it simply delegates/forwards the call to software
.
That’s it! Now you know what delegation is but there is more to it.
Class Delegation
The example you just saw is an example of class delegation. In class delegation class A
delegates some or all of its responsibilities to class B
.
As Kotlin supports class delegation natively, let’s implement the previous example in a more elegant way.
The above WordProcessor
class has the exact same functionality as the one we defined previously but without any boilerplate code. The Kotlin compiler will generate the boilerplate for us!
We used the by
keyword to let the Kotlin compiler do two things:
- Generating all the methods of the
Software
interface forWordProcessor
. - Delegating the calls of the generated methods to the corresponding methods of
software
.
Decompiling the bytecode of WordProcessor
to Java, gives something like the following code.
The Kotlin compiler has done its job!
Now it’s time to talk about the other type of delegation which is called property delegation.
Property Delegation
Before talking about property delegation let’s see why we would need it.
An example
Consider the following class.
Imagine we want to implement the following logic for each property of User
:
- Print “<property name> is read” when reading the value of the property,
- Print “<property name> is written” when assigning a value to the property.
So when running the following code:
We want to get the output below:
firstName is written
secondName is written
firstName is read
secondName is read
One naive approach is to manually add println
statements when reading/writing any property of User
.
The above code works but guess what. It’s not scalable at all and we are repeating the same logic. It would be ideal if we could define the logic once and use it without repeating the same logic over and over again. That’s where property delegates could help.
Defining a Property Delegate
A property delegate is nothing but a class that has very specificgetValue
and setValue
methods.
Let’s define a delegate that implements the same logic that we implemented earlier manually.
Note that for the following code to work we’ll need to add the following dependency to our build.gradle
file (assuming we using Gradle of course!)
implementation("org.jetbrains.kotlin:kotlin-reflect:<insert version here>")
The getValue
method is executed when we read the value of the property. It’s an operator method that must have the exact following parameters:
thisRef
must be of typeAny?
. It’s the instance of the class which contains the delegated property. In case the delegated property is not defined in a class (like being defined in themain
function)thisRef
will be null.property
must be of typeKProperty<*>
or its subtypes. It’s the property itself.
The return type of getValue
must be the same type as the property (or its subtype).
setValue
is the method that gets executed when we assign a value to the property. It’s an operator method that must have the following parameters exactly.
thisRef
has the exact same purpose as described above.property
has the exact same purpose as described above.value
is the value being assigned to the property.
Now we can use the delegate we just defined.
Running the above code generates the following output.
firstName is written
secondName is written
firstName is read
secondName is read
Here, again we’re using the by
keyword for doing the property delegation. The by
keyword tells the Kotlin compiler two things:
- When accessing the value of say
firstName
, instead of calling the default getter offirstName
, callDelegate.getValue
. - When assigning a value to say
firstName
, instead of calling the default setter offirstName
, callDelegate.setValue
.
If we decompile the bytecode of User
to Java, we will get the following code which confirms the fact that when setting/getting the value of firstName
/lastName
, it’s the Delegate
that handles the job.
Note that the delegate we just implemented is a very simple and not-so-useful one. Just use your imagination and think about the things that we can achieve using property delegation.
This is it! Now you have a basic understanding of delegation in Kotlin.
References
https://kotlinlang.org/docs/delegation.html