Interface Delegation with 'by'
Delegate interface implementations with the 'by' keyword: composition over inheritance patterns.
interface Logger {
fun log(level: String, message: String)
fun info(message: String) = log("INFO", message)
fun error(message: String) = log("ERROR", message)
fun debug(message: String) = log("DEBUG", message)
}
class ConsoleLogger : Logger {
override fun log(level: String, message: String) {
println("[$level] $message")
}
}
class FileLogger(private val filename: String) : Logger {
override fun log(level: String, message: String) {
println("[FILE:$filename] [$level] $message")
}
}
// Delegation — forward Logger calls to provided implementation
class UserService(logger: Logger) : Logger by logger {
fun createUser(name: String) {
info("Creating user: $name")
// business logic
info("User created: $name")
}
}
// Delegation with override
class AuditedService(private val inner: Logger) : Logger by inner {
private val auditLog = mutableListOf<String>()
override fun log(level: String, message: String) {
auditLog.add("$level: $message")
inner.log(level, message) // forward to delegate
}
fun getAuditLog(): List<String> = auditLog.toList()
}
// Multiple delegations
interface Cache<K, V> {
fun get(key: K): V?
fun put(key: K, value: V)
fun clear()
}
class InMemoryCache<K, V> : Cache<K, V> {
private val store = mutableMapOf<K, V>()
override fun get(key: K): V? = store[key]
override fun put(key: K, value: V) { store[key] = value }
override fun clear() { store.clear() }
}
interface Metrics {
fun increment(name: String)
fun getCount(name: String): Int
}
class SimpleMetrics : Metrics {
private val counts = mutableMapOf<String, Int>()
override fun increment(name: String) { counts[name] = (counts[name] ?: 0) + 1 }
override fun getCount(name: String) = counts[name] ?: 0
}
// Compose multiple interfaces via delegation
class CachedApiService(
cache: Cache<String, String>,
metrics: Metrics,
logger: Logger
) : Cache<String, String> by cache,
Metrics by metrics,
Logger by logger {
fun fetchData(key: String): String {
increment("fetch_total")
val cached = get(key)
if (cached != null) {
increment("cache_hit")
info("Cache hit: $key")
return cached
}
increment("cache_miss")
info("Cache miss: $key — fetching...")
val data = "data-for-$key" // simulate fetch
put(key, data)
return data
}
fun stats() {
info("Fetches: ${getCount("fetch_total")}")
info("Hits: ${getCount("cache_hit")}")
info("Misses: ${getCount("cache_miss")}")
}
}
fun main() {
// Simple delegation
val service = UserService(ConsoleLogger())
service.createUser("Alice")
// Audited
println("\n--- Audited ---")
val audited = AuditedService(ConsoleLogger())
audited.info("Starting")
audited.error("Something failed")
println("Audit log: ${audited.getAuditLog()}")
// Composed service
println("\n--- Composed ---")
val api = CachedApiService(
InMemoryCache(),
SimpleMetrics(),
ConsoleLogger()
)
api.fetchData("users")
api.fetchData("users") // cache hit
api.fetchData("posts")
api.stats()
}Use Cases
- Composition over inheritance
- Decorator/proxy pattern implementation
- Cross-cutting concern composition
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
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
Custom Property Delegates
Create reusable property delegates: validation, logging, caching, and thread-safe lazy initialization.
Best for: Input validation on property assignment
Null Safety — Advanced Patterns
Master null safety in Kotlin: chaining, smart casts, safe builders, orElse patterns, and nullable collections.
Best for: Safe navigation through nested nullable objects