【问题标题】:Kotlin Any with lambdas带有 lambda 的 Kotlin Any
【发布时间】:2017-09-11 18:17:32
【问题描述】:

虽然问题提供的代码相当琐碎,但问题集中在类型安全的更一般方面:

让我们有一个像这样的 lambda 函数:

{it: (Any) -> Any -> it(it)}

它接受另一个 lambda 并以自身作为参数执行它。所以让我们做显而易见的事情并将其作为参数调用:

{it: (Any) -> Any -> it(it)}.apply { this.invoke(this) }

但这并不像我想的那样好用:我在编译时收到以下错误:

类型不匹配:推断类型为 ((Any) -> Any) -> Any but (Any) -> Any 是预期的

好的。所以让我们试试这个:

val lambda: (Any) -> Any = { Unit }

这个属性的实际值无关紧要,我对结果不感兴趣,只对编译器行为感兴趣。所以这是另一个属性:

 val kappa: (Any) -> Any = lambda

好的,现在这实际上编译了。但这不是和以前一样吗?我将一个 (Any) -> Any 函数传递给一个属性(在另一种情况下它是一个参数),它需要一个 (Any) -> Any 函数。逻辑告诉我:是的,(Any) -> AnyAny 类型,因为一切都是。但是为什么这不适用于 lambda 调用呢?事实上,我可以将我的 lambda 显式转换为 (Any) -> Any 函数,这将导致未经检查的转换,但它会按预期编译和执行导致 StackOverflowError。

{it: (Any) -> Any -> it(it)}.apply { this.invoke(this as (Any) -> Any) }

区别在哪里?

【问题讨论】:

  • 我认为您的第一个假设是错误的。在定义中:{it: (Any) -> Any -> it(it)},所有的it都是同一个东西。
  • 是的,为什么不呢?它是一个将任何内容作为输入的函数,它应该包括另一个(或相同)将任何内容作为输入的函数。
  • 是的,你找到我了。 it 的完全混淆使用完全成功地让我感到困惑。将(Any) -> Any -> Any 转换为(Any) -> Any 是未经检查的转换。您的第二个示例没有未经检查的演员表。
  • FWIW,kappas mutator 的签名是set(f: (Any) -> Any)
  • 好的,虽然我明白为什么使用 mutator 是类型安全的,但我仍然不明白,为什么 ((Any) -> Any) ->Any 的强制转换未选中。根据我的理解,我应该能够将函数作为期望 Any 的参数传递,因为 Any 应该包含一个函数。

标签: lambda kotlin


【解决方案1】:

好的,正如我在 cmets 中所说,提出这个问题的方式存在很多不相关的混淆。让我们先清理一些:

val fn1 = {x1: (Any) -> Any -> x1(x1)}

fn1 的类型是((Any) -> Any) -> Anyx1 的类型是 (Any) -> Any。上面的定义编译。 Kotlin 编译器可以毫不费力地看到 x1Any

但是,尝试在其自身上调用fn1 是行不通的。 ...并且编译器确切地说明了原因:fn1 是一个函数,它是 ((Any) -> Any) -> Any,不能用作 (Any) -> Any。为什么那行不通?好吧,因为有人可能会调用参数!

为了便于讨论,让我们创建第二个函数,与第一个非常相似:

val fn2 = {x2: (Any) -> Any -> x2("foo")}

它也可以编译,很好。现在整个问题归结为:

为什么这不起作用:fn2(fn1)

但现在应该很明显了。如果该调用有效(无论涉及多少applyinvoke 诡计)尝试调用x2(在fn2 内)将无法工作,因为x2fn1 的别名。这是对 fn1 的调用,参数为 "foo",它需要一个函数。

tl;dr:函数的参数类型是逆变的

【讨论】:

  • 天哪,我怎么看不到这一点……更别提逆变了。谢谢,我现在看到了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多