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.
Extensions
It is possible to extend functionality of existing types without resorting to inheritance. It is also possible to program standard, reusable functionality using delegation.
Standard delegates
There are common, very useful property delegates already available.
Lazy initialization:
val ifNeeded: String by lazy { "Assigned on first access" }
Observing property changes (a.k.a. property observer):
import kotlin.properties.Delegates
fun log(message: String) {
TODO()
}
var withObserver: String by Delegates.observable("Initial") {
property, oldValue, newValue ->
log("${property.name}: $oldValue -> $newValue")
}
Selective property writes (writing a new value only when it passes validation):
import kotlin.properties.Delegates
var withInterceptor: String by Delegates.vetoable("Initial") {
property, oldValue, newValue -> newValue.isNotBlank()
}
Storing property values in a map:
data class Event(val map: Map<String, Any?>) {
val id: Int by map
val name: String by map
}
data class MutableEvent(val map: MutableMap<String, Any?>) {
var id: Int by map
var name: String by map
}
val event = Event(mapOf(
"id" to 1,
"name" to "something happened"
))
var mutableEvent = MutableEvent(mutableMapOf(
"id" to 1,
"name" to "something happened"
))