Kotlin provides two native functionalities to implement the delegate pattern. The first one is the interface delegation ( e.g. Strategy pattern). The other one is the property delegation which focuses on class members / properties ( e.g. lazy loading, observable, …). Together they provide a rich and concise set of functionalities. After this tutorial, you will understand in which situation to use this pattern. The examples will show you advantages but also known problems.
It is known that in object-oriented languages inheritance is one of the key features. It means that you can put some functionality and abstraction in a base class. Other classes can inherit from the base class and get therefore the functionality inherited. This is called an “Is-a” relationship. As a textbook example, we can imagine a Shape class (base) and rectangles, circles, etc as derived classes.
Composition over Inheritance
In the past, this way of modeling was abused which caused bad design. E.g. long inheritance chains were used to add incremental functionality to objects. As an over-exaggerated example consider the following class setup. We want to create “animals” and implement how they move and eat. A naive way of doing that would be to implement it as shown in the left picture. It is obvious that this won’t scale.
The right picture on the other hand favors composition over inheritance. The Animal
class implements the interfaces IMoving
and IEating
. However it has no actual code of the implementation, it delegates the calls to the concrete implementations of Walking
and MeatEating
. As we can see this design is more flexible as new implementations of Moving
and Eating
can be created without affecting the Animal
class.
Delegation over inheritance is therefore a way to achieve the Open-Closed principle of the SOLID design patterns.
Interface Delegation
How do I create a delegate in Kotlin?
Kotlin provides native capabilities to implement the delegation pattern without the need to write any boilerplate code. This is only working on interfaces (in other words abstract classes). The interface implementation of IWalking
is done as shown in the following code.
// Standard Delegation interface IMoving { fun move() } class Walking : IMoving { override fun move() { println("Walking") } } class Animal(private val movable: IMoving) : IMoving { override fun move() { movable.move() } } fun main() { var movable = Walking() var animal = Animal(movable) animal.move() }
// Build-In Delegation interface IMoving { fun move() } class Walking : IMoving { override fun move() { println("Walking") } } class Animal(movable : IMoving) : IMoving by movable { } fun main() { var walking = Walking() var animal = Animal(walking) animal.move() }
As we can see the left code needs to implement the interface methods. In the right version, the compiler is doing that for you. The effect is even bigger when the interface has multiple functions. The by
-clause in the supertype list for the derived class indicates that movable
will be stored internally in objects of IMoving
and the compiler will generate all the methods of the inteface
that forward to IMoving
.
Override Interface members
In case you need to override a specific member function of an interface, you can do that just by writing the function in the derived class and adding the keyword override. The compiler will use the new specification of the overridden method. In the following example, we have created a new version of the move
method.
// Override Function of By-Delegate class Animal(movable : IMoving) : IMoving by movable { override fun move() { println("Something else") } } fun main() { var walking = Walking() var animal = Animal(walking) animal.move() }
Multiple Interfaces / Inheritances
To complete the above example we must implement all interfaces and delegate all function calls to the member variables. We can do that by applying the same as we did in the previous section to all inherited interfaces.
// Several inteface delegation interface IMoving { fun move() } class Walking : IMoving { override fun move() { println("Walking") } } interface IEating { fun eat() } class MeatEater : IEating { override fun eat() { println("Eat meat") } } class Animal(movable : IMoving, eating : IEating) : IMoving by movable, IEating by eating { } fun main() { var walking = Walking() var eating = MeatEater() var animal = Animal(walking, eating) animal.move() animal.eat() }
Same function signatures
A special case occurs if you need to implement several interfaces which have the same methods declared. In this case, the delegation is ambiguous and the compiler will give an error such as: “Class ‘xxx’ must override public open fun ‘yyy’ because it inherits many implementations of it“. You need to explicitly implement (or override) this function and delegate it manually.
Replace delegate at runtime
Often it is necessary to change a delegate at runtime. This is often used in the Strategy or State pattern. (Currently) Kotlin does not provide the ability to change a delegate once it was injected natively with the “by” keyword.
The following code is misleading as it prints the following:
Walking
Walking
Running@xxx
// Change Delegate at Runtime interface IMoving { fun move() } class Walking : IMoving { override fun move() { println("Walking") } } class Running : IMoving { override fun move() { println("Running") } } class Animal(var movable : IMoving) : IMoving by movable { } fun main() { var walking = Walking() var animal = Animal(walking) animal.move() var running = Running() animal.movable = running animal.move() println(animal.movable) }
In order to change a delegate at runtime, you have to use the standard implementation. In other words, you have to implement all interface methods yourself and delegate the function calls to the derived class manually. It is likely that this will change in the future as there is already a feature request.
Property Delegation
What is a delegated property?
Kotlin provides some nice features to implement delegated properties. It adds some common functionality to class properties. You can create some properties (e.g. Lazy) in a library and wrap your class members with this functionality.
To understand how that works you need to understand that each member variable provides a getValue and setValue behind the scenes. The property delegate does not need to implement an interface but it needs to provide these functions for every type. As you can see property delegates are generic objects / functions.
In the following example, we provide a delegate that prints messages every time it is accessed.
Read only delegate
Any property delegate must at least implement the following function for the value type which is delegated. The type T
depends on the value type, which is wrapped.
// Read required delegate function operator fun getValue(example: Any, property: KProperty<*>): T { return // value of T }
As an example, we will use a slightly different code from the Kotlin reference page. Our delegate can be used only for a string type and returns only empty strings. This code is quite useless but it shows the usage of a read-only property.
// Example Read only delegate class Example { val p: String by Delegate() } class Delegate { operator fun getValue(example: Any, property: KProperty<*>): String { return "" } }
Read / Write Delegate
In order to use the above delegate for a read and write implementation, we must as well implement the following function. Note that we have a string value type. Therefore we wrote s: String
. You need to adapt this to the type you want to use.
// Write required delegate function operator fun setValue(example: Any, property: KProperty<*>, s: String) { }
Our above example can now implement the required function. We will introduce a class variable cached
which will hold the actual value.
// Example Read / Write delegate class Example { var p: String by Delegate() } class Delegate { var cached = "" operator fun getValue(example: Any, property: KProperty<*>): String { return cached } operator fun setValue(example: Any, property: KProperty<*>, s: String) { cached = s } }
Implement interface as extension
Another approach is to implement the getValue()
and setValue()
functions as an extension function to the class Delegate
. This is useful if the class is not in your source control.
// Delegated property as extension function class Example { val string : String by Delegate() } class Delegate { } operator fun Delegate.getValue(example: Any, property: KProperty<*>): String { return "" }
Generic delegate definition
The obvious drawback is that this code is only going to work with objects of type string. If we want to use it for other types we must use a generic class. We will show you how the above example can be changed to be compliant will all types.
// Generic class for Delegate class Example { var string: String by Delegate("hello") var int : Int by Delegate(3) } class Delegate<T>(var cached: T) { operator fun getValue(example: Any, property: KProperty<*>): T { return cached } operator fun setValue(example: Any, property: KProperty<*>, s: T) { cached = s } }
Anonymous object delegate
Kotlin also provides a way to create anonymous object delegates without creating new classes. This works because of the interface ReadOnlyProperty
and ReadWritePropery
of the standard library. These interfaces will provide the getValue()
for ReadOnlyProperty
. ReadWritePropery
extends ReadOnlyProperty
by adding the setValue()
function.
In the above example, the delegated property is created as an anonymous object from a function call.
// Anonymous objects delegates class Example { val string: String by delegate() } fun delegate(): ReadWriteProperty<Any?, String> = object : ReadWriteProperty<Any?, String> { var curValue = "" override fun getValue(thisRef: Any?, property: KProperty<*>): String = curValue override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { curValue = value } }
Delegate to another property
Delegating from one property to another can be a quite useful trick to provide backward compatibility. Imagine in version 1.0 of a class you had some public member functions. But during their lifetime the name changed. To not break clients we can mark the old member functions deprecated (via annotation) and forward their calls to the new implementation.
// Delegate to other member class Example { var newName: String = "" @Deprecated("Use 'newName' instead", ReplaceWith("newName")) var oldName: String by this::newName } fun main() { val example = Example() example.oldName = "hello" println(example.newName) }
We have experienced that in some cases this code does not compile, with error code:
Type 'KMutableProperty0' has no method 'getValue(MyClass, KProperty<*>)' and thus it cannot serve as a delegate
.
Knowing that there is an open bug ticket (LINK), we hope it will be fixed soon!
Delegation Examples and Use Cases
Kotlin provides some pre-implemented solutions for common problems. Have a look on the KotlinLang page to see a full list. For the current implementations, we will show you a couple of examples.
By observable
Basic functionality can be obtained by using the observable delegate. It provides a callback when the value changed. The good thing about it is that you have access to the new and previous values. To use the observable delegate you must specify the initial value. Check out our Observer Pattern Example for more information.
// By Observable class Book { var content : String by observable("") { property, oldValue, newValue -> println("Book changed") } } fun main() { val book = Book() book.content = "New content" }
By vetoable
The vetoable is kind of similar to the observer delegate. The callback function however can be used to undo the change. Note that the call back must return a boolean. In the case of success, it is true and in the case of failure (veto), it is false. In our book example, we check if the new content of the book is null or empty. In this case, it is considered wrong and we want to veto the change.
// By Vetoable class Book { var content : String by vetoable("") { property, oldValue, newValue -> !newValue.isNullOrEmpty() } } fun main() { val book = Book() book.content = "New content" println(book.content) book.content = "" println(book.content) }
By Lazy
Kotlin provides an existing implementation for lazy computation of variables. A common use case is for member variables which take a lot of time to initialize but will not be used directly at the beginning. For example consider a Book class that gets a string, representing the whole text. The book class holds another class that is doing expensive postprocessing on the book. The developers have decided to make this instance lazy since it is not called always.
The lazy property is attached to the analyzer class. When running the code it will first print out that the book is created and then that the Analyser member variable is instantiated. If you want to learn more about how Kotlin treats constructors and init blocks, check out our Constructor tutorial.
// By Lazy class Book(private val rawText: String) { private val analyser: Analyser by lazy { Analyser() } fun analyse() { analyser.doSomething() } } class Analyser { init { println("Init Analyser class") } fun doSomething() { println("DoSomething") } } fun main() { val rawText = "MyBook" val book = Book(rawText) println("Book is created") book.analyse() }
By Not Null
Returns a property delegate for a read/write property with a non-null
value that is initialized not during object construction time but at a later time. As it is created at a later time point it is related to lazy initializing. A big draw back however is that there is no native way to know if the value is initialized. It requires some boilerplate code to make it save. Our book example would look like the following code. Be aware that the “by lazy” is much better.
// By Delegates.NotNull class Analyser { init { println("Init Analyser class") } fun doSomething() { println("DoSomething") } } class Book(private val rawText: String) { var analyser: Analyser by Delegates.notNull() fun analyse() { analyser = Analyser() analyser.doSomething() } } fun main() { val rawText = "MyBook" val book = Book(rawText) println("Book is created") book.analyse() }
Logging
You can provide access to the application logging library with a delegate. In function with lazy (see below), it is the most idiomatic way to provide access to the logger to every class needed.
// Idiomatic logging access fun <R : Any> R.logger(): Lazy<Logger> { return lazy { Logger.getLogger(unwrapCompanionClass(this.javaClass).name) } } class Something { val LOG by logger() fun foo() { LOG.info("Hello from Something") } }