历时 7 天,我把一万行 Scala 代码移植到了 Kotlin 上!

【CSDN编者按】去年,Google 宣布 Kotlin 正式成为 Android 官方开发语言,由此引发了迁移 Kotlin 的一股热潮。在本文中,作者分享了他在七天内把代码从 Scala 移植到 Kotlin 的经过,以及从中吸取的经验教训。

以下为译文:

上周出了几件事,所以我决定把postgresql-async从Scala移植到Kotlin。虽然现在还有好多缺失的部分,但alpha版已经可以用了在这篇文章中我想分享把代码从Scala移植到Kotlin的经过,以及从中吸取的经验教训,希望可以帮助其他开发者解决同样的问题。而且我也在继续努力,解决剩下的问题。

首先我想解释一下为什么要移植?

在Outbrain我转到了一个新的团队,得到的任务之一就是负责将各种模块从Scala 2.10升级到2.11。这个任务是可行的,但十分痛苦,因为许多包都要求我们必须给所有JVM模块“打补丁”,就连Java模块都要!

由于所有模块都依赖于ob1k-db,而ob1k-db依赖于postgresql-async,后者又依赖于Scala 2.10和2.11下的不同的包。所以,可能更好的做法是干掉所有模块中对Scala的依赖……

而且上周,在经历了一年多的沉默后,终于有一个提交证实了postgres-sql不再提供维护了(https://github.com/mauricio/postgresql-async/commit/5716ac43818b6be0dc4fcc2b2655dde3411cdbe0)。这是压死骆驼的最后一根稻草。

而且,我们仍然在使用该函数库的MySQL异步风格的版本,而且还没有找到能代替它的东西。

但一个优势是Scala和Kotlin十分相似,无论是功能还是语法——所以我们很想试试能不能把代码移植过去。

怎么做?

在阅读下面的技术细节之前请访问下下面的函数库,然后请给加个星 :

https://github.com/jasync-sql/jasync-sql

转换本身包括两个主要步骤:

自动逐行搜索替换脚本内容,节省一些无谓的打字时间;

人工审核代码,修改所有编译错误,决定怎样进行转换,并改进脚本。

脚本

脚本其实是一段非常简单无脑的kscript代码(https://github.com/holgerbrandl/kscript),感觉都没必要贴出来。一些代码行甚至都没有替换成合法的语句(比如模式匹配和类型强制转换的部分)。

话不多说,下面是脚本的简化版本:

这个脚本是用kscript编写的(https://github.com/holgerbrandl/kscript),它接受一个参数:可以是扩展名已经改为.kt的Scala文件,也可以传递目录,如果是目录则该脚本会递归转换目录中的所有文件。

这个脚本会进行一些非常简单的逐行查找替换:def替换成fun,trait替换成interface,等等。没什么特别的东西。因为我前面说过,两者语法很相似,这一点起了很大作用。如果转换成Java则可能会更麻烦。

经验教训,以及我做出的决定

我写这篇文章的目的就是记录下我做过的事情。一些文件仍然需要转换,同时项目中还有其他人,所以这篇文章会有用的。

下面的项目顺序不分先后,以后也可能会更新。

Future CompletableFuture

原来的代码大量使用了Scala的Future,所以我需要找个东西来代替。我有许多选择:

Netty future——似乎语法很复杂,而且已经过时。

JavaRX/Guava/其他future库——需要额外的外部依赖。

Java 8兼容的Future——至少需要依赖Java 8。

Kotlin deferred——主要用于协程(coroutine),所以功能不太多,也不知道与Java用户的兼容性如何,对于我来说有点难度。

最后决定使用CompletableFuture作为主要的后端库。我觉得没必要在Android中使用响应式的relational-sql库,而且Java 8在Android之外的应用也非常广泛。

注意,CompletableFuture替换了Scala的Future和Promise。

依赖

由于这个项目类似于驱动程序,所以我尽量减少外部函数库的依赖,这个决定也影响了其他的决定。

Finalize

貌似在Kotlin中不需要覆盖finalize方法。

数据结构

有些我已经忘了,但我记得的转换有以下这些:

Seq List

IndexedSeq List

ArrayBuffer MutableList

位操作

Kotlin对于byte的处理有点奇怪,还不支持所有的操作符。一些类我转换成了Java,一些仍然保持Kotlin。希望我处理得没错,因为我并不十分确定Scala怎样处理这些操作。欢迎提意见。

扩展方法和属性

我一开始并不太理解,但后来意识到我可以使用扩展(extension)让Kotlin变得跟Scala相似,这一点非常酷。

例如Kotlin的List中有size,而Scala中叫做length。

这些问题都可以用扩展解决。

Try

我决定从Scala+Arrow移植一个相似的类使用。

方法定义和调用中的大括号

Scala并不强制大括号,所以有时转换会很痛苦。

Duration Duration

决定使用java.util.Duration。

执行上下文和隐含参数

我发现这个功能非常混乱,所以我把所有隐含参数都改成了必须。虽然代码会变得冗余,但我觉得这样更清晰。

我使用common pool作为默认的执行上下文,尽管在ob1k中我们使用的是另一个。不管怎样,我们把它也改成了显式传递。

测试

原来的库使用了specs2。一开始我想暂时保留Scala的测试,但似乎这样做也需要很多工作,因为许多内部代码都改变了。测试的移植依然在进行中,主要工作都由贡献者们进行。

Option

大部分都用nullable的类型替换了,其中用到了一些扩展的帮助函数:

https://github.com/jasync-sql/jasync-sql/blob/master/db-async-common/src/main/java/com/github/jasync/sql/db/util/NullableUtils.kt

这里我发现Kotlin的方法更好,因为Scala有时使用Option,有时却直接使用null。

也可以用Java的Optional替换。

Version KotlinVersion

其中有个专门的逻辑,但这个逻辑似乎很标准,所以我就使用KotlinVersion来替换了。

隐含转换

隐含转换是一切的邪恶之源(包括过早优化)。我发现我们的情况中可以很容易地使用扩展方法和Java静态方法来替换隐含转换。比如这里的第25行(https://github.com/mauricio/postgresql-async/blob/master/mysql-async/src/main/scala/com/github/mauricio/async/db/mysql/binary/decoder/BigDecimalDecoder.scala)我们隐含地将ByteBuf转换成了ChannelWrapper,使用的是这里的第25行(https://github.com/mauricio/postgresql-async/blob/master/db-async-common/src/main/scala/com/github/mauricio/async/db/util/ChannelWrapper.scala)定义的方法。在Kotlin中,我们在ByteBuf上使用扩展函数(如这里:https://github.com/jasync-sql/jasync-sql/blob/master/db-async-common/src/main/java/com/github/jasync/sql/db/util/ByteBufExtensions.kt),并将ChannelWrapper变成了静态方法。

Traits interface + 每个类的委托

似乎traits只是多重继承的替代品,因为它们有状态。我成功地用类委托(class delegation,第55行:https://github.com/jasync-sql/jasync-sql/blob/master/mysql-async/src/main/java/com/github/jasync/sql/db/mysql/MySQLConnection.kt)替换了它。缺点是这种实现要求方法抛出异常,所以如果没有被重载,那么运行时有可能会出错。见这里的第51行(https://github.com/jasync-sql/jasync-sql/blob/master/db-async-common/src/main/java/com/github/jasync/sql/db/pool/TimeoutScheduler.kt)。

以上,感谢阅读。欢迎大家指正!

原文:https://hackernoon.com/how-i-ported-10k-lines-of-scala-to-kotlin-in-one-week-c645732d3c1

作者:osha1

译者:弯月,责编:郭芮

微信改版了,

想快速看到CSDN的热乎文章,

赶快把CSDN公众号设为星标吧,

打开公众号,点击“设为星标”就可以啦!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181107A0YFMM00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券