Skip to Content
Course content

27: Extension Properties

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I was working on a project yesterday where I had a User class defined in a shared library. I couldn't touch the source code of that library, but I found myself constantly concatenating the first and last names of the user throughout my UI code. It felt messy. I wanted a fullName property on the User object, but since I can't modify the class, I figured I'd try an extension property.

Trying the intuitive way

My first instinct was to just declare the property and assign it a value, similar to how I would inside a class. I tried this:

class User(val firstName: String, val lastName: String)

// I want to add this to the User class from the outside
val User.fullName = "${firstName} ${lastName}" 

fun main() {
    val user = User("Jane", "Doe")
    println(user.fullName)
}

The compiler immediately yelled at me. It told me that extension properties cannot have backing fields. This is a crucial distinction. When you add a property to a class via an extension, you aren't actually adding a new field to the memory layout of that object. You're not adding a "slot" to store data in the User instance. If Kotlin allowed this, it would be magic that breaks how the JVM actually handles objects.

Solving it with a getter

So, if I can't store a value, what can I do? I remembered that properties in Kotlin are essentially just functions in disguise. A val is really just a get() method. I decided to pivot and explicitly define the getter instead of trying to assign a value.

class User(val firstName: String, val lastName: String)

val User.fullName: String 
    get() = "$firstName $lastName"

fun main() {
    val user = User("Jane", "Doe")
    // Now this works perfectly
    println(user.fullName) 
}

Now it works. When I call user.fullName, Kotlin isn't looking up a variable in memory; it's executing that get() block. It feels like a property to the person using the class, but under the hood, it's just a function that takes the User instance as a receiver.

What about setters?

I wondered if I could go a step further. What if I wanted to be able to update the firstName and lastName simultaneously using a fullName setter? I tried to implement a var extension property:

var User.fullName: String
    get() = "$firstName $lastName"
    set(value) {
        val parts = value.split(" ")
        // Wait, this only works if firstName and lastName are vars!
    }

I hit another wall. Since my User class used val for the names, I couldn't change them. But even if they were var, I realized that extension properties are purely "computed." They can't hold their own state. If you need to store a new piece of data that isn't already present in the original class, an extension property is the wrong tool—you'd need a wrapper class or a proper inheritance hierarchy.

In short: use extension properties when you want to derive a value from existing data in a way that feels natural to the API, but don't expect them to behave like actual fields in the class.




📋 Practical Task

Implement a String-based Word Counter

You are building a text analysis tool. Instead of writing a utility function like countWords(text), you want to make the code more readable by adding an extension property to the String class.

Your Task:

  • Create an extension property called wordCount for the String class.
  • The property should be a val.
  • The getter should split the string by whitespace and return the number of resulting elements.
  • In your main function, create a sentence (e.g., "Kotlin is an expressive language") and print its wordCount.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.