Kotlin “By” Class Delegation: Favor Composition Over Inheritance

Eric Lin
Eric Lin
Oct 30, 2017 · 5 min read

When people ask me why I choose Kotlin over Java, I often say, “Because Kotlin is a better Java.” You get more than half of Effective Java implemented for you. In chapter 4 of the book, the author lists many items about inheritance because it is very error prone. The most well known item probably would be Item 16: Favor composition over inheritance. To implement that item, Kotlin introduces the special keyword by to help you with implementing delegation in a single line.

Some side notes:

  • by can be used with properties as well. I will just focus on class delegation in this post.
  • Kotlin also has some other language features to “reduce” the use of inheritance, like typealias for tagging classes and class are final by default.

Inheritance can be problematic

Before jumping directly into the by keyword, let's try to understand the why first. This is always the important part of learning new things. Without knowing what problems the tool tries to solve, you will pretty much end up using the tool just to "be cool."

Problem 1: indeterministic inherited behaviors

I took this example from Fragmented episode #87. The podcast is awesome, and I recommend Android developers listen to it.

Let’s say you want to extend a standard collection to change its default behavior. Maybe you want to count how many items are added into a set. Here is how you would probably implement that:

Simple, right? All you need to do is extend the existing HashSet to avoid reinventing the wheel, and increment the addedCount every time either add() or addAll() is called. It seems very promising, but when you actually try to get the addedCount with the following example usage, you will find it surprising:

The problem here is inside the HashSet#addAll():

It iterates over the collection c and calls add() method for each element, so you end up counting each number twice.

You might argue since we know the implementation details of HashSet, we can just increment the counter inside add(). That would indeed solve this particular problem, but it is still problematic since you don't own the HashSet class and therefore cannot guarantee it would never change.

Problem 2: initialization order in constructor

This might not be directly related to the by keyword, but it is something worth mentioning here.

Sometimes it’s very tempting to write some “template methods” in an abstract class and call them at constructor:

The reason getName() returns null is because in Java super(), the constructor from super class must be called first. Therefore, when line println("I'm ${getName()}") is executed, the property name in Dog has not been assigned yet.

This is a common issue, and even IntelliJ will try to warn you:

With many potential issues, you should just avoid inheritance and program to interfaces not implementations when possible.

Delegation in Java

I want to include some examples in Java to show delegation is already widely used in many libraries and frameworks.

Not surprisingly, Guava, as the core libraries in Google, has implemented a set of Forwarding collections to help us to implement common collection interfaces without extending them.

CountingSet v2

The same counting set can be rewritten with ForwardingSet:

The implementation looks similar to the previous one, but this time we are not dependent on the actual implementation of add() and addAll() since we just "forward" the method calls to the delegate(). We can safely replace the delegate with other types without knowing its implementation details.

It is still an abstract class because Java does not support “delegation” at language level like Kotlin does.

Another delegation we use a lot as Android developers is in AppCompatActivity:

It follows the same pattern. It creates and “delegates” different implementation of AppCompatDelegate.

Delegation in Kotlin

The actual sample code here is going to be very short since Kotlin supports class delegation with by keyword, and we can have delegation in one line.

CountingSet v3

In Kotlin, the same counting set is:

A few things to know here:

#1 The target type must be an interface. It cannot be an abstract class.

#2 The delegate can be a primary constructor property or a new instance. For example you can create a set by writing just class MySet : Set<Long> by HashSet() In our example we need to call the delegate in two overridden functions so we make it a private property.

#3 You can have more than one delegations. For example:

This would work just fine, but since both Map and Set define size and isEmpty(), you would need to implement those two methods so that the compiler won't get confused.


Kotlin introduced the by keyword, making delegation so easy to implement. We should challenge ourselves to avoid inheritance more by using language features.

Rocket Fuel

Exploring the universe of connected devices to help brands…