Using Kotlin scope functions to create deeply nested Java objects easily
Tags: kotlin, readability
Published: 2020-05-19 07:42:14

In some cases, especially when working with an API with a provided Java SDK, we often find ourselves in creating a (very) deeply nested Java object using setters. Creating Java object with a lot of setters is annoying and error-prone. [Kotlin scope function](https://kotlinlang.org/docs/reference/scope-functions.html) can help to make our code much more readable in these cases.

## Problem

Here is a short example of object creation using setters to understand the problem. A more complex example can [be found below](#more-complex-example).

```kotlin
val name = PersonalName()
name.firstName = "Wile"
name.surname = "Coyote"
name.surnamePrefix = "E."
name.title = "Mr."

val personalInformation = PersonalInformation()
personalInformation.dateOfBirth = "19490917"
personalInformation.gender = "male"
personalInformation.name = name
```

In this example, we have to introduce unnecessary variables to set the child objects, which might easily introduce a bug if forgetting to link mother and child objects.

If Address and PersonalName are data classes, things might get easier:

```kotlin
PersonalInformation(
    dateOfBirth = "19490917"
    gender = "male"
    name = PersonalName(
        firstName = "Wile"
        surname = "Coyote"
        surnamePrefix = "E."
        title = "Mr."
    )
)
```

Unfortunately, these classes are part of an external library, we should not, of course, waste our time rewriting them. But, can we do it better? Yes, we can use the excellent [scope function](https://kotlinlang.org/docs/reference/scope-functions.html) [apply()](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/apply.html "apply function") for this job.

## Refactor using Kotlin scope function

Before giving more explanation in the next section, we will use directly the apply() function to demonstrate the power of the scope function. The apply function is an extension function that is available on any class.

```kotlin
// PersonalName can be rewritten as
val name = PersonalName().apply {
	firstName = "Wile"
	surname = "Coyote"
	surnamePrefix = "E."
	title = "Mr."
}

// And PersonalInformation can be rewritten
val personalInformation = PersonalInformation().apply {
	dateOfBirth = "19490917"
	gender = "male"
	name = name
}
```

or without the intermediary variable **name**:

```kotlin
PersonalInformation().apply {
	dateOfBirth = "19490917"
	gender = "male"
	name = PersonalName().apply {
      firstName = "Wile"
      surname = "Coyote"
      surnamePrefix = "E."
      title = "Mr."
  }
}
```

We can see that this version is not far from the data class version. A more complex example can [be found below](#more-complex-example).

## How does apply() work?

**apply()** function is actually pretty simple. It has following (simplified) signature:

```kotlin
fun <T> T.apply(block: T.() -> Unit): T {
    block()
    return this
}
```

If you aren't familiar with **T.() -> Unit**, technically it is a [function literal with receiver](https://kotlinlang.org/docs/reference/scope-functions.html). In other words, inside the block() function, we have access to the T object via **this** reference. Using the same example above:

```kotlin
val name = PersonalName().apply {
	firstName = "Wile"
	surname = "Coyote"
	surnamePrefix = "E."
	title = "Mr."
}

// is actually equivalent to this more verbose code
val name = PersonalName().apply {
	this.firstName = "Wile"
	this.surname = "Coyote"
	this.surnamePrefix = "E."
	this.title = "Mr."   
}
```

In the second code block, **this** = current instance of PersonalName = **T** in the apply() signature. In a Kotlin/Java class, most of the time, when calling class' method/property, you don't need to specify **this** reference. This is the same idea as in the first code block.

Without the receiver syntax, meaning:

```kotlin
fun <T> T.apply(block: (T) -> Unit): T
```

we'd have to write by specifying **it** reference in the block(). The code is a little longer and less natural. In this case, **it** = current instance of PersonalName

```kotlin
val name = PersonalName().apply {
	it.firstName = "Wile"
	it.surname = "Coyote"
	it.surnamePrefix = "E."
	it.title = "Mr."
}
```

**Apply()** is not the only scope function, there are more other functions like **also**, **let**, **use**, **with**, ... which are very well explained in the [Kotlin reference](https://kotlinlang.org/docs/reference/scope-functions.html). We might talk more about them in a later post.

## More complex example

All examples of this post are borrowed from [Ingenico API documentation](https://epayments-api.developer-ingenico.com/s2sapi/v1/en_US/java/payments/create.html?paymentPlatform=ALL#payments-create-request-example "Ingenico API").

Before using apply()

```kotlin
val billingAddress = Address()
billingAddress.additionalInfo = "b"
billingAddress.city = "Monument Valley"
billingAddress.countryCode = "US"
billingAddress.houseNumber = "13"
billingAddress.state = "Utah"
billingAddress.street = "Desertroad"
billingAddress.zip = "84536"

val name = PersonalName()
name.firstName = "Wile"
name.surname = "Coyote"
name.surnamePrefix = "E."
name.title = "Mr."

val personalInformation = PersonalInformation()
personalInformation.dateOfBirth = "19490917"
personalInformation.gender = "male"
personalInformation.name = name

val customer = Customer()
customer.accountType = "none"
customer.billingAddress = billingAddress
customer.locale = "en_US"
customer.merchantCustomerId = "1234"
customer.personalInformation = personalInformation
```

After using apply():

```kotlin
Customer().apply {
    accountType = "none"
    billingAddress = Address().apply {
        additionalInfo = "b"
        city = "Monument Valley"
        countryCode = "US"
        houseNumber = "13"
        state = "Utah"
        street = "Desertroad"
        zip = "84536"
    }
    locale = "en_US"
    merchantCustomerId = "1234"
    personalInformation = PersonalInformation().apply {
        dateOfBirth = "19490917"
        gender = "male"
        name = PersonalName().apply {
            firstName = "Wile"
            surname = "Coyote"
            surnamePrefix = "E."
            title = "Mr."
        }
    }
}
```

## Conclusion

This post has just touched the tip of the iceberg on the expressivity power of Kotlin. With just a very small function like **apply()**, the readability of our code has been improved considerably. Working with deeply nested Java object is no longer a hassle in Kotlin.