这让我感到惊讶。 在研究了Kotlin语言以了解如何最好地将这种有趣的新语言用于jOOQ之后 ,我偶然发现了这个难题 。 您认为以下程序会打印什么?

fun main(args: Array) {
    (1..5).forEach {
        if (it == 3)
            return
        print(it)
    }

    print("done")
}

好吧……您可能猜错了。 上面将打印:

1 2

它不会打印出大多数人可能期望的内容:

1245done

请注意那些不感到惊讶的人:

上面对于习惯使用Java 8的人来说是特殊的,其中以下代码的确会打印1245done

public static void main(String[] args) {
    IntStream.rangeClosed(1, 5).forEach(it -> {
        if (it == 3)
            return;

        System.out.print(it);
    });

    System.out.print("done");
}

语法原因在Kotlin手册的以下部分中进行了说明: https : //kotlinlang.org/docs/reference/returns.html

在lambda /闭包中, return语句将不会(必需)从lambda /闭包中返回,而是从lambda /闭包的直接封装范围中返回。 JetBrains的Dmitry Jemerov在两则推文中向我提供了基本原理

为Java巧妙的Kotlin语言已经消除基于语言的支持结构,如try-with-resources ,或synchronized语句。 这是非常合理的,因为这些语言构造不一定属于该语言( 正如我们先前在另一篇博客文章中所声称的那样 ),而是可以移至库中。 例如:

// try-with-resources is emulated using an
// extension function "use"
OutputStreamWriter(r.getOutputStream()).use {
    it.write('a')
}

要么:

// Synchronized is a function!
val x = synchronized (lock, { computation() })

毕竟,即使在Java中,该语言功能也只能起作用,因为该语言取决于库类型,例如Iterableforeach ), AutoCloseabletry-with-resources )或JVM功能(在每个引用上监控是否要synchronized

那么,退货有什么关系呢?

按照上述基本原理,当语言设计人员希望避免使用可通过库实现的事物构建语言构造,但仍希望您觉得它们是语言构造时,则在这种“构造”内部return的唯一合理含义-ish” lambda /闭包是从外部范围返回。 因此,当您编写如下内容时:

fun main(args : Array) {
    val lock = Object()
    val x = synchronized(lock, {
        if (1 == 1)
            return

        "1"
    })

    print(x)
}

真正的意图是使它等效于以下Java代码:

public static void main(String[] args) {
    Object lock = new Object();
    String x;

    synchronized (lock) {
        if (1 == 1)
            return;

        x = "1";
    }

    System.out.println(x);
}

显然,在Java情况下, return语句退出main()方法,因为没有其他合理的堆栈框架可从中返回。 与Kotlin不同,在Kotlin中,有人可能会说lambda /闭包将产生自己的堆栈框架。

但这确实不是。 原因是synchronized函数上的inline修饰符:

public inline fun <R> synchronized(lock: Any, block: () -> R): R {
    monitorEnter(lock)
    try {
        return block()
    }
    finally {
        monitorExit(lock)
    }
}

这意味着block作为参数传递闭包是不是一个真正的纯lambda表达式,而是嵌入在调用点的范围只是语法糖。

奇怪的。 狡猾。 聪明。 但是有点出乎意料。

这是一个好主意吗? 还是以后语言设计者会后悔? 所有的lambda /闭包是否都可能是“语言构造式的”,而期望这样的return语句会离开外部范围? 还是有明确的例子表明这种inline行为完全有意义?

走着瞧。 在任何情况下,一种语言选择此路径都是非常有趣的。

翻译自: https://www.javacodegeeks.com/2016/02/peculiar-possibly-cunning-kotlin-language-feature.html

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