Extension Functions and Properties in Kotlin

In this tutorial for Android, you’ll learn to use Kotlin extension functions and properties to extend the functionality of existing classes. By Rajdeep Singh.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Using Nullable Receivers

Everything you’ve learned about extension properties and extension functions also applies if the receiver is a nullable type. If the value of the variable is null, you can check for this == null before doing any operation in the body.

In the next few steps, you’ll change the currentHand in the MainActivity from type Hand to type Hand?. You’ll also make the necessary changes to the extensions to handle the nullable type.

Start by opening MainActivity. Above TODO: 15, you’ll see the variable definition private lateinit var currentHand: Hand.

Remove it and redeclare it as a nullable variable below TODO: 15:

private var currentHand: Hand? = null

Remove currentHand = handsDb.getLoggedInHand()!! above TODO: 16 and add the following snippet instead:

currentHand = handsDb.getLoggedInHand()

Also, replace showDescription with the following:

private fun showDescription(hand: Hand?) {
  binding.welcomeTv.text = getString(
      R.string.welcome_username, 
      hand?.userName ?: "-"
  )

  binding.userDescriptionTv.text = getString(
      R.string.user_description_total_fingers,
      hand?.bio ?: "-", hand?.totalFingers
  )
}

If hand is null, it won’t show the value of the totalFingers extension property. To fix this and print , open Extensions.kt and replace the totalFingers extension property with this:

val Hand?.totalFingers: String
  get() {
    if (this == null) return "-"
    return (fingersCount + thumbsCount).toString()
  }

You’ve changed the type to nullable and added a null check in the body. Now, your code can handle cases where currentHand is null — for example, when a user who isn’t logged in is exploring the app.

Build and run. The app should run the same as before.

Registration process showing suggested usernames

Defining Companion Object Extensions

Another helpful feature you can use is defining extension properties and functions for companion objects of classes. The syntax is the same as when you define extension properties for the classes themselves. For example, if you have a class Human with a companion object like this:

class Human {
  companion object {}
}

You can define an extension function for the companion object like this:

fun Human.Companion.greetOthers() {
  println("Hello other humans")
}

You call extension functions on companion objects using only the class name, like Human.greetOthers().

Defining the Scope of Extensions

All the extensions you’ve defined are in the top level of Extensions.kt, directly under package. You can use the extensions anywhere in the project by importing them at the call site.

One of the good pratices to follow while working with extensions in a big project is to divide them in different files grouped by the receiver class. For example, all the extensions for ImageView goes in one file and so on.

Most of the time, you’ll define extensions only at the top level, but you can also declare an extension for a class inside another class. In those cases, there are multiple implicit receivers.

The instance of the class inside which you declare the extension is the dispatch receiver and the instance of the class on which you define the extension is the extension receiver.

Open MainActivity and add the following snippet below TODO: 18:

private fun Hand?.showGreeting() {
  if (this == null) {
    Toast.makeText(this@MainActivity, getString(R.string.greeting_anonymous),
        Toast.LENGTH_SHORT).show()
  } else {
    Toast.makeText(this@MainActivity, getString(R.string.greeting_user, userName),
        Toast.LENGTH_SHORT).show()
  }
}

This code adds an extension function showGreeting on Hand within MainActvity. Here are a few things to understand about the code above:

  • The scope of the extension is inside MainActivity only. That means that classes outside MainActivity can’t call the extension.
  • In case of name conflict between members of the dispatch and extension receivers, the extension receiver takes precedence. So in the code above, using the this keyword will refer to the instance of Hand.
  • To refer to an instance of a dispatch receiver like MainActivity, the code use-qualified this syntax as this@MainActivity.
  • You can declare the extension as open instead of private so that subclasses of MainActivity can override it.

To call the extension you defined, add the following snippet under TODO: 19:

currentHand.showGreeting()

Build and run the app. If you’re not logged in, login first and you’ll see a Toast message in MainActivity like this:

Scoped extension property greeting

Toast message shown on app screen

Scoped extension property greeting

Congratulations, you’ve completed this tutorial!

Where to Go From Here?

You can download the final project using the Download Materials button at the top or bottom of this tutorial.

In this tutorial, you learned to use extension properties and functions.

If you want to learn more about them, check out Kotlin’s official extensions guide.

If you have any questions or comments, please join the discussion below.