An extension adds a function or property to a type you do not own; inside it, this is the receiver. Unlike patching String.prototype in JavaScript, nothing changes at runtime, and you import an extension where you use it.
fun Double.asPrice() = "$" + "%.2f".format(this)
val Double.isBargain get() = this < 20.0
open class Shape
class Square : Shape()
fun Shape.name() = "Shape"
fun Square.name() = "Square"
fun main() {
println("${39.5.asPrice()}, bargain: ${39.5.isBargain}")
val shape: Shape = Square()
println(shape.name())
}$39.50, bargain: false Shape
The last line prints Shape, not Square: extensions are resolved from the declared type, because each one compiles to a static method that takes the receiver as its first parameter (javap shows public static final java.lang.String asPrice(double) in class ExtKt). So an extension property has no backing field, only a getter, and a member with the same signature always wins.
Android is built on extensions: Compose 234 's 16.dp is the extension property Int.dp, Modifier.padding() extends Modifier, and Android KTX adds "https://example.com".toUri().