Beginner: Kotlin · Null safety, functions and lambdas · topic 8 of 50 · 25 min
Extension functions and scope functions
Explanation
Extension functions
Sometimes you wish a type had one more function: String.toTitleCase(), Int.toRupees(). You cannot change the String class, but Kotlin lets you write an extension function: a function declared as if it were inside the type, using the type name before the function name. Inside, this refers to the value it was called on.
fun String.initials(): String =
split(" ").filter { it.isNotBlank() }.map { it[0].uppercaseChar() }.joinToString("")
fun Int.toRupees(): String = "Rs $this"
fun main() {
println("rahul kumar sharma".initials()) // RKS
println(499.toRupees()) // Rs 499
}Extensions do not really change the class and cannot access its private parts. They are resolved at compile time from the variable's declared type. Android libraries use them heavily (for example in Android KTX), so you will see many handy functions that are really extensions.
Extension properties
val String.wordCount: Int
get() = trim().split(Regex("\\s+")).size
fun main() {
println("Jai Jawan Jai Kisan".wordCount) // 4
}Scope functions
Scope functions run a block of code on an object. They exist to make some common patterns shorter: doing something only if a value is not null, configuring an object, or adding a side action like logging. There are five, and they differ in two ways: how you refer to the object inside the block, and what the call returns.
| Function | Object inside block | Returns | Typical use |
|---|---|---|---|
| let | it | Block result | Run code on a non-null value: x?.let { } |
| run | this | Block result | Compute a result from an object |
| with(obj) | this | Block result | Call several functions on one object |
| apply | this | The object itself | Configure an object: Builder().apply { } |
| also | it | The object itself | Side effects: logging, validation |
data class Order(var item: String = "", var qty: Int = 0, var note: String = "")
fun main() {
val order = Order().apply { // configure: this = the Order
item = "Paneer tikka"
qty = 2
}.also { println("Created $it") } // side effect, returns the order
val summary = with(order) { "$qty x $item" } // compute from it
val coupon: String? = "FEST10"
coupon?.let { println("Applying $it") } // only when not null
println(summary)
}Choosing one
- Need to do something only if not null? ?.let.
- Setting up properties of a new object? apply.
- Logging or checking without changing the flow? also.
- Calling many methods on one object and returning a result? with or run.
Common mistakes
- Nesting scope functions three deep: nobody can tell what this or it means. Use named variables.
- Using apply when you need the block result (apply always returns the object).
- Writing extension functions for things that belong in the class you own. Extensions are best for types you cannot change.
- Expecting an extension to be "overridden" in a subclass. Extensions are chosen by the declared type at compile time.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Extension functions and scope functions · 1/6
fun Int.toRupees() = "Rs $this"An extension function adds a function to a type you cannot change, like String or Int.
Prefer a video by a teacher? Find free videos on “kotlin extension functions scope functions let apply also run with” on YouTube ↗
In real life: Cleaning form input across an app
A job-portal app receives names, emails and phone numbers from many forms. Instead of repeating cleaning code, the team writes extension functions like String.cleanPhone() and String.isValidEmail() in one file. Every form calls the same functions, and when the phone rule changes (for example to accept +91), one file changes.
fun String.cleanPhone(): String = filter { it.isDigit() }.takeLast(10)
fun String.isValidPhone(): Boolean = cleanPhone().length == 10 && cleanPhone()[0] in '6'..'9'
fun main() {
val typed = "+91 98765-43210"
println(typed.cleanPhone()) // 9876543210
println(typed.isValidPhone()) // true
}Practice (0/3 done)
What does this print? val sb = StringBuilder().apply { append("Chai") append(" time") } println(sb)
Which scope function returns the result of the block and refers to the object as it?
Write extension functions: String.isValidPin() (6 digits, not starting with 0), Double.toIndianRupees() (2 decimals with "Rs "), and List<Int>.secondLargest(): Int?. Then use apply to build a data class object and also to print it.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Null safetyNull safety, functions and lambdas · same module
- Inheritance, interfaces, mixins and extensionsFlutter and Dart · Null safety and object-oriented Dart
- Interview questions: KotlinAndroid career path
- FunctionsFrontend Web Development · JavaScript basics
- Scope and storage classesC Programming (from zero to systems) · Functions and arrays
- Scope and closuresJavaScript Programming (language in depth) · Functions and closures
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Can an extension function access private members of the class it extends?
Answer: No
Extensions are outside the class and only see public members.
Which scope function is best for configuring a newly created object?
Answer: apply
apply returns the object after the setup block.
What does also return?
Answer: The object it was called on
also returns the receiver object.
What is the most common use of ?.let?
Answer: Running code only when a value is not null
x?.let { } skips the block when x is null.
What does an extension method change in the original class?Related
Answer: Nothing; it adds methods usable where the extension is imported
Extensions are compile-time helpers.
Which type should a ViewModel expose for screen state?Related
A variable is declared with const inside a function. Where can it be used?Related
Answer: Only inside that function
let and const are block-scoped: they live only inside their { }.
From Functions
What is the initial value of a global int with no initialiser?Related
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Null safety, functions and lambdas”. The module exam comes next.
- Module 2 exam: Null safety, functions and lambdas · 13 questions · 20 min, without help. 4 topics of this module still to finish first.
- Then the next module, Classes, objects and collections. 50 topics left in the course before the final exam.