Extension Functions
Adding new functions to existing classes — including ones you don't own the source of — without inheritance.
읽는 데 2분
Extension functions let you add a new function to an existing class — even one from the standard library, or from a third-party dependency you can't modify — without inheriting from it, wrapping it, or touching its source at all.
Basic syntax
fun String.isPalindrome(): Boolean {
val cleaned = this.lowercase().replace(" ", "")
return cleaned == cleaned.reversed()
}
println("racecar".isPalindrome()) // true
println("Was it a car".isPalindrome()) // false — "wasitacar" isn't a palindrome
println("A man a plan a canal Panama".replace(" ", "").isPalindrome()) // trueString.isPalindrome() reads as "an isPalindrome function on String." Inside the function, this refers to the specific String instance the function was called on — "racecar" in the first example — exactly like this inside a real method.
Once declared (and imported, if it lives in a different file or package), it's called exactly like a built-in method:
val word = "level"
if (word.isPalindrome()) {
println("$word is a palindrome")
}There's no way to tell, at the call site, whether isPalindrome() was defined by Kotlin itself or by you.
Why this exists
Without extension functions, adding behavior to a class you don't control means one of two workarounds: a static utility function (StringUtils.isPalindrome(word), which reads backward from how the rest of the language works and doesn't show up in autocomplete on the value itself), or wrapping the class in a new one that adds the method (which means converting back and forth between the wrapper and the original everywhere). Extension functions give you the readability of a real method, on types you couldn't otherwise touch — including String, Int, List<T>, or a class from a library.
A more realistic example
data class CartItem(val name: String, val price: Double, val quantity: Int)
fun List<CartItem>.totalPrice(): Double {
return this.sumOf { it.price * it.quantity }
}
val cart = listOf(
CartItem("Book", 12.99, 2),
CartItem("Pen", 1.50, 5)
)
println(cart.totalPrice()) // 33.48totalPrice() is domain-specific to this app, but it reads exactly as naturally as cart.size or cart.first() — Kotlin doesn't distinguish, from the calling code, between its own built-in extensions and yours.
An important limitation: no real overriding
Extension functions are resolved at compile time based on the declared type of the expression, not the actual runtime type — they aren't true polymorphism, and they can't access private members of the class they extend. They're best thought of as syntactic sugar for a regular function that takes the receiver as its first, implicit parameter — genuinely useful sugar, but not a replacement for real class membership when you need one.
Extension functions round out how Kotlin lets you organize behavior. The next lesson covers sealed classes — a way to model a fixed, known set of possible types that the compiler can check exhaustively.