Everything else is just a tool to model your domain in a way that’s easier to read and reason about, and to produce much shorter code. Interfaces define module boundaries and can be mocked to enable simpler testing. Through classes, objects, and data structures, you can model your domain entities. Enums make a list of limited options crystal clear. Extensions allow you to add functionality to existing types, without having access to their original source code, giving advantage of better logic structure. With generics, you can write universal code, promoting reuse. And overloaded operators give that extra edge to common operations that benefit from succinct, crisp syntax.

Operator overloading

You can customize operators for your custom types, however you cannot create new ones. With infix notation, you can write functions that appear as new operators. There’s also an operation called destructuring, that deserves a special attention.

At the end of the day, it’s all syntax sugar, albeit quite convenient one.

Destructuring

Decomposition of a structure into its elements.

Data classes support destructuring out of the box:

data class Point(var x: Double, var y: Double)

val (x, y) = Point(1.0, 2.0)
val (first, second) = Pair("one", "two")

Can be used in for-loops conveniently:

val map = mapOf("yes" to true, "no" to false)
for ((key, value) in map) {
  "$key=$value"
}

Adding support to a custom type, by adding an operator function componentN() for each element that can participate in destructuring:

class IndexedBox<T>(val index: Int, val wrappedValue: T) {
  operator fun component1() = index
  operator fun component2() = wrappedValue
}

val (index, wrappedValue) = IndexedBox(1, "Fodder")

// Also useful in lambda parameters
val unpack: (IndexedBox<String>) -> String =
  { (_, wrappedValue) -> wrappedValue }

[--]