将 Integer 转换为 Unit 编译成功

Omk*_*kar 3 generics kotlin kotlin-extension

为什么该程序在运行时应该因 ClassCastException 失败而打印 kotlin.Unit?

class Animal<T> {
  
}

fun <T> Animal<T>.extension(block: ()-> T){
    print(block())
}

fun main(){
    //(4 as Unit) //Runtime ClassCastException, OK
    //Animal<String>().extension { 2+2 } //Compilation error, ok
    Animal<Unit>().extension { 2+2 } // Why no ClassCastException but prints kotlin.Unit?
}
Run Code Online (Sandbox Code Playgroud)

如果这不是错误,是否可以强制执行约束?

Ten*_*r04 9

在解释 lambda 表达式时,Kotlin 将返回 Unit 的函数视为特殊情况。如果它期望 lambda 返回 Unit,则无论 lambda 的最后一行计算结果如何,它都会隐式返回 Unit。否则,lambda 经常必须在末尾有一个无用的行来返回Unit。因此,由于它解释 lambda 的方式,这里没有发生强制转换。在 lambda 定义的函数中有一个隐式的额外行返回 Unit。

假设您正在调用一个带有回调参数的函数fun foo(onComplete: (String)->Unit),并且您想要将返回值添加到 Set 中:

foo {
    someMutableSet.add(it)
}
Run Code Online (Sandbox Code Playgroud)

add函数返回一个我们在这里不关心的布尔值。如果您必须记住像这样标记它,那会很烦人:

foo {
    someMutableSet.add(it)
    Unit
}
Run Code Online (Sandbox Code Playgroud)

这种特殊处理不仅仅适用于 lambda。您也不必return Unit在每个返回 Unit 的传统函数的末尾添加一行。而且您也不必显式设置: Unit函数签名的返回类型。


Sil*_*olo 6

你的Int没有被投射到Unit. 实际上恰恰相反。您的() -> Intlambda 被视为() -> Unitlambda。具体来说,来自 lambda 表达式的文档(重点是我的),

lambda 表达式的完整语法形式如下:

val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
Run Code Online (Sandbox Code Playgroud)

...

  • 如果 lambda 的推断返回类型不是Unit,则 lambda 主体内的最后一个(或可能是单个)表达式将被视为返回值。

所以在你的例子中,

Animal<Unit>().extension { 2+2 }
Run Code Online (Sandbox Code Playgroud)

{ 2+2 }不是返回.Int这是一个 lambda 返回Unit,即没有任何价值。如果 lambda 的返回值能够使类型推断成功,Kotlin 很乐意简单地丢弃它。那是为了允许类似的事情

myButton.addEventListener { event ->
  ...
  someFunctionThatHappensToReturnInt()
}
Run Code Online (Sandbox Code Playgroud)

如果 lambdaInt仅仅因为我碰巧调用了一个返回 的函数(为了产生副作用)而推断出返回类型Int,那将非常烦人,因此 Kotlin 会在必要时删除它。

如果您显式地编写该单词return,您将强制参数的类型为() -> Int您所期望的错误。

Animal<Unit>().extension { return 2+2; }
Run Code Online (Sandbox Code Playgroud)