如何在 Kotlin 中明确指定 lambda 的实际类型?
我认为有两个选项可以使代码更加清晰:泛型和函数式接口。它们也可以组合!
选项 1:泛型(* 请参阅下面的更新)
不要指定返回值 - 改用泛型。我们可以只为所有交互使用一个函数:
fun <R> use(block: Context.() -> R) : Boolean = context.block()
如果block 不返回布尔值,您还希望返回默认值true。我们也可以这样做:
fun <R> use(block: Context.() -> R): Boolean {
// use a 'when' block - it solves type safety issues
return when (
val result = context.block() // execute the lambda and store the output in a val
) {
!is Boolean -> true // default to 'true' if result is not a boolean
else -> result // else, we can return the boolean result of 'block'
}
}
*更新实际上,泛型不是必需的,因为您的 use(...) 函数总是返回布尔值,而不是 lambda 的结果。
这里有一个非泛型 use(...) 函数,可以解决“两个函数的 jvm 签名冲突”的问题:
fun use(block: Context.() -> Any): Boolean {
// use a 'when' block - it solves type safety issues
return when (
val result = context.block() // execute the lambda and store the output in a val
) {
!is Boolean -> true // default to 'true' if result is not a boolean
else -> result // else, we can return the boolean result of 'block'
}
}
选项 2:功能接口
使用functional interfaces - 它使 lambda 的定义更加清晰和可重用。
// step 1: define our 'lambdas'
// equivalent: Context.() -> Boolean
fun interface ContextPredicate {
fun Context.execute(): Boolean
}
// equivalent: Context.() -> Unit
fun interface ContextConsumer {
fun Context.execute()
}
// step 2: define the 'use' functions
class ContextService(
private val context: Context
) {
fun use(block: ContextPredicate): Boolean = with(block) {
context.execute()
}
fun use(block: ContextConsumer): Boolean = with(block) {
context.execute()
true // default response is 'true' for 'ContextConsumer'
}
}
// 3. dummy class for testing
data class Context(
val name: String
)
// 4. test our code
fun main() {
val service = ContextService(Context("hello"))
println(
service.use(ContextPredicate { name == "hello" })
)
// output: true
println(
service.use(ContextPredicate { name == "good morning" })
)
// output: false
println(
service.use(ContextConsumer { println("~~~$name~~~") })
)
// output:
// ~~~hello~~~
// true
}
现在返回类型没有歧义了 - use(...) 函数要么接受谓词,要么接受消费者,故事结束。
注意:with(block) { ... } (read more about it here) 是必需的,因为我们定义的功能接口具有接收器。
泛型 + 函数式接口
我们可以将两者结合起来,以获得两全其美:简洁明了:
// define a generic fun-interface
fun interface ContextFunction<R> {
// change Context to be a parameter, not a receiver
fun execute(context: Context): R
}
class ContextService(
private val context: Context
) {
// use our generic fun-interface
fun <R> use(block: ContextFunction<R>): Boolean {
return when (val result = block.execute(context)) {
// if the result isn't a boolean, then the default response is 'true'
!is Boolean -> true
// else, we can use the actual result
else -> result
}
}
}
// usage is the same as 'Option 1: Generics'
fun main() {
val service = ContextService(Context("hello"))
println(
service.use { it.name == "hello" }
)
// output: true
println(
service.use { it.name == "good morning" }
)
// output: false
println(
service.use { println("~~~${it.name}~~~") }
)
// output:
// ~~~hello~~~
// true
}