How to Work with Delegation in Kotlin

What is delegation in computer science? Delegation is the assignment of authority from one instance to another. Delegation can entail mutable as well as static relationships between classes, and inheritance, in turn, is based on the constant concept. What does this mean?

Let’s say you want to give class B the functionality of class A and condition "is a" is met. In this case, you can use inheritance. Inheritance gives you a permanent static relationship between these classes that you can’t change. Using delegation, you can pass an object of another type, such as a subtype of class A, to an instance of class B. This makes delegation an extremely powerful mechanism.

There are two types of delegation:

  • Explicit delegation, which can be implemented in any object-oriented language.

  • Implicit delegation, which requires language-level support for the delegation pattern.

Explicit Delegation

Explicit delegation can be implemented in any object-oriented programming language. Let’s look at an example:

class View { 
  void show() {
    print("View.show()");  
  }   
}
class Screen {
  private View view; // delegation link
  public Screen(View view) {
    this.view = view;
  }
  void show() {
    view.show();
  } 
}
View view = new View();
Screen screen = new Screen(view); // establish delegation between two objects
screen.show(); //View.show()

This example was written in Java. But I don’t use any language-specific features to implement the delegation pattern. The delegation pattern is a design pattern that allows object composition to achieve the same level of code reuse as inheritance. Keep in mind that you can use any implementation of type A:

class CustomView extends View {
    @Override
    void call() {
        System.out.println("CustomView.show()");
    }  
}
View customView = new CustomView();
Screen screen = new Screen(CustomView);
screen.show(); //CustomView.show()

You can also use interfaces:

interface Showable {
    void show();
}
class View implements Showable {
    @Override
    public void show() {
        System.out.println("View.show()");
    } 
}

class CustomView implements View {
    @Override
    public void show() {
        System.out.println("CustomView.show()");
    }  
}

class Screen implements Showable {
    private Showable showable;
    Screen(Showable showable) {
        this.showable = showable;
    }
    @Override
    public void show() { 
        showable.show(); 
    } 
}

Showable view = new View();
Showable customView = new CustomView();
new Screen(view).show(); //View.show()
new Screnn(customView).show(); //CustomView.show()

The delegation pattern builds mutable relationships between classes. That’s why it’s a flexible and powerful mechanism. As you can see in the previous the examples, explicit delegation requires a lot of effort to write methods that contain forwarding calls to delegate instances.

Because explicit delegation requires so much effort, a lot of languages support this feature out of the box.

Implicit Delegation

Implicit delegation is a language-level feature that allows an object to designate another object as its “parent.” Implicit delegation can be further divided into unanticipated delegation and anticipated delegation. Unanticipated delegation refers to a case when the delegation structure can be changed dynamically. Anticipated delegation refers to a case when an object can’t change its parent during its lifecycle.

There are many languages, including Kotlin, that offer delegation out of the box. Kotlin is designed to be a “better” language than Java and to interoperate with it. Kotlin is a statically typed language that runs on the Java Virtual Machine (JVM) and can be compiled to JavaScript source code. Moreover, the creators of Kotlin – JetBrains – are working on compiling to native binaries that run without the JVM.

Unlike Java, Kotlin supports delegation natively in the form of class delegation and delegated properties.

The class delegation feature uses the delegation pattern as an alternative to inheritance. You can see how it’s implemented in Kotlin at the language level.

interface Nameable {
    var name: String
}
class JackName : Nameable {
    override var name: String = "Jack"
}
class LongDistanceRunner: Runnable {
    override fun run() {
        println("long")
   }
}
class Person(name: Nameable, runner: Runnable): Nameable by name, Runnable by runner
fun main(args: Array<String>) {
    val person = Person(JackName(), LongDistanceRunner())
    println(person.name) //Jack
    person.run() //long
}

Implicit delegation is a powerful feature that allows you to extend class functionality easily and conveniently. As you can see, you don’t need to implement methods from interfaces manually, since a compiler does this for you. This feature allows you to implement things like multiple inheritance similar to the example above. As you can see from the example, Kotlin uses the simple keyword “by” to define a delegate.

Delegated Properties

To use a property in Kotlin, simply refer to it by name:

class Foo {
    var prop: String? = null
}
...
val foo = Foo()
foo.prop = "something"
val another = foo.prop

You can also create custom getters and setters:

var str: String
    get() = this.toString()
    set(value) {
        println(value)
        field = value
    }

Sometimes your getter and setter methods contain the same code. To prevent duplication of code or just to encapsulate the logic of these functions, you can use delegated properties.

