如何检查参数是否为lambda

时间:2018-08-06 13:23:54

标签: lambda kotlin

我正在如下处理MyCustomType的实例集合:

fun runAll(vararg commands: MyCustomType){
    commands.forEach { it.myMethod() }
}

除了MyCustomType的实例外,我还要传递和处理类型为() -> Unit的lambda,如下所示:

fun runAll(vararg commands: Any){
    for(command in commands){
        if (command is MyCustomType){
            command.myMethod()
        } else
        if(command is () -> Unit){
            command.invoke()
        }
    }
}

if(command is () -> Unit){行不与以下消息一起编译:{{1​​}}。

是否可以在运行时检查对象是否为Cannot check for instance of erased type: () -> Unit

我见过another answer that recommends using wildcards。我认为这不相关:我的代码未使用泛型。

2 个答案:

答案 0 :(得分:1)

Kotlin将把您的lambda编译为Function0的实例。如果您知道这一点,则可以使用when块进行比较并很好地智能投射:

fun runAll(vararg commands: Any) {
    commands.forEach {
        when(it) {
            is Function0<*> -> it.invoke()
            is MyCustomType -> it.myMethod()
        }
    }
}

然后调用它:

fun main(args: Array<String>) {
    runAll(
        MyCustomType(), 
        { println("Lambda!") }
    )
}

警告:此方法的缺点是使用类型擦除时,您不知道要获取Function0<Unit>还是Function0<Int>,因为类型在运行时无法确定。这意味着有人可以给你一个lambda,该lambda返回某些内容,而您将忽略结果。

答案 1 :(得分:0)

您的MyCustomType本身不是Function0的原因吗? 如果是这样,您可以只使用vararg commands : () -> Unit,然后不管传递什么,都可以调用command()(这与调用command.invoke()相同)。您甚至可以使用当前代码来做到这一点:

runAll的签名更改为:

fun runAll(vararg commands: () -> Unit) { ...

然后使用:

runAll(MyCustomType()::myMethod, { println("self-built-function") } /*, ... */)

或同一个字母的val稍有不同:

val customObj = MyCustomType()
runAll({ customObj.myMethod() }, { println("self-built-function") })

此外,如果您要求所有命令都具有一定的返回值,则仍然可以这样做,例如如果所有命令都必须返回vararg commands: () -> String,则只需使用String

在这方面使用Any可能会在将来引起头痛。如果缩小类型的范围,则至少可以确保始终具有相同的命令行为。此外,您将以这种方式获得最佳的代码完成支持。使用Any,您可以传递任何内容,但不能run传递所有内容。

如果只想检查参数是否为lambda,则还可以使用以下命令:

someObj is Function<*>

但是,如果不将其强制转换为特定的函数类型,即invokeFunction0等,则无法调用Function1。 关于Function0等,托德已经回答了。但是正如他在警告中还提到的那样:这有一个缺点。我只建议您尽可能地省略Any