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