[Kotlin Pearls 5] Multiple Inheritance

And other useful tricks of Implementation By Delegation

Uberto Barbini
ProAndroidDev

--

This is the fifth article in a series about little known features of Kotlin.
You can find the previous posts here here here and here.

Another kind of multiple inheritance…

Let’s talk today about what used to be my least favorite Kotlin feature: the by keyword. It was my least favorite because I saw it useful for a very limited number of use cases only and it was an unnecessary complication.

I discovered later that on the contrary, it works very well together with other Kotlin features and it is really beneficial if you know how to use it wisely.

First things first: the by keyword has two different uses in Kotlin:

  • interface implementation delegation. It allows us to implement an interface using a different existing instance of that interface.
  • property delegation. It will enable us to define a common custom behavior for all properties, for example, to read the values from a map.

This post is only about the first use. I will post in the future about the property delegation, which also has some surprises inside.

Let’s have first a taste of the most straightforward case.

Remember that only interfaces can be delegated to. It won’t work with an open abstract class.

So we define an interface with two implementations, one supposed to be the real one and the other for testing purposes.

interface ApiClient {
fun callApi(request: String): String
}
//implementation for testing as singleton
object ClientMock: ApiClient {
override fun callApi(request: String): String = "42"
}
//implementation real as a class
class HttpApiClient: ApiClient {
override fun callApi(request: String): String {
//... lot of code
return "The Answer to $request is 42"
}
}

So far so good. Now let’s say we need another class that implements ApiClient but adding new functionalities.

It’s a perfect case for the by operator. It specifies that we want to implement an interface using an existing instance. It can use an instance created on the fly or an object:

class DoSomething: ApiClient by HttpApiClient(){
//other stuff needed to do something
}
class DoSomethingTest(): ApiClient by ClientMock {
//other stuff needed to do something
}

You can test that it works, but exactly what is happening here?

To see it we can inspect the Kotlin bytecode (on Intellij Tools|Kotlin|Show Kotlin Bytecode) produced by the class DoSomething.

Java ByteCode is not exactly easy to read, but we can easily decompile it back in Java.

Click on the button Decompile to see the Java equivalent code

Now the equivalent Java code of our Kotlin code is this one:

public final class DoSomething implements ApiClient {
// $FF: synthetic field
private final HttpApiClient $$delegate_0 = new HttpApiClient();
@NotNull
public String callApi(@NotNull String request) {
Intrinsics.checkParameterIsNotNull(request, "request");
return this.$$delegate_0.callApi(request);
}
}

It is easy to see now that the compiler put the “delegated implementor” as a hidden field of the class, and then proceed to implement all method of the interface with calls to the corresponding methods on the delegate.

In case you wonder, variable, fields and functions in Kotlin cannot start with the dollar sign, so you cannot overwrite or easily access the $$delegate_0 field from the Kotlin code.

In the examples so far we are creating a new delegate instance or referring to an object singleton. A more useful pattern is actually to pass the delegate as a constructor parameter, like this:

class DoSomethingDelegated(client: ApiClient): ApiClient by client {
//other stuff needed to do something
}

This pattern is quite convenient because we can reuse our class with more flexibility, we just need to pass different objects in the constructor.

What about overriding?

Overriding one or more methods of the interface works just as you would expect. You define a new behavior and your class would ignore the previous one.

class DoSomethingOverriden( client: ApiClient): ApiClient by client {
override fun callApi(request: String): String {
return "something different $request"
}
}

Keep in mind that there are some differences between implementation by delegation and standard override and polymorphism:

  1. The delegate is not aware of the delegator object, so methods of the delegate cannot call the overridden method of the delegator.
  2. Inside the overridden method, you cannot call super, because the delegator is not inheriting from any class.

As far as I know, there is no straightforward way to call the delegate method from inside the override, but we can still call the delegate method in another way. We can store the delegate as private property and call the method directly.

For example, in case we want to print a message before calling the original method, we can do like this:

class DoSomethingWrapper(private val client: ApiClient): ApiClient by client {    override fun callApi(request: String): String {
println("DoSomethingWrapper: hello!")
return client.callApi(request)
}
}

if we really really don’t want to store the client as a property you can create an override on an anonymous object with this quite complicated syntax:

class DoSomethingDoubleBy( client: ApiClient): ApiClient by object : ApiClient by client {
override fun callApi(request: String): String {
println("DoSomethingDoubleBy: hello!")
return client.callApi(request)
}
}

