Extension Properties and Receiver Functions
Add properties and functions to existing classes: extension receivers, generic extensions, and DSL patterns.
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
// Extension properties
val String.isEmail: Boolean
get() = matches(Regex("""[\w.+-]+@[\w-]+\.[\w.-]+"""))
val String.wordCount: Int
get() = trim().split("""\s+""".toRegex()).filter { it.isNotEmpty() }.size
val String.titleCase: String
get() = split(" ").joinToString(" ") { word ->
word.replaceFirstChar { it.uppercase() }
}
val Int.isEven: Boolean get() = this % 2 == 0
val Int.isOdd: Boolean get() = this % 2 != 0
val Int.factorial: Long
get() {
require(this >= 0) { "Factorial not defined for negative numbers" }
return if (this <= 1) 1L else (2..this).fold(1L) { acc, i -> acc * i }
}
// Duration extensions
val Int.seconds: Long get() = this * 1000L
val Int.minutes: Long get() = this * 60.seconds
val Int.hours: Long get() = this * 60.minutes
val Int.days: Long get() = this * 24.hours
// Date extensions
val LocalDate.isWeekend: Boolean
get() = dayOfWeek.value >= 6
val LocalDateTime.formatted: String
get() = format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
// Collection extensions
val <T> List<T>.tail: List<T>
get() = if (isEmpty()) emptyList() else subList(1, size)
val <T> List<T>.head: T?
get() = firstOrNull()
fun <T> List<T>.second(): T? = getOrNull(1)
// Safe math extensions
fun Double.roundTo(decimals: Int): Double {
var multiplier = 1.0
repeat(decimals) { multiplier *= 10 }
return kotlin.math.round(this * multiplier) / multiplier
}
// String extensions
fun String.truncate(maxLength: Int, suffix: String = "..."): String =
if (length <= maxLength) this
else take(maxLength - suffix.length) + suffix
fun String.removeHtml(): String =
replace(Regex("<[^>]*>"), "")
fun String.toSlug(): String =
lowercase()
.replace(Regex("[^a-z0-9\\s-]"), "")
.replace(Regex("\\s+"), "-")
.trim('-')
// Generic extension
fun <T : Comparable<T>> T.clamp(min: T, max: T): T = when {
this < min -> min
this > max -> max
else -> this
}
// Extension with receiver
fun <T> buildIf(condition: Boolean, builder: () -> T): T? =
if (condition) builder() else null
// Nullable extensions
fun String?.orDefault(default: String = ""): String = this ?: default
fun <T> List<T>?.orEmpty(): List<T> = this ?: emptyList()
fun main() {
// String extensions
println("test@email.com isEmail: ${"test@email.com".isEmail}")
println("not-email isEmail: ${"not-email".isEmail}")
println("\"hello world\".wordCount: ${"hello world".wordCount}")
println("\"hello world\".titleCase: ${"hello world".titleCase}")
// Truncate
val long = "This is a very long string that needs truncation"
println("Truncated: ${long.truncate(20)}")
// Slug
println("Slug: ${"Hello World! This is Cool".toSlug()}")
// Number extensions
println("\n5.isOdd: ${5.isOdd}")
println("6.isEven: ${6.isEven}")
println("5! = ${5.factorial}")
// Duration
println("\n5.minutes = ${5.minutes}ms")
println("2.hours = ${2.hours}ms")
// Math
println("\n3.14159.roundTo(2) = ${3.14159.roundTo(2)}")
// Clamp
println("150.clamp(0, 100) = ${150.clamp(0, 100)}")
// List extensions
val list = listOf(1, 2, 3, 4, 5)
println("\nHead: ${list.head}")
println("Tail: ${list.tail}")
println("Second: ${list.second()}")
// Date
val now = LocalDateTime.now()
println("\nFormatted: ${now.formatted}")
println("Weekend: ${LocalDate.now().isWeekend}")
// Nullable
val nullStr: String? = null
println("\nDefault: ${nullStr.orDefault("N/A")}")
// HTML
println("Clean: ${"<p>Hello <b>World</b></p>".removeHtml()}")
}Use Cases
- Adding utility methods to existing types
- Domain-specific language sugar
- Null-safe operations on nullable types
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
Custom Property Delegates
Create reusable property delegates: validation, logging, caching, and thread-safe lazy initialization.
Best for: Input validation on property assignment
Extension Functions and Properties
Add methods to existing classes without inheritance: extension functions, properties, and generic extensions.
Best for: Adding utility methods to third-party types
Delegation — by, lazy, observable, and Custom
Use Kotlin delegation for reusable behavior: by keyword, lazy, observable, vetoable, and map-backed.
Best for: Composing behavior without deep inheritance
Enum Classes — Advanced Patterns
Use Kotlin enum classes with properties, methods, interfaces, and companion utilities.
Best for: Type-safe constant sets with behavior