They let you add functions to a type without modifying its source (syntactic sugar). A common pitfall: extensions are resolved statically by the declared type, so they don’t behave like virtual overrides.
open class Base
class Child : Base()
fun Base.say() = "base"
fun Child.say() = "child"
val x: Base = Child()
println(x.say()) // "base" (static dispatch)