V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
twoyuan
V2EX  ›  Kotlin

Kotlin 编译器如何判断在嵌套 Lambda 中应该取哪个默认参数 it?

  •  
  •   twoyuan · 2017-11-24 17:55:52 +08:00 · 8529 次点击
    这是一个创建于 2555 天前的主题,其中的信息可能已经有所发展或是发生改变。

    code

    例如上述代码中,第一个 it 去了 async 的默认参数,而 第二个 it 使用了 forEach 的默认参数

    14 条回复    2017-11-29 14:09:44 +08:00
    sorra
        1
    sorra  
       2017-11-24 18:19:26 +08:00
    当然是取最近的。
    你这个奇怪,可能是根据用法自动匹配了不同类型的 it,但这不合理,应该报编译错误才是编译器的合理设计。
    对开发者来说,这里要给参数命名,不应该使用 it。
    jinyang656
        2
    jinyang656  
       2017-11-24 19:06:06 +08:00
    这编译器不报错咩,神奇
    DeweyReed
        3
    DeweyReed  
       2017-11-24 19:26:43 +08:00
    一般为了可读性都会重命名
    list.forEach{ item -> ... }
    twoyuan
        4
    twoyuan  
    OP
       2017-11-24 21:28:08 +08:00
    @sorra #1 确实是应该有 warning 才合理一些,第一个 it 匹配到了带有 invoke 的 async 默认参数可以理解,第二个在两个 it 都有 toString 的情况下却匹配了正确的,让人觉得可怕……
    sagaxu
        5
    sagaxu  
       2017-11-24 21:39:25 +08:00
    sagaxu
        6
    sagaxu  
       2017-11-24 21:40:20 +08:00
    Expression 'it' of type 'Int' cannot be invoked as a function. The function 'invoke()' is not found
    SoloCompany
        7
    SoloCompany  
       2017-11-24 22:28:38 +08:00 via iPhone
    async block 哪来的输入参数,明明是 ()->?
    twoyuan
        8
    twoyuan  
    OP
       2017-11-24 23:13:23 +08:00
    @sagaxu #6
    @SoloCompany #7

    抱歉,async 是我自己随便封装的一个函数,忘了协程好像也有这个 😂
    sorra
        9
    sorra  
       2017-11-25 10:37:45 +08:00
    属实,测试代码如下,可怕。谁去给官方报个 issue 啊
    ```

    fun sync(f: ((() -> Unit) -> Unit) -> Unit) {
    f {
    it()
    }
    }

    fun main(args: Array<String>) {
    sync {
    it {}
    (1..10).forEach {
    it {
    println(it)
    }
    }
    }
    }

    ```
    SoloCompany
        10
    SoloCompany  
       2017-11-25 19:49:43 +08:00
    @twoyuan 很正常, type inference 有时候因为过于智能会显得比较魔幻

    执行一下
    fun main(args: Array<String>) = run { val run = 1; run { print("run: $run") } }

    或者甚至 (后面这个会有个编译警告, 但警告的也仅仅是变量重名而不是 type inference 混淆)
    fun main(args: Array<String>) {
    val x: (() -> Any) -> Any = ::run;
    x { val x = 1; x { print("x: $x") } }
    }
    sorra
        11
    sorra  
       2017-11-26 11:37:02 +08:00
    @jinyang656 @SoloCompany @sagaxu
    https://discuss.kotlinlang.org/t/will-it-variable-stay-or-go/522/8
    2014 年有讨论,但直到 1.0 发布都没结论,于是为了兼容性而搁置了问题。
    大概只能用 Lint 工具来解决了。
    SoloCompany
        12
    SoloCompany  
       2017-11-26 15:18:09 +08:00 via iPhone
    @sorra 这根本是两个问题好吗,请看我给出的例子,这个问题和 it 变量没有关系,只是类型推断系统过于智能是否合适的问题而已
    sorra
        13
    sorra  
       2017-11-26 16:30:59 +08:00
    帖子里讨论了嵌套 Lambda 的问题,对于重定义的 it 不报任何警告是危险的。显式的参数没那么危险。
    Java 的做法是禁止 Lambda 重定义局部变量。用匿名内部类就没有这个限制。

    题外话,Java 只允许 final 变量提升到闭包作用域,而 Scala/Kotlin 允许 var。

    这方面个人认为还是 Java 更严谨。
    SoloCompany
        14
    SoloCompany  
       2017-11-29 14:09:44 +08:00
    @sorra #13 重名的问题的确 error 比 warning 更合适, 不过有 lint 工具的话这个也不是什么大问题; 反而 mutable 变量不能在闭包中使用这一点我绝对认为 kotlin 的做法(当然只是 sugar) 更合理, 在没有更好选择的前提下为此写一个 wrapper 的做法简直就像破补丁一样太不优雅了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2767 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 09:40 · PVG 17:40 · LAX 01:40 · JFK 04:40
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.