I'm trying to make extensions that wraps part of t...
# announcements
f
I'm trying to make extensions that wraps part of the behavior of interfaces without changing the interface signature. Not finding a scalable solution though. Anyone finding a decent solution would be awesome 🤗 Problem:
Copy code
interface Counter {
    fun add()
    fun remove()
}

// We have some interfaces extending counter
interface MyCounter : Counter {
    fun doStuff()
}

interface MyOtherCounter : Counter {
    fun doOtherStuff()
}

// Implements counter by wrapping a counter and calling add/remove twice.
class DoubleCounter(val baseCounter: Counter) : Counter {
    override fun add() {
        baseCounter.add()
        baseCounter.add()
    }
    override fun remove() {
        baseCounter.remove()
        baseCounter.remove()
    }
}

// These two are okay, but very boilerplate and non-scalable
fun MyCounter.double(): MyCounter {
    val double = DoubleCounter(this)
    return object : MyCounter by this {
        override fun add() = double.add()
        override fun remove() = double.remove()
    }
}
fun MyOtherCounter.double(): MyOtherCounter {
    val double = DoubleCounter(this)
    return object : MyOtherCounter by this {
        override fun add() = double.add()
        override fun remove() = double.remove()
    }
}

// This is illegal, is there any way to do this or similar?
inline fun <reified T: Counter> T.double(): T {
    val double = DoubleCounter(this)
    return object : T by T {
        override fun add() = double.add()
        override fun remove() = double.remove()
    }
}
t
hard with a, probably, fake example. something like
Copy code
interface Counter {
  fun add()
  fun remove()
}

fun Counter.add(times: Int = 1) = (0..times).forEach { add() }
fun Counter.remove(times: Int = 1) = (0..times).forEach { remove() }
maybe?
we actually have some real usages, like
Copy code
interface LineItemRepository {
  fun <T : LineItem> save(lineItem: T): T

  fun <T : LineItem> get(type: KClass<T>, id: LineItem.Id, includeDeleted: Boolean): T

  fun find(claimId: ClaimId): ClaimLineItems

  fun find(parentId: LineItem.Id): ClaimLineItems

  fun delete(id: LineItem.Id)
}

inline fun <reified T : LineItem> LineItemRepository.get(id: LineItem.Id, includeDeleted: Boolean = false) =
  get(T::class, id, includeDeleted)

inline fun <reified T : LineItem> LineItemRepository.getOrNull(id: LineItem.Id) =
  runCatching { get(T::class, id, false) }.getOrNull()
we use extension function to enable reification and to return
null
sometimes, keeping the interface clean