Chapter 19: Extension Functions & Properties
Extension Functions & Properties — this is one of the most loved and most powerful features in Kotlin! ☕✨
Extension functions and properties are the reason many developers say: “Kotlin feels like the language is built for me” — because you can add new behavior to existing classes (even from Java libraries like String, List, View, etc.) without modifying their source code!
Imagine we’re sitting together in a cozy Bandra café — I’m going to explain this like I’m teaching my younger brother who just discovered he can give his old bicycle new superpowers without changing the bicycle itself.
We’ll go super slowly, with real-life analogies, lots of complete runnable examples, step-by-step breakdowns, tables, common mistakes with fixes, and practical tips so everything sticks perfectly.
Let’s dive in!
1. What Are Extension Functions?
An extension function is a normal function that you can call as if it were a member of an existing class — even if you don’t own that class!
Syntax (very simple):
|
0 1 2 3 4 5 6 7 8 |
fun ReceiverType.extensionFunctionName(parameters): ReturnType { // 'this' refers to the receiver (the object you're extending) } |
Receiver type = the class you’re adding the function to this inside the function = the object you’re calling it on
Real-life analogy: You have a bicycle (existing class). You can’t change the bicycle factory, but you can give it rocket boosters (extension function). Now every bicycle can fly — without touching the original design!
Example 1 – Basic extension on String
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Extension function on String fun String.shout(): String { return this.uppercase() + "!!!" } fun main() { val message = "Hello Webliance" println(message.shout()) // HELLO WEBLIANCE!!! // Works on any String! println("Kotlin is awesome".shout()) // KOTLIN IS AWESOME!!! } |
Step-by-step:
- fun String.shout() → we’re adding shout() to every String
- Inside the function → this refers to the String we called it on
- Call it like a normal method: message.shout()
2. Extension Properties
Just like functions — you can add properties to existing classes!
Syntax:
|
0 1 2 3 4 5 6 7 8 9 10 11 |
val ReceiverType.propertyName: ReturnType get() = ... // getter var ReceiverType.propertyName: ReturnType get() = ... set(value) { ... } |
Example 2 – Extension property on String
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
val String.isPalindrome: Boolean get() = this == this.reversed() fun main() { println("radar".isPalindrome) // true println("kotlin".isPalindrome) // false println("".isPalindrome) // true (empty string is palindrome) } |
Example 3 – Mutable extension property
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
var String.lastChar: Char get() = this[this.length - 1] set(value) { // Strings are immutable → we can't change them! // But we can throw or return new string throw UnsupportedOperationException("Strings are immutable!") } fun main() { println("Kotlin".lastChar) // n // "Kotlin".lastChar = 'x' // Throws exception } |
Important: Extension properties don’t add real fields — they are just getters/setters. You cannot add real backing fields to existing classes.
3. this & Receiver – Understanding the Magic
Inside an extension function/property:
- this refers to the object you’re extending (the receiver)
- You can omit this. most of the time (just like inside normal class methods)
Example 4 – Using this explicitly
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
fun String.addExclamation(): String { return this + "!" // this is the String // or just: return this + "!" (same) } fun String.repeatTwice(): String { return this.repeat(2) // Calls String.repeat() } |
Fun fact: You can even use qualified this if there is name shadowing:
|
0 1 2 3 4 5 6 7 8 |
fun String.greet(other: String) { println("Hello $this! How is $other?") } |
4. Overriding Extensions – The Rules
Important: Extension functions cannot be overridden like normal methods.
Why? Extensions are resolved statically (at compile time) based on the declared type — not the runtime type.
Example – No overriding
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
open class Animal class Dog : Animal() fun Animal.makeSound() = println("Generic animal sound") fun Dog.makeSound() = println("Woof Woof!") fun main() { val animal: Animal = Dog() animal.makeSound() // Generic animal sound (static resolution!) } |
Output:
|
0 1 2 3 4 5 6 |
Generic animal sound |
Even though animal is actually a Dog at runtime → Kotlin chooses the extension based on declared type (Animal).
Lesson: Extensions are not polymorphic — they are static dispatch.
5. Quick Recap Table (Your Cheat Sheet)
| Feature | Kotlin Way (Best Practice) | Key Point |
|---|---|---|
| Extension function | fun String.shout() = this.uppercase() + “!!!” | Add methods to existing classes |
| Extension property | val String.isPalindrome: Boolean get() = … | Add getters/setters without fields |
| this in extension | this = the receiver object | Can omit this. most of the time |
| Overriding extensions | Not possible – static dispatch | Choose based on declared type, not runtime |
| Use case | Extend Java libraries (String, View, List, etc.) | Clean, no inheritance needed |
6. Common Newbie Mistakes & Fixes
| Mistake | Problem | Fix |
|---|---|---|
| Forgetting this in extension | Compile error (e.g., length not found) | Use this.length or just length (omit) |
| Trying to override extension | Wrong method called at runtime | Extensions are not overridable |
| Adding extension property with backing field | Not allowed | Extensions can’t have backing fields |
| Not using extensions for Java interop | Verbose code | Extend Java classes like String, View |
| Confusing this in nested scopes | Wrong object referenced | Use qualified this@OuterClass if needed |
7. Homework for You (Let’s Make It Fun!)
- Basic Create an extension function on String: fun String.reverseAndShout() → returns uppercase reversed string with “!!!”.
- Medium Create extension property on Int: val Int.isEven: Boolean → true if even.
- Advanced Create extension function on List<String>: fun List<String>.joinWithSmiles() → joins with ” 😊 “.
- Fun Create extension on String: fun String.toMorseCode() (simple mapping, e.g., “SOS” → “… — …”).
- Challenge Create extension on Int: infix fun Int.timesRepeat(action: () -> Unit) → repeat action that many times.
You’ve just unlocked Kotlin’s superpower — extensions! Now you can make any class (even Java’s) feel like it was built for Kotlin.
