kotlinintermediate

Class Delegation with by Keyword

Use Kotlin's 'by' keyword for class delegation: interface forwarding, decorated behavior, and composition.

kotlin
interface Repository<T> {
    fun findAll(): List<T>
    fun findById(id: String): T?
    fun save(item: T): T
    fun delete(id: String): Boolean
}

class InMemoryRepository<T>(private val idOf: (T) -> String) : Repository<T> {
    private val store = mutableMapOf<String, T>()
    override fun findAll() = store.values.toList()
    override fun findById(id: String) = store[id]
    override fun save(item: T): T { store[idOf(item)] = item; return item }
    override fun delete(id: String) = store.remove(id) != null
}

// Logging decorator via delegation
class LoggingRepository<T>(
    private val delegate: Repository<T>
) : Repository<T> by delegate {
    override fun save(item: T): T {
        println("[LOG] Saving: $item")
        return delegate.save(item)
    }
    override fun delete(id: String): Boolean {
        println("[LOG] Deleting: $id")
        return delegate.delete(id)
    }
}

// Caching decorator
class CachingRepository<T>(
    private val delegate: Repository<T>
) : Repository<T> by delegate {
    private var cache: List<T>? = null
    override fun findAll(): List<T> = cache ?: delegate.findAll().also { cache = it }
    override fun save(item: T): T { cache = null; return delegate.save(item) }
}

// Multiple interface delegation
interface Printer { fun print(doc: String) }
interface Scanner { fun scan(): String }

class SimplePrinter : Printer {
    override fun print(doc: String) = println("Printing: $doc")
}
class SimpleScanner : Scanner {
    override fun scan() = "Scanned document content"
}

class AllInOne(
    printer: Printer, scanner: Scanner
) : Printer by printer, Scanner by scanner

data class Task(val id: String, val title: String)

fun main() {
    val repo: Repository<Task> = CachingRepository(
        LoggingRepository(InMemoryRepository { it.id })
    )
    repo.save(Task("1", "Buy groceries"))
    repo.save(Task("2", "Write code"))
    println("All: ${repo.findAll()}")
    println("All: ${repo.findAll()}") // from cache

    val device = AllInOne(SimplePrinter(), SimpleScanner())
    device.print("Hello")
    println("Scanned: ${device.scan()}")
}

Use Cases

  • Decorator pattern with minimal boilerplate
  • Composing behavior from multiple interfaces
  • Transparent caching and logging layers

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.