Extension functions are one of the key distinctions of Kotlin from Java, first appearing in the early versions of the language inspired by C#. They allow adding new methods to existing classes without the need for inheritance or modification of the original class. This sharply increases the expressiveness and conciseness of the code.
In Java and many other languages, it is impossible to add methods to an already existing class (for example, String or List) without using inheritance or the Decorator pattern. Kotlin addresses this limitation through the mechanism of extension functions.
Many classes of the library (for example, String, Int, List) cannot be changed directly. Often, additional methods are required to make the code more readable and to avoid logic duplication. However, extension functions do not modify the actual class — they are created statically and do not have access to private/protected members.
In Kotlin, declaring an extension function looks like this:
fun String.lastChar(): Char = this[this.length - 1]
Now you can call this method on any String:
"Kotlin".lastChar() // will return 'n'
Can an extension function be overridden in a descendant class?
No. Extension functions are not members of the class, and they cannot be overridden using override: their static nature is evident even for descendants — the compiler chooses the function based on the expression type, not the actual object type.
open class Base class Derived : Base() fun Base.foo() = "base" fun Derived.foo() = "derived" fun printFoo(b: Base) { println(b.foo()) } val d = Derived() printFoo(d) // will output "base"
Can extension functions change the state of an object?
Extension functions cannot access private/protected state, however, if the type is mutable (for example, List or your class), they can change the available external state through public methods.
fun MutableList<Int>.addTwice(elem: Int) { add(elem) add(elem) }
Do extension functions conflict with class members with the same name?
If the extension name matches a real method name, the class member takes precedence. The extension will be called only if the members are absent or not visible.
class Foo { fun bar() = "member" } fun Foo.bar() = "extension" val f = Foo() println(f.bar()) // "member"
A developer extends the standard String with a function that sets a project-specific format and uses it everywhere — while other developers do not notice that this is not a standard operation of the String class.
Pros:
Cons:
Extending the standard class with a utility method that is clearly localized within the utils package and is well documented. Use is limited to clearly specified parts of the code.
Pros:
Cons: