如何扩展Kotlin的with函数使其支持多参数调用?
with for Multiple Parameters Great question! Kotlin's with function is such a clean way to scope operations to a single object, so wanting to extend that pattern to handle multiple targets is totally reasonable. Unfortunately, the native with uses a single-receiver lambda, which means it can't natively expose multiple objects as the implicit this in the block. But we can absolutely build our own version to support multi-parameter use cases—let's break down a few practical options.
Option 1: Dual-Parameter with with Receiver + Argument
This approach keeps the familiar scoping feel, using the first object as the lambda's receiver (this) and passing the second as an explicit argument (you can name it for clarity instead of relying on it):
fun <T1, T2, R> with(first: T1, second: T2, block: T1.(T2) -> R): R { return first.block(second) }
Usage Example:
data class Foo(val name: String) { fun fooFunction() = println("Foo says: $name") } data class Bar(val id: Int) { fun barFunction() = println("Bar's ID: $id") } fun main() { val foo = Foo("Kotlin") val bar = Bar(42) with(foo, bar) { barObj -> fooFunction() // Implicitly uses `this` (foo) barObj.barFunction() // Explicit reference to bar } }
Option 2: Multi-Parameter Lambda with Explicit Arguments
If you prefer full clarity over implicit receivers, this version passes both objects as named parameters to the lambda. It's straightforward and avoids any ambiguity:
fun <T1, T2, R> with(first: T1, second: T2, block: (T1, T2) -> R): R { return block(first, second) }
Usage Example:
with(foo, bar) { fooObj, barObj -> fooObj.fooFunction() barObj.barFunction() }
You can also use destructuring with a Pair if you want to stick closer to the native with signature:
with(foo to bar) { (fooObj, barObj) -> fooObj.fooFunction() barObj.barFunction() }
Option 3: Combined Context Class (For Advanced Use Cases)
If you frequently need to access members of both objects without prefixes, you could create a wrapper class that delegates properties and methods to both targets. This requires a bit more setup but gives you a clean, prefix-free experience:
class FooBarContext(val foo: Foo, val bar: Bar) { // Delegate Foo's members fun fooFunction() = foo.fooFunction() val fooName get() = foo.name // Delegate Bar's members fun barFunction() = bar.barFunction() val barId get() = bar.id } fun <R> with(foo: Foo, bar: Bar, block: FooBarContext.() -> R): R { return FooBarContext(foo, bar).block() }
Usage Example:
with(foo, bar) { fooFunction() // No prefix needed! barFunction() println("Foo: $fooName, Bar: $barId") }
Quick Note on Idioms
The native with's simplicity comes from its single-receiver design. For multiple objects, explicit references (or a wrapper context) are the most idiomatic Kotlin approaches—they avoid confusion and keep your code readable for other developers.
内容的提问来源于stack exchange,提问作者svkaka

