Any之间的联盟类型?和单位

时间:2017-09-11 16:42:01

标签: kotlin

我试图编写一个允许我这样做的高阶函数:

blah1 { em ->
    // do some stuff here
}

以及:

blah2 { em ->
    // do some stuff here
    listOf("1","2","3")
}

在第一个例子中,我没有返回任何内容,在第二个例子中,我返回一个字符串列表。

这适用于第一个:

inline fun blah1(f: (em: EM) -> Unit): Result {
    f(em)
    return Result()
}

对于第二个,我需要一个不同的返回类型:

inline fun blah2(f: (em: EM) -> Any?): Result {
    val res = f(em)
    when (res) {
        is List<*> -> res.forEach { item ->
            // do something with item
        }
    }
    return Result()
}

是否可以合并blah1blah2,以便我这样做:

blah { em ->

}

val res = f(em) res将是Unit类型,当我这样做时:

blah { em ->
   listOf("1","2","3")
}

val res = f(em) res的类型为List?

我试过这样做:

inline fun blah(
    noinline f1: (em: EM) -> Unit = f1fun,
    noinline f2: (em: EM) -> Any? = f2fun
): Result {
    f1(em)
    f2(em)?.let { res ->
        when (res) {
            is List<*> -> res.forEach { item ->
                // do something with item
            }
        }
    }
    return Result()
}

val f1fun: (em: EntityManager) -> Unit = { Unit }
val f2fun: (em: EntityManager) -> Any? = { null }

...但现在我是否

blah { em ->
    // code here    
}

blah { em ->
    // code here
    listOf("1", "2")
}
总是执行

f2&#39; lambda。

有没有办法让blah { with or without statement }允许你收集该值为Any?如果一个语句确实在lambda中并且没有语句存在时,将其视为{{ {1}} lambda?

此: Unit

在某些情况下无法编译:

inline fun blah2(f: (em: EM) -> Any?): Result {

尽管这很好用:

blah2 {
   k = k + 1
}

这很好用:

blah2 {

}

更新:在此期间记录了错误:https://youtrack.jetbrains.com/issue/KT-20215

2 个答案:

答案 0 :(得分:3)

这适用于Kotlin:

inline fun blah2(f: (em: EM) -> Any?): Result {
    val res = f(EM())
    when (res) {
        is List<*> -> res.forEach { 
            // do something with item
            println("Item: " + it)               
        }
        is Unit -> {
            // do something else
        }
    }
    return Result()
}

fun tryit() {
    var res = blah2 {  }
    res = blah2 { arrayListOf<String>("A","b","c") }
}

但是如果你试图从Java代码中调用你的Kotlin blah2,你可能会遇到问题。此链接(Unit closures required to return Kotlin.Unit)描述了将函数从Java返回到Kotlin方法的函数的问题。我没有跟着看它是否已经解决了。

修改 这些也应该有效:

    var k: Int = 0
    res = blah2 { k++ }
    //res = blah2 { k = k + 1 }  // does not compile
    //res = blah2 { k = k + 1; return@blah2 } // does not compile
    res = blah2 { k = k + 1; return@blah2 k }

但这两条注释的行不起作用。 OP已就此编写了一份错误报告(我同意这是一个错误)。但是,请记住,如果它确实有效,那么行为将与Java所期望的行为不同。在Java中,k = k + 1是一个表达式,但在Kotlin中却不是。因此,此表达式的返回类型不是Int。如果有效,则返回类型为Nothing(请参阅Return and Jumps)。如果SO要从Java移植到Kotlin,他们需要注意&#34;赋值作为表达式&#34;。 Here is an interesting discussion on this matter.

<强>更新 以下是不一致的另一个例子:

class TestLambdaReturnType {
    var k = 1;
    var a: () -> Any = {} //explicitly typed (accepts: () -> Unit )
    val b = { k=2 }       //compiler implicitly types b: () -> Unit
    val c = {}            //compiler implicitly types c: () -> Unit
    val d: () -> Any = {k=2}  //compiler error (??: lambda returns Unit, Unit is derived from Any)
    val e: () -> Unit = { k=2 } //no compiler error
    val f: () -> Any = e // no compiler error: ?? inconsistent

    fun blah2(v: () -> Any): Any = v()

    companion object{
        @JvmStatic
        fun main(args: Array<String>) {
            val tn = TestLambdaReturnType()
            tn.a = tn.b

            tn.blah2(tn.a) //ni compiler error, no surprise
            tn.blah2(tn.b) //no compiler error, no surprise
            tn.blah2(tn.c) //no compiler error, no surprise
            tn.blah2 { tn.k=2 }  //error: why "f" different than "v"?
        }
    }
}

lambda必须具有返回类型。在这些示例中,返回类型是Unit。但基于这些示例,似乎有两种类型Unit,一种源自Any,另一种源自who-knows-what

答案 1 :(得分:3)

k = k + 1是一个赋值,它不是Kotlin中的表达式,因此不会评估为Unit或任何其他类型。

  

为什么在没有类型的作业时会考虑到   编译器正在寻找表达式?

考虑以下简短程序:

fun main(args: Array<String>) {
    val result = num { a ->
        var b = a * 5
        // ...put whatever code you want here
        // ...and here
        b++ // <- the last line matters
    }
}

fun num(f: (i: Int) -> Int): Int {
    return f(5)
}

该lambda体内的最后一行,这是重要的行,它必须是一个表达式(在这种情况下为Int),因为它是这个lambda返回的内容。

那么,最后一行可以b = b + 1吗?不,因为它不是表达式,因此不能这样返回。

即使您使用AnyUnit作为返回类型也不会改变b = b + 1不属于这些类型的事实。

  

我必须做的就是这样做:blah2 {k = k + 1;   单位},否则我的lambda不起作用

是的,因为Unit是有效的表达式,blah2 { k = k + 1; Unit }

相同
blah2 { 
   k = k + 1 
   Unit // again, last line matters
}