Extension functions in Kotlin allow adding new functionality to existing classes without modifying their source code. These functions are defined outside the class they extend and operate on instances of that class, known as the receiver type. For example, to retrieve the last character of a string, define a extension function:
fun String.lastCharacter(): Char = this[length - 1]
The receiver type is String, and the receiver object is the string instance on which the function is invoked. This function can be called as if it were a member method:
println("Kotlin".lastCharacter()) // Output: n
Under the hood, extension functions compile to static methods in Java, with the receiver object passed as the first parameter. They cannot access private or protected members of the class, maintaining encapsulation.
To use an extension function, import it explicitly:
import package.lastCharacter
In Java, call the corresponding static method:
char c = StringExtensions.lastCharacter("Java");
Extention functions are useful for creating utility functions. For instance, a generic joinToString extension for collections:
fun <T> Collection<T>.joinCustom(
separator: String = ", ",
prefix: String = "",
postfix: String = ""
): String {
val builder = StringBuilder(prefix)
for ((idx, item) in withIndex()) {
if (idx > 0) builder.append(separator)
builder.append(item)
}
builder.append(postfix)
return builder.toString()
}
To restrict usage to specific types, define the receiver type accordingly:
fun Collection<String>.merge(
separator: String = ", ",
prefix: String = "",
postfix: String = ""
): String {
val builder = StringBuilder(prefix)
for ((idx, element) in withIndex()) {
if (idx > 0) builder.append(separator)
builder.append(element)
}
builder.append(postfix)
return builder.toString()
}
Extension functions are static and cannot be overridden; they are resolved based on the declared receiver type at compile time.
Extension properties provide a way to add properties to classes without backing fields. They require explicit getter (and setter for mutable types) implementations. For example, an extension property for the last character of a string:
val String.finalChar: Char
get() = this[length - 1]
For mutable types like StringBuilder, define a var property with custom accessors:
var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value) {
setCharAt(length - 1, value)
}
Usage:
println("Kotlin".finalChar) // Output: n
val builder = StringBuilder("Kotlin?")
builder.lastChar = '!'
println(builder) // Output: Kotlin!
In Java, access extension properties via their getter or setter methods, such as StringExtensions.getFinalChar("Java").