'Kotlin DSL - Generic lambda with multiple receivers
I'm creating a DSL for my project and I really want be able to use fields/methods from multiple receivers without nesting them:
For example:
class Foo {
val fooField = "Foo field"
}
class Bar {
val barField = "Bar field"
}
fun main() {
val foo = Foo()
val bar = Bar()
(foo..bar) {
println(fooField)
println(barField)
}
}
Instead of:
class Foo {
val fooField = "Foo field"
operator fun invoke(block: Foo.() -> Unit) {
block(this)
}
}
class Bar {
val barField = "Bar field"
operator fun invoke(block: Bar.() -> Unit) {
block(this)
}
}
fun main() {
val foo = Foo()
val bar = Bar()
foo {
bar {
println(fooField)
println(barField)
}
}
}
I have thought about something like:
class Buzz<T1, T2>(
val t1: T1,
val t2: T2
) {
operator fun invoke(function: /* T1 + T2*/ () -> Unit) { // <--- ???
function(/* t1 + t2*/)
}
}
operator fun <T, R> T.rangeTo(r: R): Buzz<T, R> {
return Buzz(this, r)
}
But can I combine two receivers somehow without boilerplate?
Solution 1:[1]
You can't do that simply because a single variable can't hold reference of two objects at the same time.
But similar looking solution could be to use a pair, it could relate* with your problem:
val foo = Foo()
val bar = Bar()
Pair(foo,bar).apply { // this: Pair<Foo, Bar>
println(first.fooField)
println(second.barField)
}
Note*: This is just a suggestion as it does remove boilerplate by nested lambdas, but in other hand does not provide a solution with lambda with multiple receivers
Solution 2:[2]
Yes, you can implement this using delegation pattern
interface Foo {
val fooField get() = "Foo field"
}
class FooImpl: Foo
interface Bar {
val barField get() = "Bar field"
}
class BarImpl: Bar
class FooBar(
val foo: Foo,
val bar: Bar
): Foo by foo, Bar by bar {
operator fun invoke(block: FooBar.() -> Unit) {
block(this)
}
}
operator fun Foo.rangeTo(bar: Bar): FooBar = FooBar(this, bar)
fun main() {
val foo = FooImpl()
val bar = BarImpl()
(foo..bar) {
println(fooField)
println(barField)
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Animesh Sahu |
| Solution 2 | Semyon |
