Kau andrey breslav cover

Kotlin 的现状和未来

Andrey Breslav,Kotlin 项目负责人,今天他以一个工具开发者和工具生态系统的角度来给我们介绍 Kotlin 的概况。然后他会讲讲未来和他们下个版本的一些信息。


介绍 (00:00)

我的名字叫 Andrey,我在 JetBrains 带领着 Kotlin 团队。今天我来和大家聊聊我们现在的状态和未来的计划。

学习材料 (00:30)

这篇演讲不是 Kotlin 的入门介绍。如果你想学习 Kotlin,到处都能找到学习的资源。这里有一个 语言参考,教程,演讲视频。试试我们的 mini-IDE,Kotlin Koans 是一组你上手这个语言的相关问题,类似于一个嵌入教程的 IDE。 我在这里鼓励你们学习!

现状 (01:03)

Kotlin 1.0 在 2016 年二月份发布了。那之后我们做了许多更新:1.0.2 是我们现在的版本,我们现在正在开发 1.1 (一个功能驱动的版本,虽然我不能给你们一个确定的发布日期)。项目的早期试用 (EAP) 会在今年夏天开始。

在本次的讲稿中 (参见视频) 我们有已经使用 Kotlin 的人了 (也许没有公开承认!)。 JetBrains 有很多的用户,我们可能是第一个试着产品化的先锋者。我们有超过 500 万行的代码,而且在不同项目里面产品化,从我们主要的 IDE IntelliJ 产品到服务器端的产品 (……到每一个产品)。一些项目从开始的时候就是用的 Kotlin。其他的也跟上了。我们非常感谢每一个在它们的产品中使用 Kotlin 的人。我们还处在发布的很早期!加入我们吧,你会发现这是一个好公司的。

而且作为一个好公司,你不会孤独。你会有一个活跃的社区,Slack,论坛,StackOverflow,所有的人都非常愿意指出你做的不对的地方,和帮助你修改你的问题。说到我们的 GitHub 项目,它是开源的,你可以加入 (我们已经有了超过 100 OSS 贡献者!)。如果你碰到问题,我们会尽最大努力工作来服务你。如果你有关键的问题呢,我们会以一个 EAP build 的方式来发布,这样你可以在它上面工作来解决问题。

KEEP (03:14)

Kotlin 演进和增强流程 (KEEP): 我们试着积累我们有的所有的设计流程,而且最大程度开放。如果你去看 GitHub repo 你会看到现在我们给 Kotlin 1.1 准备的一些方案。你可以提供你的点子,反馈,做些修改。我们非常感谢你们:用户用例是我们设计的动力。KEEP 是我们计划的地方。

我们的计划 - 开发路线 (03:56)

现在我们有 1.0, 1.0.2,作为主要的版本,我们的开发分成两条线。我们有增量的更新,1.0.2, 1.0.3,它们是兼容的 (意味着语言的变化没有 bug 修正多)。我们修正编译器里面的 bug;这会少量修改语法 (但是这都是为了更好)。这里你会发现缺陷修正,性能提升,和工具功能。这里是 IDE 演进的地方:编译系统。

Receive news and updates from Realm straight to your inbox

1.1 是另外一条线,这里我们实现语法功能,它们的兼容性是向后兼容。1.0 也许不会兼容 1.1 (因为有些功能 1.0 是不知道的)。向后兼容是可以的。如果你有些老的代码,它们会和 1.1 一起工作正常。

这个幻灯片不意味着二进制的兼容性,因为那不用说。

工具计划:1.0.X (05:18)

  • 我们发布了 1.0.2,它支持 Gradle 的增量编译 (这会使得事情加快一些)。现在,当你改变一些代码而且调用它们,然后用 Gradle 来编译的时候,使能增量编译标志 (这是个实验性的标志,默认没有使能),你只会有哪些真的需要编译的代码重新参加编译。整个项目没有重新构建,只有那些改动的个别文件或者和改动相关的文件才会编译。而且你会用上这个功能的;一段时间以后它不再是实验性的了 (希望是!)。这一个重大的结构性的改变。

  • IDE 里集成 Spring 支持。你会有一些像 IntelliJ idea 为 Java 实现的功能,但是不在 caudlen 里。

  • 减少我们标准库的大小。那不是标准库因为我们的标准库太大了 (这可能是可选择的 jvm 语言里面最小的了)。最近一次,我们从二进制库中去掉了 1500 个方法,而不改变 API。这是个兼容的改动,但是现在方法少些了。如果你是安卓开发者,这对你很重要。

  • 安卓 Lint 检查 (人们需要这个)。 IDE 会警告你,如果你做了一些安卓标准条款规定不允许的事情的话。(不是语言特定的标准)。我们会有越来越多的 Lint 检查。

  • Jack&Jill,我们正在集成安卓最新的工具。我们修改了一些缺陷能让 Jill 编译 Kotlin 核心库 (因为,没有这些修正,它编译不过)。

新的目标 (07:49)

