Sometimes the way to get things done is to delegate them to someone else. I’m not suggesting that you delegate your work to a friend, but that you delegate the work of one object to another.

Delegating is nothing new in the software industry, of course. Delegation is a design pattern in which objects delegate requests to a helper object, called a delegate. The proxy is responsible for processing requests on behalf of the original object and making the results available to the original object.

Kotlin not only supports proxies for classes and attributes, but also includes some built-in proxies itself, making it easier to implement delegates.

Class representative

Here, for example, you need to implement a use case that is essentially the same as ArrayList, except that this use case can restore the item that was last removed. Basically, all you need to implement this use case is an ArrayList with the same functionality and a reference to the last item removed.

One way to implement this use case is to extend the ArrayList class. Because the new class inherits the concrete ArrayList class rather than implementing the MutableList interface, it is highly coupled to the implementation of ArrayList.

How nice it would be to just override the remove() function to keep references to deleted items and delegate the rest of the empty implementation of MutableList to other objects. To achieve this, Kotlin provides a way to delegate most of the work to an internal ArrayList instance and to customize its behavior, introducing a new keyword: by.

Let’s look at how class proxies work. When you use the by keyword, Kotlin automatically generates code that uses the innerList instance as a proxy:

<! -- Copyright2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
class ListWithTrash <T>(private val innerList: MutableList<T> = ArrayList<T>()) : MutableCollection<T> by innerList {
	var deletedItem : T? = null
	override fun remove(element: T): Boolean {
	       deletedItem = element
			return innerList.remove(element)
	}
	fun recover(a): T? {
		return deletedItem
	}
}
Copy the code

The by keyword tells Kotlin to delegate the functions of the MutableList interface to an internal ArrayList named innerList. ListWithTrash still supports all functions in the MutableList interface by bridging to the internal ArrayList object methods. In the meantime, you can now add your own behavior.

The working principle of

Let’s see how it all works. If you look at the decompile Java code from the ListWithTrash bytecode, you’ll see that the Kotlin compiler actually creates some wrapper functions and uses them to call the corresponding functions of the internal ArrayList object:

public final class ListWithTrash implements Collection.KMutableCollection {
  @Nullable
  private Object deletedItem;
  private final List innerList;

  @Nullable
  public final Object getDeletedItem(a) {
     return this.deletedItem;
  }

  public final void setDeletedItem(@Nullable Object var1) {
     this.deletedItem = var1;
  }

  public boolean remove(Object element) {
     this.deletedItem = element;
     return this.innerList.remove(element);
  }

  @Nullable
  public final Object recover(a) {
     return this.deletedItem;
  }

  public ListWithTrash(a) {
     this((List)null.1, (DefaultConstructorMarker)null);
  }

  public int getSize(a) {
     return this.innerList.size();
  }
   $FF: bridge method
  public final int size(a) {
     return this.getSize();
  }
  / /... .
}
Copy the code

Note: To support class proxies in the generated code, the Kotlin compiler uses another design pattern –Decorator pattern. In decorator mode, the decorator class uses the same interface as the decorator class. The decorator holds an internal reference to the target class and wraps (or decorates) any public methods provided by the interface.

The delegate pattern is useful when you can’t inherit a particular type. By using a class proxy, your class does not inherit from any class. Instead, it shares the same interface with its internal object of source type and decorates that object. This means that you can easily switch implementations without breaking the public API.

The property broker

In addition to class proxies, you can also use the BY keyword for property proxies. By using the property broker, the broker handles calls to the corresponding property get and set functions. This feature is useful when you need to reuse getter/setter logic across other objects, and allows you to easily extend functionality that simply supports fields.

Let’s assume you have a Person type, defined as follows:

class Person(var name: String, var lastname: String)
Copy the code

The name attribute of this type has some formatting requirements. When name is assigned, you want to make sure that the first letter is capitalized and the rest of the letters are formatted in lower case. Also, you want to add the updateCount property automatically when you update the value of name.

You can do this as follows:

<! -- Copyright2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->

class Person(name: String, var lastname: String) {
   var name: String = name
       set(value) {
           name = value.toLowerCase().capitalize()
           updateCount++
       }
   var updateCount = 0
}
Copy the code

