虽然提供问题的代码相当简单,但问题集中在类型安全的更一般方面:
让我们有一个像这样的lambda函数:
{it: (Any) -> Any -> it(it)}
它需要另一个lambda并将其自身作为参数执行。所以,让我们做一些显而易见的事情并将其作为参数调用:
{it: (Any) -> Any -> it(it)}.apply { this.invoke(this) }
但是这并不像我想的那样好:我在编译时遇到以下错误:
类型不匹配:推断类型为((Any) - > Any) - >除了(任何)之外的任何 - >任何预期的
确定。所以,让我们试试这个:
val lambda: (Any) -> Any = { Unit }
这个属性的实际值并不重要,我对结果不感兴趣,只是在编译器行为中。所以这是另一个属性:
val kappa: (Any) -> Any = lambda
好的,现在这实际上是编译的。但是和以前一样吗?我将(Any) -> Any
函数传递给属性(在另一种情况下,它是一个参数),它需要一个(Any) -> Any
函数。逻辑告诉我:是的,(Any) -> Any
的类型为Any
,因为一切都是。但为什么不能使用lambda调用呢?实际上,我可以显式地将我的lambda转换为(Any) -> Any
函数,这将导致未经检查的强制转换,但它将按预期编译并执行StackOverflowError。
{it: (Any) -> Any -> it(it)}.apply { this.invoke(this as (Any) -> Any) }
区别在哪里?
答案 0 :(得分:0)
好吧,就像我在评论中所说的那样,这个问题提出的方式存在很多无关紧要的混淆。让我们从清理其中一些开始:
val fn1 = {x1: (Any) -> Any -> x1(x1)}
fn1
的类型为((Any) -> Any) -> Any
。 x1
的类型为(Any) -> Any
。上面的定义编译。 Kotlin编译器可以毫不费力地看到x1
是Any
。
但是,尝试调用fn1
本身并不起作用。 ...并且编译器确切地说明了原因:fn1
是一个((Any) -> Any) -> Any
的函数,不能用作(Any) -> Any
。为什么不起作用?好吧,因为有人可能会调用这个论点!
为了更容易讨论,让我们创建第二个函数,与第一个函数非常相似:
val fn2 = {x2: (Any) -> Any -> x2("foo")}
它也编译,就好了。现在整个问题归结为:
为什么这不起作用:fn2(fn1)
但在这一点上应该是显而易见的。如果该调用有效(无论涉及多少apply
或invoke
欺骗),调用x2
(在fn2
内)的尝试都无效,因为{{1 }}是x2
的别名。它是fn1
调用fn1
参数,它需要一个函数。
tl; dr:函数在其参数类型中是逆变的