Kotlin Advanced Concepts: Lambda With Receiver, Wildcards, Where Clause, and Delegates
1. Lambda with Receiver
package com.example.lambdawithreceiver
fun <T> T.executeBlock(block: T.() -> Unit) {
this.block()
}
fun <T> runWithParameter(receiver: T, block: T.() -> Unit) {
receiver.block()
}
fun main() {
5.executeBlock {
println(this * 3)
}
runWithParameter("Hello") {
println(this.length)
}
}
2. Star Projection (Wildcard)
package com.example.wildcard
fun main() {
val mixedList: List<*> = listOf(42, "Kotlin", null, 3.14)
mixedList.forEach { element ->
println(element is Double)
}
}
3. Where Clause for Generic Constraints
package com.example.genericconstraints
interface Printable
interface Serializable
interface Comparable<T>
class Product : Printable, Serializable, Comparable<Product> {
override fun compareTo(other: Product): Int = 0
}
class DataHolder<T>(val item: T) where T : Printable, T : Serializable, T : Comparable<T> {
fun logItem() {
println("Holding: $item")
}
}
fun <T> validate(item: T) where T : Printable, T : Serializable, T : Comparable<T> {
println("Validation passed for $item")
}
fun main() {
val holder = DataHolder(Product())
holder.logItem()
validate(Product())
}
4. Out and In Keywords (Covariance/Contravariance)
out marks a type parameter as covariant (read-only, producer), allowing subclass instances to be assigned to supertype variables.
in marks a type parameter as contravarient (write-only, consumer), allowing supertype instances to be assigned to subclas variables.
5. Delegation
5.1 Interface Delegation
package com.example.interfacedelegation
interface UserRepository {
fun fetchAllUsers(): List<String>
}
interface OrderRepository {
fun fetchOrderById(id: Int): String
}
class UserRepositoryImpl : UserRepository {
override fun fetchAllUsers(): List<String> = listOf("Alice", "Bob")
}
class OrderRepositoryImpl : OrderRepository {
override fun fetchOrderById(id: Int): String = "Order #$id"
}
class DataService(
userRepo: UserRepositoryImpl,
orderRepo: OrderRepositoryImpl
) : UserRepository by userRepo,
OrderRepository by orderRepo {
fun fetchCachedData(): String = "Cached data"
}
fun main() {
val dataService = DataService(UserRepositoryImpl(), OrderRepositoryImpl())
println(dataService.fetchAllUsers())
println(dataService.fetchOrderById(101))
println(dataService.fetchCachedData())
}
5.2 Property Delegation
package com.example.propertydelegation
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
class ConfigManager : ReadWriteProperty<Settings, String> {
private var internalValue = "default"
override fun getValue(thisRef: Settings, property: KProperty<*>): String {
println("Reading property: ${property.name}")
return internalValue
}
override fun setValue(thisRef: Settings, property: KProperty<*>, value: String) {
println("Writing property: ${property.name}, New value: $value")
internalValue = value
}
}
class Settings {
var appVersion: String by ConfigManager()
var apiKey: String by ConfigManager()
}
fun main() {
val appSettings = Settings()
appSettings.appVersion = "2.1.0"
println(appSettings.appVersion)
}
5.3 Lazy Delegation
package com.example.lazydelegation
class ReportGenerator {
val compiledData: String by lazy {
println("Compiling report data...")
"Monthly Report: October 2024"
}
}
fun main() {
val generator = ReportGenerator()
println("Report generator initialized")
println(generator.compiledData)
println(generator.compiledData) // No recompilation
}
5.4 Observable Property Delegatino
package com.example.observabledelegation
import kotlin.properties.Delegates
fun main() {
var temperature by Delegates.observable(25) {
property, oldValue, newValue ->
println("${property.name} changed from $oldValue°C to $newValue°C")
}
temperature = 30
temperature = 22
println(temperature)
}