Kotlin Extension Functions and Properties: Add Methods & Computed Properties to Existing Classes
1. What are extension functions and properties in Kotlin?
Extension Functions: Allow adding new functions to existing classes without modifying or inheriting from them. They appear as if they are part of the class.
Syntax: fun ClassName.functionName(parameters): ReturnType { ... }.
Receiver: The object on which the extension is called (e.g., this@ClassName).
Extension Properties: Allow adding new properties to existing classes, similar to extension functions but for data access.
Syntax: var ClassName.propertyName: Type or val ClassName.propertyName: Type.
Use Case: Extending third-party libraries, adding domain-specific functions, or improving API usability.
2. Can you give an example of extension functions and properties?
fun String.isValidEmail(): Boolean {
return this.contains("@") && this.contains(".")
}
val String.wordCount: Int
get() = this.split(" ").size
class Person(val name: String, val age: Int)
fun Person.isAdult(): Boolean = age >= 18
fun Person.greet(): String = "Hello, I'm $name"
fun main() {
val email = "[email protected]"
val text = "Hello World"
// Extension functions
println("${email} is valid email: ${email.isValidEmail()}")
println("${text.wordCount} words in text")
val person = Person("Ram", 25)
println("${person.name} is adult: ${person.isAdult()}")
println(person.greet())
}
Output:
[email protected] is valid email: true
2 words in text
Ram is adult: true
Hello, I'm Ram
Note:
String.isValidEmail()extendsStringwith email validation.String.wordCountextendsStringwith a computed property.Person.isAdult()andPerson.greet()extendPerson.- Extension functions can access the receiver (
this) and public members.
3. How do extension functions work in Kotlin?
- Dispatch Resolution: Extension functions are resolved statically at compile time, based on the receiver type.
- Visibility: Can be
public,private,internal, etc., like regular functions. - Overloading: Multiple extensions with the same name but different parameters are allowed.
- Null Safety: Extensions on nullable types (e.g.,
String?) require safe calls (?.). - Use Case: Adding utility functions to standard library types or third-party classes.
4. Can you give an example of advanced extension functions?
fun String.capitalizeWords(): String {
return this.split(" ").joinToString(" ") { it.capitalize() }
}
fun List.average(): Double? {
return if (isEmpty()) null else sum().toDouble() / size
}
fun List.shuffle(): List {
val shuffled = this.toMutableList()
shuffled.shuffle()
return shuffled
}
fun String?.safeLength(): Int {
return this?.length ?: 0 // Safe call for nullable
}
fun main() {
val sentence = "hello world"
println(sentence.capitalizeWords()) // "Hello World"
val numbers = listOf(1, 2, 3, 4, 5)
println(numbers.average()) // 3.0
println(numbers.shuffle()) // Random order, e.g., [3, 1, 5, 2, 4]
val nullable = null
println(nullable.safeLength()) // 0
}
Output (Sample):
Hello World
3.0
[2, 5, 3, 1, 4]
0
Note:
capitalizeWordsprocesses strings with splitting and mapping.averageis a generic extension for lists.shufflemutates a copy of the list.safeLengthhandles nullable strings with safe calls (?.).
5. How do extension properties work in Kotlin?
- Backing Field: Extension properties cannot have a backing field (no storage); they are computed (getter) or computed/set (setter).
- Visibility: Can be
val(read-only) orvar(read-write). - Use Case: Adding computed properties to existing classes (e.g., length of a string, size of a collection).
6. Can you give an example of extension properties?
val String.vowelCount: Int
get() = count { it in "aeiouAEIOU" }
var List.maxValue: Int?
get() = maxOrNull()
set(value) {
if (value != null) {
add(value)
}
}
fun main() {
val text = "Hello World"
println("${text} has ${text.vowelCount} vowels") // 3 vowels
val numbers = mutableListOf(1, 2, 3)
println("Max before: ${numbers.maxValue}") // 3
numbers.maxValue = 10
println("Max after: ${numbers.maxValue}") // 10
println("List: $numbers") // [1, 2, 3, 10]
}
Output:
Hello World has 3 vowels
Max before: 3
Max after: 10
List: [1, 2, 3, 10]
Note:
vowelCountis a computed read-only property.maxValueis a read-write property that modifies the list.- Extension properties are dynamic and computed on access.
7. What are advanced features of extensions in Kotlin?
- Extension on Nullable Types: Use safe calls (
?.) for nullable receivers. - Extension on Generic Types: Extend generic classes (e.g.,
List<T>). - Extension in Companion Objects: Extend companion objects for utility functions.
- Extension Libraries: Create reusable extension files (e.g.,
StringExtensions.kt). - Use Case: Building DSLs or extending standard library types.
8. Can you give an example of advanced extension usage?
fun List.safeGet(index: Int): T? {
return if (index in 0 until size) this[index] else null
}
fun String?.safeUppercase(): String {
return this?.uppercase() ?: "UNKNOWN"
}
class Config {
companion object {
fun loadFromFile(fileName: String): Map {
return mapOf("app" to fileName) // Simplified
}
}
}
fun Config.Companion.loadWithDefault(): Map {
return loadFromFile("default.cfg")
}
fun main() {
val list = listOf("a", "b", "c")
println(list.safeGet(3)) // null
println(list.safeGet(1)) // "b"
val nullable = null
println(nullable.safeUppercase()) // "UNKNOWN"
val config = Config
println(config.loadWithDefault()) // {app=default.cfg}
}
Output:
null
b
UNKNOWN
{app=default.cfg}
Note:
safeGetis a generic extension for safe list access.safeUppercasehandles nullable strings.loadWithDefaultextends the companion object.
9. What are common mistakes in Kotlin extensions?
Extension Functions:
- Overusing extensions, leading to cluttered APIs.
- Ignoring receiver type, causing incorrect dispatch.
Extension Properties:
- Attempting to use backing fields (not supported).
- Forgetting that properties are computed, causing performance issues.
Advanced Usage:
- Misusing nullable extensions without safe calls.
- Extending generic types without proper bounds.
General:
- Not documenting extensions clearly.
- Extending third-party classes without considering updates.
10. What are best practices for Kotlin extensions?
Extension Functions:
- Use for utility functions on existing types (e.g.,
String.isValidEmail()). - Keep extensions focused and single-purpose.
Extension Properties:
- Use computed getters for derived values; avoid heavy computations.
- Handle nullable receivers with safe calls.
Advanced Usage:
- Use generics for type-safe extensions.
- Extend companion objects for factory-like utilities.
General:
- Document extensions with KDoc (e.g.,
/** Validates email. */). - Organize extensions in dedicated files (e.g.,
StringExtensions.kt). - Test extensions with unit tests.
- Follow Kotlin conventions (e.g., camelCase for functions).