英文原文:Kotlin vs Java The Whole Story
Kotlin
Kotlin 是一门相对比较新的 JVM 语言,JetBrains 自 2011 年以来一直在积极地开发。
多年来,该语言在 Android 社区受到的关注度越来越高,并在 Google IO 2017 大会之后成为 Android 开发领域最热门的话题。这次大会宣布,Android 正式支持 Kotlin。
遗憾的是,虽然已经有许多关于 Kotlin 的文章,但并没有多少客观信息,许多开发人员仍然在苦思冥想,迁移到 Kotlin 是否是一条正确的道路。
在本文的剩余部分,我将尝试提供一个在将 Kotlin 作为 Java 的替代项进行评估时需要考虑的更完善的事项清单。
Kotlin 与 Java 的主观比较
“Kotlin 比 Java 好”,“Kotlin 可读性比 Java 强”,“Kotlin 开发速度比 Java 快”,类似这样的陈述缺少相关准确数据的支持,所以都归为主观看法一类。
主观看法是个体开发人员在对与 Kotlin 或 Java 相关的主题作出一个或多个主观判断时形成。
开发人员的主观判断存在如下问题:
由于没有与主观判断相关联的量化指标,建立在这些判断基础上的观点只是反映出了开发人员之前就有的偏见。不同的开发人员可能有着截然不同的偏见,因此,有开发人员认为 Kotlin 是不错(或糟糕)的 Java 替代者并不意味着其他开发人员也这么认为。
而且,由于没有客观指标,主观分歧就无法客观地消除,这经常会导致“口水战”。
主观判断的谬误
为了说明主观判断可能导致的误解,让我们仔细审视一个非常常见的主观看法:
Kotlin 可读性比 Java 强
——Web 上无数的文章
理论上讲,可以设法设计一个度量 Kotlin 和 Java 之间可读性差异的实验,但据我所知,没有任何人真正地开展这样一个实验。因此,截至目前,这个看法没有任何数据支撑。
Kotlin 的语法是许多开发人员称赞其可读性的一个原因。他们的逻辑如下:
Kotlin 有更好的语法,因此它的可读性更强
——Web 上无数的文章
在这句话中,“更好的语法”又是一个主观判断,本身就值得商榷,但为了避免争论,我们假设 Kotlin 的语法确实更好。但是,这就能说明 Kotlin 的可读性更强吗?
为了观察语法对可读性的影响,请阅读下这段“文本”:
开始的时候,这段“文本”很难理解,但慢慢地,读起来会越来越容易。如果你再读个两三遍,那么你根本就不会再注意它是由非标准的字母组成的。准确地说,字母的替换不是句法变化,但这确实可以说明,对于熟练的读者而言,外观很少会成为可读性的障碍。
我们也可以把这个例子扩展到自然语言。我了解三门截然不同的语言。虽然它们之间差别很大,但我发现,当我不理解文本中使用的单词时,阅读任何一种语言的文本都非常困难。一旦我认识构成文本的单词并熟悉上下文——无论它使用了哪一种语言,我读起来都不困难。
因此,对我而言,语言的选择并不会影响可读性,只要理解内容和上下文就可以了。
编程语言同样如此。
当我们开始使用一门新语言,我们会有一段时间很难理解源代码,需要认真领会每个句法结构。但是,随着我们阅读和编写特定语言的代码越来越多,我们逐渐就熟悉了那门语言的语法,到某个时候,我们就不会再注意句法结构了。
我自己在多门语言上有过这种体验:Verilog、Bash、Perl、Tcl、Lisp、Java。
根据我使用上述语言的经验,我可以告诉你:如果一个人适应了 Lisp 的代码,并且不会再注意到小括号,那么跟 Java 比起来,Kotlin 的语法完全不能对可读性产生不可忽视的影响,即使它“更好”。
既然我们在讨论这个话题,我就分享下自己对于影响源代码可读性因素的主观判断。
在读过其他开发人员使用许多语言编写的代码后(上面只罗列了我在某个阶段精通的语言;我用过的所有语言比这个多),我得出如下结论:如果开发人员使用某一门语言可以编写出可读性和可理解性都很好的代码,那么他们通常也可以使用其他语言编写出可读性和可理解性都很好的代码。
因此,我根据自己的经验作出的主观判断是,源代码的可读性和选择的语言无关,那取决于代码编写者的技能和读者的技能(编写者的技能更重要)。
如果你仍然认为主观看法具有代表性,那么至少阅读并思考下 Robert “Uncle Bob” Martin 在这篇博文中的观点。
Kotlin 与 Java 的客观比较
与主观比较相反,客观比较使用量化指标来度量或评估 Kotlin 比 Java 有优势的地方。
用一套标准客观地证明一门编程语言是否强过另一门,这种想法非常有吸引力,但是有个问题:据我所知,没有与编程语言相关的通用客观指标。
考虑到我们无法进行精确的直接比较,那我们能否客观地比较 Kotlin 和 Java 呢?能!我们仍然能评估从 Java 切换到 Kotlin 所带来的积极和消息影响的程度,然后比较结果,并讨论它们的影响。
为了评估 Kotlin 所能带来的最好结果,我们将做如下假设:
事实上,上述假设没有一个是合理的,但在开始的时候,有一个理想化的设定便于说明。然后,我们会抛开这些假设,讨论真实世界的效应所带来的影响。
Kotlin 最佳结果估计
遵循 Steve McConnell 在 Code Complete 一书中提出的模式,我们可以将软件构建活动分解成三个子活动:详细设计、编码与调试、开发测试。
Kotlin 对于详细设计子活动没什么影响(这项活动通常独立于选用的特定的面向对象编程语言),因此,在这一部分,Kotlin 和 Java 需要付出同样的努力。
据我所知,对于开发测试子活动,Kotlin 也没有提出什么革命性的东西。因此,开发测试需要付出的努力也一样。
就剩编码与调试子活动了。
如果我们用 Kotlin 替换 Java,那么我在编码与调试活动中可以节省多少工作量?这个问题很难回答,不同程序员之间这一数值会有很大差异(有些程序员使用 Java 更高效)。不过,既然我们在评估最好的情况,我们不妨假设从 Java 切换到 Kotlin 可以将开发人员在编码与调试阶段的生产力平均提高 10%。
10% 的生产力提升是一个不现实到令人吃惊的数值。即使我们在文本编辑器中手工输入所有代码,那也是不现实的。考虑到现如今 IDE 的功能,这一数值更是不现实。考虑到有些开发人员使用 Java 更高效,这个数值就毫无道理了。
我不介意使用这样一个既不现实又对 Kotlin 评估有利的数值,因为我知道,不管它对评估结果产生了怎样不切实际的积极影响,一旦我们抛开其中部分“理想的假设”,由此带来的负面影响会抵消掉那些积极影响。
那么,在编码与调试方面提升了 10%——我们把产品交付给客户的速度快了多少?
下面这张图片来自 Code Complete 一书,展示了软件项目的各种活动所占的比例:
图 小项目以构建活动为主。大点的项目需要更多架构、集成和系统测试工作来保证项目成功。这张图没有显示需求,因为和其它活动不一样,需求工作不是直接的程序功能。(Albrecht 1979; Glass 1982; Boehm, Gray, and Seewaldt 1984; Boddie 1987; Card 1987; McGarry, Waligora, and McDermott 1989; Brooks 1995; Jones 1998; Jones 2000; Boehm et al. 2000)Code Complete,第二版。
根据来自 Code Complete 的这张图片,在一个较大的软件项目中(多于 10K 行),编码和调试只占项目总工作量的不足 20%。
因此,在一个较大的软件项目中,我们所假设的编码和调试效率提升 10%,只能将完成项目所需的总工作量缩减2%。
例如,一个需要 5 人年才可以完成的项目(这是相对比较大的 Android 项目),总工作量的2% 为:
5 人-年 * 12 * 4 * 5 * 0.02 = 24(人-天)
如果我们真得能够把项目工作量减少 24 人-天,这会是一个从 Java 切换到 Kotlin 的很好的理由。然而,我们应该还记得,上述积极评估是在理想情况下得出的,其基础是不切实际的假设。
在真实世界里,切换到另外一门编程语言会产生不可避免的影响,我们将评估这种影响,并与上述理想化评估作个比较。
开发人员准备
为了评估最好的情况,我们假设开发人员可以立即从 Java 切换到 Kotlin。
实际上,虽然 Kotlin 和 Java 非常类似,但开发人员仍然需要花一些时间来学习,然后再花一些时间来调整开发实践和工具。准备时间因人而异:有些开发人员可以三四天完成切换,其他人则需要 10 天甚至更多的时间。
让我们乐观一点,平均每个开发人员只要 5 天就可以从 Java 切换到 Kotlin。
一个需要 5 人年才能完成的项目会有 3 到 5 名开发人员(最好的情况下)。平均每个开发人员的切换时间为 5 天,这样,一个项目总计就需要 15 到 25 个人天的切换时间。
切换到 Kotlin 所节省的工作量(乐观估计)与切换所需的总工作量似乎差不多。
开发人员技能损失
我们已经讨论了这项技能的其中一个方面(代码可读性),但还有许多其他方面。当从一门语言切换到另一门时,与旧编程语言相关的部分技能可以运用到新语言上,但该技能的其他部分会损失掉。
为了评估编程语言技能损失对项目工作量的影响,我们将使用源自 Cocomo2 评估模型的“语言与工具体验”因子:
语言与工具经验(LTEX)
该指标用于衡量开发软件系统或子系统的项目团队使用编程语言和软件工具的经验。软件开发包括借助工具完成需求、表现形式设计与分析、配置管理、文档提取、库管理、程序样式与格式化、一致性检查、计划与控制等等。除了项目编程语言经验外,项目支持工具集的经验也会影响开发工作。经验低于 2 个月会获得一个很低的评级,有 6 个月或多年的经验则会获得一个很高的评级,见下表:
Cocomo 2 模型定义手册
例如,假设我们有一个 Java 开发团队,团队成员平均有一年的经验,我们想迁移到 Kotlin。
由于 Kotlin 和 Java 非常像,与许多 Java 工具兼容。我们可以乐观地假设,在经过初步的准备后,开发人员就可以归为有 6 个月开发经验这一类(而不是低于 2 个月)。根据这个假设,为了评估技能损失所导致的额外工作,项目的额定工作总量应该乘以 1.09。
一个需要 5 人年完成的项目,配备了平均具有 1 年 Java 经验的开发人员,切换到 Kotlin 所导致的额外工作达到了令人咂舌的 108 人天。
技能损失所导致的额外工作是切换到 Kotlin 所缩减的工作的四倍。
语言和工具的稳定性和成熟度
有个普遍的说法,就是 Kotlin 是一门生产就绪的语言。这种说法也许是有道理的,因为 Kotlin 已经用在了若干项目里。
不过,与 Java 相比,Kotlin 是一门并不稳定的年轻语言。
有些开发人员认为,Kotlin 的不稳定性是个优势——语言在演进,可以更快地提供新特性,更快地改进。在我看来,他们对于这件事的看法过于简单。
下面是 Kotlin 1.1.4 发布说明里的第一句话(写这篇文章时的最新版本):
修复 IntelliJ IDEA 的一项重大性能衰退
——Kotlin 1.1.4 发布说明
我不知道这是什么样的衰退,有多少项目受到了影响,但我的大脑自动将“重大性能衰退”这个搭配翻译成了“浪费了许多小时的开发时间。”
此外,如果你读一遍发布说明的评论,你就会注意到,许多人遇到了迁移问题。在 1.1.2 版本的评论里,甚至有人指出,这个“补丁”发布引入了破坏性(向后不兼容)的修改。
相比之下,如果你读一遍 Oracle JDK8 的发布说明,你就会发现,它比较稳定。大多数修改都是安全改进方面的。
因此,与 Java 相比,Kotlin 是一门不稳定且不成熟的语言——迁移到 Kotlin 会对项目产生怎样的影响?为了回答这个问题,我将使用来自 Cocomo 2 评估模型的“平台波动性”工作因子:
平台波动性(PVOL)
这里使用“平台”一词指代软件产品执行任务时调用的复杂硬件和软件(OS、DBMS 等)。如果开发的软件是一个操作系统,那么平台就是计算机硬件。如果开发的是数据库管理系统,那么平台就是硬件和操作系统。如果开发的是网络文本浏览器,那么平台就是网络、计算机硬件、操作系统和分布式信息库。平台包括支撑软件系统开发所需的编译器或装配器。如下表所示,如果平台每 12 个月才有一次重大变更,则评级就会很低,如果每 2 周有一次重大变更,则评级就会很高:
Cocomo 2 模型定义手册
你可能已经注意到,编程语言并没有直接出现在该工作因子的描述里,但出现了编译器和装配器。在我看来,这段描述没有显式包含编程语言,是因为得出 Cocomo 2 模型的所有项目都使用了稳定的语言。
由于编译器和装配器属于这个工作因子,所以我们也可以推断出编程语言及相关工具。
根据平台波动性的这种评级范围,Java 的评级应该是“very low”,而 Kotlin 的评级应该是“low”或更高。Kotlin 的评级可能会更高,因为它内部依赖于其它工具,增加了出现兼容性问题的风险。
由于“very low”没有提供工作因子,所以我们需要估计。
看下该因子从“very high”到“low”的评分递减规律,我认为,我们可以放心的假设,“very low”的评分不高于 0.82。
基于这些假设(有利于 Kotlin),如果一个项目需要 5 人年的额定工作量,那么使用 Kotlin,工作量就变成了 1044 人天,而使用 Java 的总工作量是 984 人天。
选择使用 Kotlin 而不是 Java 实现这样一个项目会使总工作量增加 60 人天。
语言和工具不稳定所导致的额外工作是切换到 Kotlin 所缩减的工作的 2 倍多。
综合所有因素
我当成例子来讨论的项目需要 5 人年的额定工作量。
根据上述评估,如果该项目由平均具备 1 年 Java 开发经验的开发人员使用 Java 实现,则总工作量为:
5 人-年 * LTEX (Java) * PVOL (Java) = 984 (人-天)
如果同样的项目由几乎没有 Kotlin 开发经验的开发人员使用 Kotlin 实现,则总工作量为:
5 人-年 * LTEX (Kotlin) * PVOL (Kotlin) * 0.98 + T_ramp_up = 1115 + 5 * N_developers (人-天)
据估计,选择 Kotlin 替换 Java 所导致的额外工作量为131 + 5 * N_developers (人-天)
。
评估注意事项
在评估讨论的过程中,我们得出了与 Kotlin 和 Java 相关的、便利的工作量单点值。
但实际上,单点值根本不是估计——它们只是猜测。真正的估计必须有一个相关联的不确定性。换句话说,估计表示可能性的范围,而不是单点值。
我们最终使用单点值代替了范围,那是因为我从估算范围里选择了最有利于 Kotlin 的值,将所有的估计都转换成了单点值。
例如,当讨论 Kotlin 对编码与调试活动的影响时,我从估计出的可能性范围[-5%,10%]中选择了最大的生产力提升值 10%。在其他情况下,当我们讨论开发人员切换到 Kotlin 的平均时间时,我从估计的可能性范围[5 天,21 天]中选择了最小的 5 天。
此外,我们使用了 Cocomo 2 估计模型专用的工作因子。这些因子并不是放之四海而皆准的真理,在最一般的情况下,应该也有相关联的不确定性。我赋给 Kotlin 的评级高于我实际上认为它应得的评级,我希望通过这种方式消除这种不确定性。
不用说,我们获得的单点值并不是百分百正确。为了得出更完整的估计,我们可以利用真正的估计进行 Monte Carlo 仿真。通过这项技术,我们可以观察可能结果的分布,弄清楚哪种结果最可能出现。
请记住,由于我们将估计压缩成了对 Kotlin 而言最为有利的单点值,所以其他可能的结果会显示出更大的 Kotlin 切换开销。因此,在所有可能的结果中,我们在上文描述的单点值是最有利于 Kotlin 的。
小结
在文章开头部分,我们展示了一些可能会对开发人员比较编程语言造成误导的主观判断。
接下来,我们讨论了客观比较编程语言存在的困难,并进行了一系列的估计,以便弄清楚 Kotlin 栈与 Java 栈完成软件项目所需的总工作量。在执行估计时,我们一直使用估计范围里最有利于 Kotlin 的值。
通过我们的分析,从 Java 切换到 Kotlin 似乎会导致完成软件项目所需的总工作量增加。
更多的工作意味着企业切换到 Kotlin 需要花更多的钱才能获得同样的功能,而用户需要等待更长的时间才能获得产品。
有些开发人员可能会吃惊,觉得这个结果不容易接受。
在考虑了所有的情况之后,谷歌最终决定支持 Kotlin Anroid 开发。对此,谷歌可能需要相当大的投入——谷歌云平台团队是不是没有人可以做类似的分析,从而弄清楚切换到一门新语言所带来的负面影响?
我认为,谷歌员工都是非常聪明的人,我相信他们在决定支持 Kotlin 之前已经进行了非常深入的分析。
在下一篇博文中,我们将讨论谷歌为什么要支持 Kotlin,即使是在通过简单的分析就可以知道那不利于 Android 社区的情况下。