A primer on delegation
If we check the keyword reference page of Kotlin
by is listed there
and it says that it is capable of two different things:
- delegates the implementation of an interface to another object
- delegates the implementation of accessors for a property to another object
I think this is the source of most confusion so after reading this article you’ll be able to see that these two things are essentially the same in Kotlin.
Before we do that we should clarify what delegation is in the first place. By perusing the related Wikipedia page we can find the following definition:
In software engineering, the delegation pattern is an object-oriented design pattern that allows object composition to achieve the same code reuse as inheritance.
I think this is a pretty accurate description and it also comes with a Kotlin code example!
As we have seen above Kotlin supports delegation of an interface and delegation of accessors for a property. Let’s take a look at how they work in detail.
Delegation of an interface
For this exercise we’ll use
DefaultPositionable which is just an implementation of
The above functionality can be used without the
by keyword like this:
This involves a lot of boilerplate code as seen in the above example. The purpose of the
by keyword is to do away
with this kind of drudgery, therefore it allows object composition to achieve the same code reuse as inheritance:
So in a nutshell: with
by we get Item 16
from Effective Java for free. Pretty nifty, huh?
Taking a look under the hood
Now that we know how it works on the outside let’s check out the internals. This is really important as we’ll see later.
This is the Java code which will be generated from the
Rect implementation above:
You can do this yourself in IDEA by using
Tools > Kotlin > Show Kotlin Bytecodeand then clicking
So what Kotlin does is that it takes our delegate,
DefaultPositionable, puts it in the resulting class as a
$$delegate_0 and adds implementations for all the methods of
Positionable and in them it calls our delegate.
If by eyeballing the above code you had guessed that we can have multiple delegates in a class you’d be right!
Let’s do just that by extracting
height into its own delegate:
Sizable we can have a class which has the same functionality as
Rect but with only delegates:
The resulting Java code is this:
This also means that with delegation we can unit test our building blocks in isolation (
DefaultSizablehere) which is a significant improvement over inheritance in itself.
So it turns out that with the
by keyword we have a really powerful alternative to inheritance and the only limitation
is that we have to use
interfaces on the right hand side of
by: Kotlin can only delegate to interfaces.
Remember that multiple inheritance comes in 3 “flavors”: inheritance of state, implementation and type. Java originally only supported multiple inheritance of type (with interfaces), and with the advent of Java 8 it added support for multiple inheritance of implementation (with default methods). By using the
bykeyword we can have multiple inheritance of state as well!
Variations of interface delegation
There are multiple ways of using interface delegation and not all of them work in a way as you would expect.
Rect in fact could have been defined with all of the examples below to similar effect but slight differences
RectWithConstructor lets us pass custom implementations of
Positionable to our Rect. With
we can have access to our delegate within our Rect and with
RectWithDefaultValue we can achieve both
while being able to just pass a
Position to the constructor if we are fine with the default value:
RectWithFunction is a bit of an outlier and it is there to demonstrate that we can also have functions producing
delegates for us. This will be useful later when we’ll try to understand property delegation.
RectWithDefaultValueachieves multiple inheritance of state as well.
The pitfall of interface delegation
You might have wondered about what happens when we can not only pick an implementation for our delegate but change it at runtime:
This looks good, but if we take a peek at the generated Java code we’ll see a discrepancy:
Yep, if we make our delegate a
var and change it at runtime it won’t change the actual delegate. It will happily
set the value of the
positionable property and leave
$$delegate_0 unchanged. This can lead to pretty nasty
problems if you forget this so my advice is not to do this.
If you try to hack around this by renaming
\it won’t work, you’ll get a compiler error detailing a JVM name clash.
You might have seen code like this before:
and wondered about why we have a keyword for seemingly unrelated things. To fully understand how this works we need to take a look at the difference between properties and fields.
Properties and Fields
In Java we have fields and they look like this:
There are no methods, no strings attached, just our solitary field. If we write the same class in Kotlin while it might look similar:
the Java code generated from it will be a bit different:
So here is the difference: fields are just solitary slots which store data, properties on the other hand
have getters and if we declared them as a
var they will also have setters. This difference is very important
and we’ll see how this makes it possible to have not only interface delegates but property delegates as well!
There are cases when the Kotlin compiler won’t even declare a backing field for a property. For example with this Kotlin code:
the generated Java code will only contain a method, since the value of
foo can’t be changed and it is pre-determined:
There are more to properties but I won’t explain them here, but the Kotlin docs is rather verbose about this topic. You can check it out here.
Peeking at an actual property delegate
With this knowledge we have everything to understand how property delegation works. If we take a look at the docs for this topic (which I recommend reading thoroughly) we can see this:
There are certain common kinds of properties, that, though we can implement them manually every time we need them, would be very nice to implement once and for all, and put into a library. Examples include:
- lazy properties: the value gets computed only upon first access;
- observable properties: listeners get notified about changes to this property;
- storing properties in a map, instead of a separate field for each property.
To cover these (and other) cases, Kotlin supports delegated properties:
Let’s revisit the example above which has a
lazy property delegate:
If we take a look at what the
lazy function does:
we can see that it returns a
SynchronizedLazyImpl which is a class that implements
Lazy also comes with an extension function:
which is there to implement the
You can read more about operator overloading here.
The main point here is that delegating to a property uses the exact same mechanisms behind the scenes as interface
delegation. The differences which cause the confusion are the
setValue operators which we need to implement
within our property delegates but not because of how delegation works, but because how overriding property access operators
lazy() to delegate to an instance of
Lazy is the same on the code level as calling
fetchPositionable() in the example
above to delegate to an instance of
How to write our own delegate
Kotlin gives us two interfaces to use for this purpose:
This further reinforces the similarity between the two kinds of delegation. Let’s take a look at how we can implement
a property which is not initialized during object construction but at a later time but still won’t let us read
null values from it:
Note that this class is in the Kotlin stdlib, but it is not well known that it is there.
We can also add a factory method to produce it:
and we can use it like this:
Taking a look at the Java code
If we decompile the Java byte code from
SomeClass above we’ll see this:
Looks strangely familiar, no? Kotlin generates a field for our delegate (
someValue$delegate) and uses it
when either the getter or the setter of our property is called.
By having properties instead of fields property delegation can work out of the box because properties come with getters and setters so Kotlin can provide custom implementations for them when we want to user property delegates.
With interface delegation we can have all three kinds of multiple inheritance: state, implementation, and type. It also lets us use composition without the boilerplate.
And also we now understand perfectly what the documentation says about delegation: With interface delegation we delegate the implementation of an interface to another object and with property delegation we delegate the implementation of accessors for a property to another object.
Now, with all this new knowledge under our belt let’s go forth, and Kode on!