Collections and Extension Functions
Read-only vs. mutable collections
Kotlin applies the same val/var philosophy to collections: prefer read-only by default.
val languages = listOf("Kotlin", "Java", "Go") // read-only List
val mutableLanguages = mutableListOf("Kotlin") // MutableList
mutableLanguages.add("Java")
listOf(...) returns a List with no .add()/.remove() at all, not just a convention, the compiler enforces it. Reach for mutableListOf(...) only when you genuinely need to change the collection after creating it.
Functional operations
val upper = languages.map { it.uppercase() } // ["KOTLIN", "JAVA", "GO"]
val short = languages.filter { it.length <= 4 } // ["Java", "Go"]
val total = listOf(1, 2, 3).reduce { acc, n -> acc + n } // 6
map transforms every element into something new, filter keeps only the elements matching a condition, and reduce combines every element into a single result by repeatedly applying a function to an accumulator (acc) and the next element.
Extension functions
An extension function adds a new function to an existing type, even one you don't own the source code for, without subclassing or modifying it:
fun String.shout(): String = this.uppercase() + "!"
println("hello".shout()) // "HELLO!"
String.shout() reads as "an extension function on String", inside its body, this refers to the specific String it was called on ("hello" here). Kotlin's own standard library, including map, filter, and reduce above, is itself built almost entirely out of extension functions on the built-in collection types.
TIP
Extension functions are resolved at compile time based on the declared type, not real "monkey-patching", they're a safe, readable way to add convenience methods without touching the original class.