关于 1.1…… 我们计划了许多的事情!

  1. Java 8/9。我们当前的目标是 Java 6:你在 Kotlin 编译的任何东西都能在 Java 6 上运行,包括 Java 8/9 (但是它不能用 8/9 的功能)。我们在实现 Java 8 的自动生成默认方法的功能。在 Kotlin 里,你可以实现方法和接口,但是如果你想扩展一个 Java 里的接口,Java 6 是不知道那些方法是实现了的 (Java 8 可以)。我们的 Java 8 里的 Stream API collection 也有一些问题。我们有一些 Java 8 streams 的支持库,但是它们在 Kotlin 1.1 里面被移除了,所以我们只支持本地编译器了。

  2. Kotlin 的 JavaScript 后台已经有些年头了,但是我们降低了它的优先级,为了使得 1.0 能够尽快发布。我们现在又重启了这项工作,而且所有的语言功能都会被覆盖。我们工作在实时模块的支持上 (amd, umd, common.js)。JavaScript 有许多公共的工具链 (npm, browserify, gulp, ……),而且我们会尽量支持我们能做到的最多的工具链。

  3. 另外一个流行的问题,我能够在一个没有虚拟机的本地环境上运行 Kotlin 吗?现在的回答是不行;至少不容易。这部分没有任何代码,而且也不会在 1.1 里面支持,但是我们正在研究这个问题,也许不久就会有相关的消息出来。

Scripting: example.kts (10:08)

现代语言的一个必要条件就是需要成为一个脚本语言。或多或少支持一些,虽然它没有正式地提出来。你可以在 Kotlin 里面写些脚本,文件的扩展名是 .kts。从现在开始,你可以在文件头导入任何东西而且可以写任何的陈述和表达。它会被编译成 Java 类,然后运行正常。

然而,你不能在一个脚本里面定义依赖,这意味着你要么配置自己的 class path,或者使用 jtk 和 Kotlin 的标准库。这是我们正在工作的地方,而且你可能帮助许多其他的 Kotlin 的用法。这是一个结构性的改变。

Type Aliases (11:04)

当你打算传递一个函数 (比如,一个 foo 和 bar to buzz),,你最终会重复好几遍这个函数的签名。这是为什么人们想用一些缩写的机制,给这种复杂类型一个名字。

typealias Int32 = Int
typealias Predicate<T> = (T) -> Boolean

这里是我的给 Boolean 的一个复杂类型 T,一个函数,而且我打算这么叫它。我说 typealias Predicate<T> = (T) -> Boolean。这不是一个新的类型,它只是一个缩写;一个简称。Int32 这里就是一个 Int。它们是通用的,但是它们会帮助你缩写你的长类型,然后使你的 API 更易读。新类型也是人们想要的东西。它不是一个类型缩写。它可能会在版本 1.1 里面支持,通过值类型 (或者相关的其他东西)。你可能听说过 Valhalla 项目 (那些对 Java 感兴趣的人),这更容易。

绑定方法引用 (12:35)

kotlin 语言支持反射(假设其运行平台支持反射)。

val p: Predicate<String> = foo::equals
    // behaves as { x -> “foo”.equals(x) }

val c: KClass<Foo> = x.foo()::class

这两个冒号 (type ::foo) 是我们支持的。你可以使用一个类型,然后引用它的一个成员,一个函数或者一个属性。或者,你可以使用一个类型然后让它成为一个类,通过这样 Type::class。这是现在你能做的一些事情,但是在 1.1 里面,你可以对一个对象做同样的事情了。他能使用一个 foo (“foo” 明显是一个串),然后它可以和另外一个串 相等,这可以是一个单一参数的函数,然后让它的变量是可变的。这会使你能够拿传入的任何串和 foo 比较。这是一个部分应用,但是很有限的例子。根据我们的用户调查,这很方便。

或多或少都工作,就像 lamda 一样。你将可以对一个类做同样的事情;你可以有一个类 X 成为 foo 通过声明 ::class。

属性增强 (13:54)

如果你不知道代理属性是什么东西…… 谷歌它,因为它很酷。 (Ed. 注意:这里!) 这是允许我们更好的重用代码的方法,Kotlin。好消息:在 1.1 里面我们允许本地变量也有这个属性。现在你可以在最顶层或者一个类里面使用 delegated properties;你也可以在一个函数里面使用它。这使能了 DSLs,而且它能在一些新的情况下帮助我们。

