The main arguments against extending prototypes in JavaScript are twofold:
- The methods you add might have the same name as methods added by some library used in the same application, but have different behaviour.
- Future version of JavaScript/ECMAScript itself might include a method with the same name as the one you're adding, but with different behaviour.
In JavaScript, both of these scenarios will lead to the wrong version of a method being called at runtime, unexpectedly, resulting in a runtime crash or unexpected runtime behaviour.
In Kotlin, most scenarios akin to this will result in a compile-time error or at least a warning, and so the perils encountered when extending types in JavaScript are mostly avoided in Kotlin. However, there is still some slight scope to encounter similar runtime bugs in rare cases.
To illustrate this, let's write some examples of code where conflicting implementations of a method exist, and see what happens.
Scenario 1: Two libraries provide an extension method with the same signature
Suppose we have this code, in Main.kt
and Library1.kt
respectively:
import library1.*
import library2.*
fun main() {
listOf(99, 101, 103, 104).printOddVals()
}
package library1
fun List<Int>.printOddVals() {
for (x in this) {
if (x % 2 != 0) {
println(x)
}
}
}
So the library1
package defines a printOddVals
method that prints the odd values in a list, and main()
uses it. Now suppose that in library2
, we introduce a conflicting printOddVals
method, that prints the values with odd indices:
package library2
fun List<Int>.printOddVals() {
for ((i, x) in this.withIndex()) {
if (i % 2 != 0) {
println(x)
}
}
}
In the equivalent scenario in JavaScript, this would probably cause a runtime bug. In Kotlin, it merely leads to a compile-time error:
Main.kt:5:31: error: overload resolution ambiguity:
public fun List<Int>.printOddVals(): Unit defined in library1 in file Library1.kt
public fun List<Int>.printOddVals(): Unit defined in library2 in file Library2.kt
listOf(99, 101, 103, 104).printOddVals()
^
IntelliJ IDEA will also tell you how to fix the issue - by introducing an import alias:

Do that, and we get this code, with the ambiguity about which printOddVals
we want to call resolved:
import library1.*
import library2.*
import library2.printOddVals as printOddVals1
fun main() {
listOf(99, 101, 103, 104).printOddVals1()
}
Scenario 2: A library introduces a member function that shadows an extension method you already wrote
Suppose we have the following files:
package library1
class Cow {
fun chewCud() {}
}
import library1.*
fun Cow.moo() {
println("MOOOOOOO!")
}
fun main() {
val cow = Cow()
cow.moo()
}
So Cow.moo
is initially an extension method we wrote. But then we update library1, and then new version has a moo
member function:
package library1
class Cow {
fun chewCud() {}
fun moo() { println("moo") }
}
Now, because member functions are preferred to extension functions when resolving method calls, our extension function is not used when we call cow.moo()
in main()
, and then library's new member function is used instead. This is a change in runtime behaviour, and potentially a bug if the library's new moo()
implementation isn't an adequate substitute for the extension function we'd written before. However, the saving grace is that this at least produces a compiler warning:
Main.kt:3:9: warning: extension is shadowed by a member: public final fun moo(): Unit
fun Cow.moo() {
^
Scenario 3: A library introduces a member function that shadows an extension method you already wrote, and doesn't produce a compiler warning
Once we add inheritance (or interface implementation) into the mix, we can contrive a situation similar to the one above where an extension function we were previously using gets partially shadowed by a member function after a library update, causing changes in runtime behaviour, and no compiler warning occurs.
This time, suppose we have these files:
import library1.*
fun Animal.talk() {
println("Hello there! I am a " + this::class.simpleName)
}
fun main() {
val cow = Cow()
val sheep = Sheep()
cow.talk()
sheep.talk()
}
package library1
interface Animal
class Cow : Animal
class Sheep : Animal
At first, when we run our main()
function, our extension function gets used for both cow.talk()
and sheep.talk()
:
Why hello there. I am a Cow.
Why hello there. I am a Sheep.
. But suppose we add a talk
member to Cow
:
package library1
interface Animal
class Cow : Animal {
fun talk() {
println("moo")
}
}
class Sheep : Animal
Now if we run our program, the member function on Cow
gets preferred to the extension method, and our program's behaviour has been changed - all with no compiler errors or warnings:
moo
Why hello there. I am a Sheep.
So although extension functions are mostly safe in Kotlin, there's still a tiny bit of potential to run into the same pitfalls as in JavaScript.
Is this theoretical danger sufficient to mean that extensions should be used sparingly, or perhaps avoided entirely? Not according to the official Coding Conventions, which take the view that extensions are great and you should use them lots:
Extension functions
Use extension functions liberally. Every time you have a function that works primarily on an object, consider making it an extension function accepting that object as a receiver. To minimize API pollution, restrict the visibility of extension functions as much as it makes sense. As necessary, use local extension functions, member extension functions, or top-level extension functions with private visibility.
You are, of course, free to form your own view!