The above code certainly works, but what if the requirements change, say you want to add updateCount when the value of lastName changes? You could copy and paste this logic and implement a custom setter, but you’ll find yourself writing exactly the same setter for all the properties.

<! -- Copyright2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->

class Person(name: String, lastname: String) {
   var name: String = name
       set(value) {
           name = value.toLowerCase().capitalize()
           updateCount++
       }
   var lastname: String = lastname
       set(value) {
           lastname = value.toLowerCase().capitalize()
           updateCount++
       }
   var updateCount = 0
}
Copy the code

The two setter methods are almost identical, which means the code here can be optimized. By using a property broker, we can delegate getters and setters to properties, making code reusable.

Like class proxies, you can use BY to proxy a property, and Kotlin generates code to use the proxy when you use the property syntax.

<! -- Copyright2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->

class Person(name: String, lastname: String) {
   var name: String by FormatDelegate()
   var lastname: String by FormatDelegate()
   var updateCount = 0
}
Copy the code

After this modification, the Name and lastName properties are delegated to the FormatDelegate class. Now let’s look at the FormatDelegate code. If you only need to delegate getters, the proxy class needs to implement ReadProperty

; If both getters and setters are delegated, the proxy class needs to implement ReadWriteProperty

. In our example, the FormatDelegate needs to implement ReadWriteProperty

, because you want to do formatting when you call the setter.
?>
?>
?>

<! -- Copyright2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->

class FormatDelegate : ReadWriteProperty<Any? , String> {
   private var formattedString: String = ""

   override fun getValue(
       thisRef: Any? , property:KProperty< * >): String {
       return formattedString
   }

   override fun setValue(
       thisRef: Any? , property:KProperty<*>,
       value: String
   ) {
       formattedString = value.toLowerCase().capitalize()
   }
}
Copy the code

You may have noticed that there are two additional arguments in the getter and setter functions. The first argument is thisRef, which represents the object containing the property. ThisRef can be used to access the object itself for purposes such as checking other properties or calling other class functions. The second argument, KProperty<*>, can be used to access metadata on the proxied property.

Looking back at the requirements, let’s use thisRef to access and increment the updateCount attribute:

<! -- Copyright2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->

override fun setValue(
   thisRef: Any? , property:KProperty<*>,
   value: String
) {
   if (thisRef is Person) {
       thisRef.updateCount++
   }
   formattedString = value.toLowerCase().capitalize()
}
Copy the code

The working principle of

To understand how this works, let’s look at the decompiled Java code. The Kotlin compiler generates code for the Name and lastName properties that holds a private reference to the FormatDelegate object, as well as getters and setters that contain the logic you added.

The compiler also creates a KProperty[] to hold the proxied properties. If you look at the getter and setter generated for the Name property, you’ll see that its instance is stored at index 0, while lastName is stored at index 1.

public final class Person {
  // $FF: synthesizes fields
  static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Person.class), "name"."getName()Ljava/lang/String;")), (KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Person.class), "lastname"."getlastname()Ljava/lang/String;"))};
  @NotNull
  private final FormatDelegate name$delegate;
  @NotNull
  private final FormatDelegate lastname$delegate;
  private int updateCount;

  @NotNull
  public final String getName(a) {
     return this.name$delegate.getValue(this, $$delegatedProperties[0]);
  }

  public final void setName(@NotNull String var1) {
     Intrinsics.checkParameterIsNotNull(var1, "
      
       "
      ?>);
     this.name$delegate.setValue(this, $$delegatedProperties[0], var1);
  }
  / /...
}
Copy the code

With this technique, any caller can access proxy properties through regular property syntax.

Person.lastname = "Smith" // Call the generated setter and increment the amount println(" Update count is $person.count ")Copy the code

Kotlin not only supports the implementation of the delegate pattern, but also provides built-in proxies in the standard library, which we will cover in detail in another article.

Proxies can help you delegate tasks to other objects and provide better code reuse. The Kotlin compiler creates code that allows you to use the proxy seamlessly. Kotlin uses simple by keyword syntax to proxy properties or classes. Internally, the Kotlin compiler generates all the code needed to support the proxy without exposing any changes to the public API. In short, Kotlin generates and maintains the boilerplate code required by all agents; in other words, you can safely delegate your work to Kotlin.