Extension functions — a mechanism that allows you to add new functions to existing classes without the need for inheritance or modification of the class itself.
For example, let's add a function that reverses a string to the String class:
fun String.reverse(): String { return this.reversed() } println("abc".reverse()) // "cba"
Extensions do not actually modify the class; they are just syntactic sugar: they compile into static methods that take the instance of the object as the first parameter.
Advantages: conciseness, readability, and extensibility of code. They are well-suited for utility functions for collections, strings, etc.
Pitfalls:
Can extension functions be used to add a new variable (property) that stores state to a class?
Answer: No. Extension properties are always computed properties (getter/setter), not fields. They cannot store state — only compute it on the fly.
val String.secondChar: Char get() = this[1] // Just computation, not storage!
Story
In a data verification project, a developer added an extension property to a model class as if it stored a value, but later noticed that the value was always computed and not remembered, which caused the logic to work incorrectly on multiple calls.
Story
In a large application, extensions were named the same as class methods, which led to confusion: class methods always had priority, and extensions were not called — a day was wasted debugging "invisible" code.
Story
In one of the libraries, extensions were used for private fields of the class, but later it turned out that they had no access rights to private members, which necessitated refactoring the architecture of models.