class Example {
    val someName by NameDelegate()
}
class NameDelegate {
    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return property.name
    }
}

The method getValue takes two arguments: a reference to the class that contains the property and an instance of the KProperty class from the Reflect package, which contains metadata. Now, whenever you refer to this property you get someName.

You can also create a delegate with both a getter and setter and use it with the var (mutable) property.

class SimpleDelegate {
    operator fun getValue(thisRef: Any, property: KProperty<*>): String {
        return thisRef::class.java.name
    }
 
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("you pass me $value")
    }
}
...
var mutable by SimpleDelegate()

To meet the property delegate requirements more easily, your delegate classes can implement ReadOnlyProperty or ReadWriteProperty interfaces.

The concept of the delegate properties feature differs from class delegation. You just saw an example of hybrid delegation. The difference between simple Delegation pattern and Hybrid Delegation pattern is that with Hybrid delegation the delegate isn’t the “parent” of the delegating class, but contains a reference to the outer instance. Thus, you allow the delegate object to use the functionality of the delegating class.

Standard Delegates

The Kotlin standard library contains several implementations of the delegated properties feature.

Lazy

val tvName by lazy {
        findViewById(R.id.tvName) as TextView
    }

Lazy is the most popular delegate. Its name speaks for itself. The code in lambda executes only when someone refers to this property.

Observable


var name: ViewState by Delegates.observable(ViewState()) { d, old, new ->
        updateView(new)
    }

The code above represents an implementation of all known observer pattern. You can use this feature to implement a reactive approach in your app.

Map

class UserResponse(val map: Map<String, Any?>) {
    val user: User by map
}

class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}

fun main(args: Array<String>) {
    val user = mapOf(
            "name" to "Jack",
            "age" to 40
    )
    val response = mapOf(
            "user" to User(user)
    )

    val userResponse = UserResponse(response)
    println(userResponse.user.name) // Jack
    println(userResponse.user.age)  // 40
}

Map is a convenient tool to parse data with a key-value structure. You can use it to unmarshal JSON or other similar data formats. It appears that under the hood there’s an extension function for map that meets the getValue requirement.

Vetoable

val name by vetoable("Jack") {property: KProperty<*>, oldValue, newValue ->
        newValue.startsWith("J")
    }

You can use Vetoable to establish a restriction on setting the value. If the callback returns true, the value of the property has been set to the new value, and if the callback returns false the new value is discarded and the property retains its previous value.

NotNull

class Person {
    var name : String by notNull()
}

fun main(args: Array<String>) {
    val person = Person()
    person.name // java.lang.IllegalStateException: Property name should be initialized before get.
    person.name = "Jack"
    print(person.name) //Jack
}

You can use the NotNull feature to prevent the use of nullable types and refer to the field without “?”. Trying to read the property before the initial value has been assigned results in an exception.

Since version 1.1 of Kotlin, you have been able to use delegated properties inside function bodies, and the provideDelegate operator allows you to check a property that uses Delegate before providing a delegate.

class StringProvider {
    operator fun provideDelegate(
            thisRef: Main,
            prop: KProperty<*>
    ): ReadOnlyProperty<Main, String> {
        if(checkProperty(thisRef, prop.name)) {
            return object : ReadOnlyProperty<Main, String> {
                override fun getValue(thisRef: Main, property: KProperty<*>): String {
                    return "true string"
                }
            }
        } else {
            return object : ReadOnlyProperty<Main, String> {
                override fun getValue(thisRef: Main, property: KProperty<*>): String {
                    return "false string"
                }
            }
        }
    }

    private fun checkProperty(thisRef: Main, name: String) = name.length > 4
}

fun bindString() =  StringProvider()

class Main {
    val fake by bindString()
    val valid by bindString()
}

fun main(args: Array<String>) {
    Main().apply {
        println(fake) //false string
        println(valid) //true string
    }
}

Kotlin is a modern language that is constantly being improved. It has already earned the trust and loyalty of many developers. In this article, we have touched on only several features and have reviewed how they work. We’ve seen that the delegation pattern is an extremely powerful mechanism, and it’s a real pleasure that Kotlin implements this concept out of the box. Using class delegation and delegation properties, you can improve readability and quality of code. In this way, you can prevent the emergence of bugs.

3.7/ 5.0
Article rating
20
Reviews
Remember those Facebook reactions? Well, we aren't Facebook but we love reactions too. They can give us valuable insights on how to improve what we're doing. Would you tell us how you feel about this article?

We use cookies to personalize our service and to improve your experience on the website and its subdomains. We also use this information for analytics.

More info