.apply() in Kotlin

Learn via video courses
Topics Covered

Overview

Apply is a scope function in Kotlin. Scope functions execute a block of code by providing a temporary scope to an object when it is called with a lambda expression. apply() is called on an object reference and after operating on the object, it also returns the reference to an object. It helps us write complex logic into concise code blocks. In this article, we will be exploring its usage and benefits along with code examples.

Syntax and Usage of .apply()

Definition of .apply() Function

The apply() function is called on an object. It takes a lambda function as an argument, and using this lambda function we can access and configure the properties of the object. It finally returns the reference to this object.

Usage of apply()

  • The apply() function in Kotlin is often used to initialize or configure the properties of an object in a concise and readable manner.
  • It is also useful in chaining multiple operations on a single object in a concise way.
  • it is not allowed with apply()
  • It sends this as an argument
  • It returns this i.e. reference to the object itself.

Object Initialization with .apply()

Let us see how object initialization is facilitated by the apply() function in Kotlin. apply() can be used to initialize various properties of the object in a single go.

We will create a Person class with a few properties and initialize it with and without apply().

Code:

Output:

Explanation:

We are creating a Person class that takes the name and age of a person. The class uses default values for the properties. We are creating two objects person1 and person2. The person1 is initialized with the apply() function and the person2 is initialized manually.

The initialization with the apply() function is concise and easy to understand as compared with manual modification.

Builder Pattern and .apply()

apply() is also used with the builder class in Kotlin. Builder class is a design pattern that provides a structured way of creating objects of the class.

It is a separate class called (the Builder class) that encapsulates the object construction methods. It contains getters and setters and allows the chaining of methods together for more concise and readable code.

Compare the code below:

The chaining of methods looks concise and improves readability.

To facilitate chaining, we use the apply() function in setters as the apply() function operates on the object and returns a reference to this object. This reference can further be used by chained methods to operate on the same object.

Let us take an example of a Car class. For this class, we will use the Builder class and the apply() function in setters.

Code:

Output:

Explanation:

The car class has four properties, make, model, year, and color. The builder class has setters and error handlers, it creates a Car object.

All the setter functions operate on the Car object and also return the same modified object. The next method can be chained to apply modifications.

.apply() vs .run()

Here's a comparison of apply() and run() in Kotlin:

Featureapply()run()
Context objectThis within the lambda refers to the receiver objectThis refers to the lambda result
Return valueReturns the receiver object itselfReturns the lambda result
Common use casesConfiguring object properties, chaining callsPerforming actions on an object, returning a value
Builder patternOften used with the Builder patternLess common with Builder, more general usage
Usage guideThink of apply as "applying" changes to an object and returning it for further use.Think of run as "running" code on an object and returning a result from that code.
Exampleval person = Person().apply { name = "Alice" }val age = person.run { if (name == "Alice") 30 else 25 }

Practical Examples

Example - 1

Let us take an example of file operations performed with the help of the apply() function. It lets us check certain conditions, perform operations, and print the output. Inside the apply() function we will be checking if the file exists. If the file doesn't exist then we will create one. Next, we will perform read and write operations on the same file.

Code:

Output:

Explanation:

We are first checking if the file exists or not. If the file doesn't exist we create one. Next, we add contents to the file. Lastly, we read the contents of the file and print it.

Example - 2

Let us take another example of Smart Home Configuration. We will create an object of SmartHome and inside the apply() function we will change all the default values for all of its properties like homeName, lights, etc. lights is a list and thus instead of modifying it we can simply add components to the list with the help of nested apply() function.

Code:

Output:

Explanation:

The SmartHome class has four properties homeName, thermostat, lights(list), and securitySystem. It also has a method that prints these properties for an object.

In the main method, we use apply to modify the default values of the object. We also add various lights using the apply().

Conclusion

  • The apply() function is applied to the object. It takes a lambda function as an input and returns a reference to the object itself.
  • apply provides a concise way to set properties without explicit references to the object. Thus, it is often used with configuration and initialization.
  • It also facilitates the chaining of method calls especially in the builder class for compact and readable code.
  • it is not allowed with the apply, this keyword is used inside the apply block, and this object itself is returned by the apply block.