任何之间的联盟类型? 和Unit

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

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的lambda总是被执行。

有没有一种方法来实现一个blah { with or without statement } ,将允许您收集Any?价值Any? 如果一个语句确实在lambda内部,并且没有语句存在,那么就像一个Unit lambda?

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

在某些情况下不会编译:

 blah2 { k = k + 1 } 

即使这工作正常:

 blah2 { } 

这工作正常:

 blah2 { k++ } 

更新 :同时记录了一个错误: https : //youtrack.jetbrains.com/issue/KT-20215

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; 单位},否则我的拉姆达没有工作

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

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

这应该适用于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 ,你可能会遇到问题。 这个链接( 返回Kotlin.Unit所需的单元闭包 )描述了一个传递一个从Java返回void到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 (请参阅返回和跳转 )。 如果是从Java移植到Kotlin,他们需要注意“作为表达的任务”。 这是关于这个问题的一个有趣的讨论。

更新这里是不一致的另一个例子:

 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