Delegated Properties in Kotlin

The same pattern to delegate properties I found myself inventing at least several times in different code bases. I’m thrilled to share what I found. Let us start the discussion from the very basics and explain how to allow delegate properties with the following syntax:

val property by someObject::anotherProperty

Let’s start from the basics building blocks. There is by keyword on Kotlin that helps to delegate interfaces and properties. These features will not help to delegate one data class in another. Let’s assume we need to have a property that has to delegate to another object’s property. The basic implementation may look like:

val property: PropertyType 
  get() {
    return base.property
  }

Expression function will make it a bit shorter:

val property: PropertyType 
  get() = base.property

This syntax allows Kotlin compiler to infer the type of the property, so we may simplify the code to the following:

val property 
  get() = base.property

We may even break Coding Conventions and shorten this a bit more:

val property get() = base.property

That one works pretty well, but let me show a nicer one which is based on the delegated properties:

val property by base::foo

That example will not work as is. We need a specific getValue operator function to make the example above work. The documentation on delegated properties suggests do declare the function:

operator fun <R, T> getValue(thisRef: R, property: KProperty<*>): T

This function can be declared as an extension function on the type of property references (base::foo in our case). The type of the bound callable reference is KProperty0<R> where R is the return type of the property.

It turns out we need the following function to implement the delegation above:

operator fun <R> KProperty0<R>.getValue(x: Any?, property: KProperty<*>): R = this.invoke()

Now the example above will work, and the following would work:

val property by base::foo

The Code

To summarize, the full example is as follows. We may keep that getValue function somewhere in the utils to allow more usages.

operator fun <R> KProperty0<R>.getValue(x: Any?, property: KProperty<*>): R {
  return this.invoke()
}

class Y(val x: X) {
  val boo by x::foo
}

The JVM Bytecode Level

For someone, it is always nice to question about the overhead of the trick above. Our implementation uses the bound callable reference underneath, and it may consume some resources. The easiest way to see that is to use the Kotlin Bytecode feature in IntelliJ. I’ve been speaking about that trick last summer. First we use the Find action… (CMD/CTRL+SHIFT+A) popup and type the action name:

Kotlin Bytecode Action

In the opened Kotlin Bytecode tool window we may see the generated bytecode for our delegated property code, it is probably quite hard to understand, so we click the Decompile button to see the same bytecode as decompiled Java code, that would be easier to understand:

Kotlin Bytecode Decompile

From the bytecode side we’ll see the following code Java decompiled for the Y class:

public final class Y {
   // $FF: synthetic field
   static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(Y.class), "boo", "getBoo()Ljava/lang/String;"))};
   @NotNull private final KProperty0 boo$delegate;
   @NotNull private final X x;
   @NotNull public final String getBoo() {
      return (String)UtilsKt.getValue(this.boo$delegate, this, $$delegatedProperties[0]);
   }
   public Y(@NotNull X x) {
      Intrinsics.checkParameterIsNotNull(x, "x");
      super();
      this.x = x;
      this.boo$delegate = new Y$boo$2(this.x);
   }
   // ... some code is omitted
}

final class Y$boo$2 extends PropertyReference0 {
   @Nullable public Object get() {
      return ((X)this.receiver).getFoo();
   }
   // ... some code is omitted
}

public final class UtilsKt {
   public static final Object getValue(@NotNull KProperty0 $this$getValue, @Nullable Object x, @NotNull KProperty property) {
      Intrinsics.checkParameterIsNotNull($this$getValue, "$this$getValue");
      Intrinsics.checkParameterIsNotNull(property, "property");
      return $this$getValue.invoke();
   }
   // ... some code is omitted
}

From the code we see that the delegated property essentially compiled into the call to the Y$boo$2 delegate, that simply calls the respective getter of the type X.

From that we see that the shorter delegation costs us an extra method call. We have to also pay an extra object with one field of memory per object instanc for that. I’ve made several experiments with inline keyword which did not help to evaporate the Y$boo$2 class usage.

val x = ...

// the delegation (it allocates an instance field to store the x::foo class
val boo by x::foo

// the operator that is expected by the `by` expression
operator fun <R> KProperty0<R>.getValue(x: Any?, property: KProperty<*>): R {
  return this.invoke()
}
comments powered by Disqus