Home > Enterprise >  Is there any way to constrain a Kotlin type parameter to an interface type?
Is there any way to constrain a Kotlin type parameter to an interface type?

Time:08-13

I'd like to be able to implement two interfaces with one object, so something like

fun thing(c1: CharSequence, c2: Iterable<Int>) = object : CharSequence by c1, Iterable<Int> by c2

but in a generic way -

fun <C1, C2> thing(c1: C1, c2: C2) = object : C1 by c1, C2 by c2

The compiler complains C1 Only classes and interfaces may serve as supertypes - is there any way that I can placate it?

Motivation:

I'm playing with context receivers, and would like to be able to write a function to construct a context from two implementations, rather than the current:

    val contexts = object :
        Clock by Clock(Instant::now),
        AnalyticsContext by AnalyticsContext(loggingAnalytics) {}
    val routes =
        with(contexts) {
            routesFor(
                stockFile = file,
            )
        }

CodePudding user response:

I don't think there is a way to limit generic type params to just interface types, unfortunately. But if your ultimate goal is just to specify several contexts, you probably don't need to create a third object out of the 2. You could just use 2 with to provide both in scope:

val routes = with(Clock(Instant::now)) {
    with(AnalyticsContext(loggingAnalytics)) {
        routesFor(stockFile = file)
    }
}

If you want a more convenient way to deal with 2 with calls, you could also extract that into your own function (e.g. withBoth) to call it this way:

val x = withBoth(Clock(Instant::now), AnalyticsContext(loggingAnalytics)) {
     routesFor(stockFile = file)
}

Note that, to implement withBoth, I needed to use one context receiver and one regular receiver on the block function type:

fun <C1, C2, R> withBoth(c1: C1, c2: C2, block: context(C1) C2.() -> R): R = block(c1, c2)

This allows calling withBoth both with a function reference and with a lambda. The proper expected way (with 2 context receivers) somehow only allowed withBoth to be called with a function reference as block argument, using a lambda failed with the compile error No required context receiver found. This might be similar to this bug, which will apparently be fixed in 1.7.20

  • Related