fun foo() {
    val lazyBar by lazy { ... }
    while (...) {
        if (...) {
            lazyBar.doBaz()
    ...
}

内嵌属性:一些库的技术;二进制兼容性,还是内嵌属性更好。

val foo: Foo
    inline get() = ...
    inline set(v) { ... }

数据类的继承 (14:53)

(如果你没有听过数据类,学习下它们)。 (Ed. 注意: 这里!)

data class User(val name: String, val age: Int)

// automatically gets
equals() / hashCode() / toString()
copy() // val newUser = someUser.copy(name = “Jane Doe”)
componentN() // val (name, age) = someUser

Kotlin 支持这个简单但是有用的功能,这里你可以定义一个有两个成员的类。它是 类的使用者,它有一个名字和一个年纪 (这些是字段)。它可以被标识成数据,而且编译器可以为你生成所有的事情。它会在这些值得基础上给你一个 equals() 和 hashCode(),toString(),然后渲染它们。一个 copy() 浅拷贝函数来改变一个对象的名字。和一个 componentN() 函数用来析构。可以这么说:我有一个用户 (一个用户是名字和年龄的值对),我声明了一个本地变量,同时有名字和年龄然后分配给用户。我同时有两个值赋给它们两个。

数据类很棒,但是我们不支持它们的继承。1.1 会支持这个;你可以说,有一个密封的类,而且能够从它开始扩展数据类。

sealed class C() {
    data class Example(...) : C()
}

这包含了人们在 Kotlin 里需要的代数数据类型。这还不是 Haskell (它永远不会是),但是它和 Haskell 和类似,和我们看到的要求的用户用例一样。

Lambdas 里的解构 (16:22)

myMap.forEach {
    (k, v) ->
    println(“$k => $v)
}

一个从 N 到 串的 map 是一个值对的序列,对应着所有的键和值。在这个库里,Map 有一个 .forEach 的方法。这简单地遍历了所有的值对,然后你可以做所有你想做的事情。在 Kotlin 1.0 里,一个完整的值对只能有一个变量。你可以说,forEach 有一个条目,而且可以说 (k, v) -> println(“$k => $v”)。这个 1.1 的新语法会帮你正确地解构。如果你说 K 和 V 是一对 (这意味着整个条目会构建这两个值),然后你可以打印这两个值。 现在我已经丢掉了我的听众了吗?

我有一个黑色的屏幕 (看视频)。你不需要读那些代码。你需要关心的只有括号内的缩进。这是我在谷歌搜索 ‘callback hell’ 的图片。这张图片是一个典型的 JavaScript 的片段 (不仅仅是 JavaScript,其他语言也会有这个痛点)。这说明异步计算的世界多多少少今天在许多语言中都存在。

我们想尽可能的变得异步,因为我们喜欢把不同的事情交给不同的线程或者其他不同的执行方式去完成。我想读取一个文件的方式是:”这是我的文件,我想读取它。当读完的时候,这是我的回调函数,结束的时候调用它”。这太棒了;我可以调用一些事情,我不会被阻塞。我可以继续工作;读操作会完成的。但是,如果在读完之后我想写数据,然后另外一个读操作发生了,每一个下步异步调用会被嵌入到刚才的回调函数中。这样就会在回调里面有一个回调又有一个回调……然后最后我有这些缩进。如果 115 行能完成大概还行,但是有时候就不是这样了。这是为什么有的语言有 async/await。

异步计算 (19:10)

一些语言想写出同样的异步调用,不阻塞在调用上,但是是用序列的方式。

fun loadImage(url: URL) = async {
    val bytes = await(loadBytes(url))
    bytesToImage(bytes)
}

我只说了 loadBytes 然后 bytesToImage,意味着 loadBytes 应该异步工作而且只在结束的时候才会执行 bytesToimage。这里这个 await 关键字,意味着调用 load bytes,把其他的计算放在别处,不阻塞当前的线程,让线程能为别人做些事情。而且,当 loadBytes 结束的时候,复活所有的计算然后执行下一行。

这是 C# 在版本五里引入的。其他的语言也采纳了这个点子因为它卓有成效,而且这也是我们想涉及的东西。但是在我们看来,我们不想抄袭别人因为它不够通用。他们有一个异步计算,一个同步块和一个悬挂点。 Await 意味着我们可以在这里暂停然后再恢复。

协同 (20:24)

另外我们想用协同。这是通用的。我们想在其他语言的生产块中覆盖 async/await/yield,但是保持最大的灵活性。我们想最终支持已有的所有异步 API,futures,回调,promises。而且这意味着语言不会和给定的任务框架绑定。

fun loadImage(url: URL) = async {
    val bytes = await(loadBytes(url))
    bytesToImage(bytes)
}

我们有一些 asyncawait:这些是库的功能,不是关键字。这是不同的 API 确有着同样的功能,但是更灵活。原型正在开发 (也许六月能完成),我们会采用抽象来构建这些库和使用这些库。

结论 (21:25)

我想邀请你们来到 KEEP,审核这些提案。提供你们的反馈,想法和用例。

Kotlin 以漂亮的 APIs 而闻名,而且有些人喜欢设计 APIs,我非常有荣幸能请到 Hans Docter 来到这里,因为他 有些东西想给你们看.

About the content

This content has been published here with the express permission of the author.

Andrey Breslav

Andrey Breslav, the lead language designer for Kotlin, began his career at Borland, where he worked on language implementations for MDA support. After spending a few years as a college teacher, he joined JetBrains in 2010 to lead Project Kotlin and currently serves as a member of the Java Community Process Expert Group for JSR 335, “Project Lambda.” He is a frequent conference speaker at venues such as OSCON, JavaOne, Strange Loop, and Devoxx.

4 design patterns for a RESTless mobile integration »

close