Finally, let’s see how to obtain Multiple Inheritance using delegates.

As an example let’s say we have two interfaces, Printer and Scanner, with two separate implementations and we want to create a PrinterScanner class putting the two together.

data class Image (val name: String)interface Printer{
fun turnOn()
fun isOn(): Boolean
fun printCopy(image: Image)
}
interface Scanner{
fun turnOn()
fun isOn(): Boolean
fun scan(name: String): Image
}
object laserPrinter: Printer {
var working = false
override fun isOn(): Boolean = working override fun turnOn() {
working = true
}
override fun printCopy(image: Image) {
println("printed ${image.name}")
}
}object fastScanner: Scanner {
var working = false
override fun isOn(): Boolean = working override fun turnOn() {
working = true
}
override fun scan(name: String): Image = Image(name)}class ScannerAndPrinter(scanner: Scanner, printer: Printer): Scanner by scanner, Printer by printer { override fun isOn(): Boolean = (this as Scanner).isOn() override fun turnOn() = (this as Scanner).turnOn()

fun scanAndPrint(imageName: String) = printCopy(scan(imageName))
}

There you go! It works precisely as you can imagine.

In the (quite rare) case of having identical functions in both objects, you need to override explicitly like in this example.

Composition over inheritance (or composite reuse principle) in object-oriented programming (OOP) is the principle that classes should achieve polymorphic behavior and code reuse by their composition (by containing instances of other classes that implement the desired functionality) rather than inheritance from a base or parent class. This is an often-stated principle of OOP, such as in the influential book Design Patterns.
Wikipedia

This is true for OOP and even more for Functional Programming. On top of this, in Kotlin classes are not open by default, so inheriting is not an option.

The Kotlin by operator is handy and maybe under-appreciated. Comparing to the standard composition, it makes the delegate “invisible” and so it forces you towards a better design.

As a final example here is a kind of mini library to read domain objects from a database. It separates well (I think) all the different concerns and responsibilities without using inheritance and instead leveraging on the delegation. See the comments in the code for how it works.

typealias Row = Map<String, Any> //a db row is expressed as a Map field->valueinterface DbConnection {
//abstraction on the db connection/transaction etc
fun executeQuery(sql: String): List<Row>
}
interface SqlRunner<out T> { //out is because we can return T or subtypes
// interface to execute sql statement and return domain objects
fun builder(row: Row): T
fun executeQuery(sql: String): List<Row>
}
//declared outside SqlRunner interface to avoid overriding and multiple implementations
fun <T> SqlRunner<T>.fetchSingle(sql: String): T = builder( executeQuery(sql).first() )
fun <T> SqlRunner<T>.fetchMulti(sql: String): List<T> = executeQuery(sql).map { builder(it) }
//a real example would be: class JdbcDbConnection(dbConnString: String): DbConnectionclass FakeDbConnection(): DbConnection{
//trivial example but in reality manage connection pool, transactions etc and translate from JDBC
override fun executeQuery(sql: String): List<Row> {
return listOf( mapOf("id" to 5, "name" to "Joe") )
}
}//now how to use all this for retrieve Usersinterface UserPersistence{
//interface needed by the domain
fun fetchUser(userId: Int): User
fun fetchAll(): List<User>
}
class UserPersistenceBySql(dbConn: DbConnection): UserPersistence, SqlRunner<User> by UserSql(dbConn) {
//translate domain in sql statements but still abstract from db connection,transactions etc.
override fun fetchUser(userId: Int): User {
return fetchSingle("select * from users where id = $userId")
}
override fun fetchAll(): List<User> {
return fetchMulti("select * from users")
}
}
class UserSql( dbConn: DbConnection) : SqlRunner<User>, DbConnection by dbConn {
// implementation for User
override fun builder(row: Row): User = User(
id = row["id"] as Int,
name = row["name"] as String)
// note that we don't need to implement executeQuery because is already in DbConnection
}
object UserRepository: UserPersistence by UserPersistenceBySql(FakeDbConnection())//example of use
fun main() {
val joe = UserRepository.fetchUser(5)
println("fetched user $joe")
}

The full code is on my GitHub project.

If you like this post, please applaud it and follow me on Twitter and Medium.

--

--

JVM and Kotlin independent consultant. Passionate about Code Quality and Functional Programming. Author, public speaker and OpenSource contributor.