当今的Kotlin真是炙手可热啊。自2011年JetBrains为了提高IDEA的销量而推出这个项目至今,它一直低调而稳定地发展着。到了上个月即2017年5月,Google宣布将Kotlin语言作为Android开发的一级编程语言,算是到达了语言生的第一个巅峰。Kotlin自身受到Java、C#、JavaScript、Scala、Groovy等语言的影响,本文总结了它提供的部分常见语法糖并与其它语言进行比较。
字符串模板(string template)
Kotlin可以直接通过println("Hello, {name}")或println("Hello, $name")来使用字符串模板,而Java则需要先借助String.format来生成字符串。而kotlin还支持表达式,如${person.name}和${2 * 3}。这块古老的糖从shell开始就有了:
1 | echo "Hello, ${name}" |
如果你疑惑为什么无须导入包即可直接使用println,那是因为这个方法所在的kotlin.io包是默认导入(Default Imports)的,正如Java会默认导入java.lang.*一样。Kotlin的默认导入请参考这里。
分号推断(semicolon inference)
我们知道Java中的每一条语句结束后,需要加上分号。Kotlin中的分号是可选的。这应该是来自JavaScript吧。不过写JavaScript还是推荐把分号都加上,否则可能有危险。Groovy、Scala也有同样的能力。
定义函数(define function)
JavaScript中,function;Go语言,func;Kotlin,fun。怎么看都像是SIM卡变成了micro-SIM,然后又变成了nano-SIM卡。Kotlin可以在类之外定义全局函数,也可以在函数中定义局部函数。这一点类似于JavaScript。
var/val(local variable)
变量是var,表示variable;常量是val,表示fixed value。这个很明显来自Scala。在Java中就只能用final关键字了。
可空值(nullable value)
在变量类型后面加上一个问号,表明这个变量可以为null,否则默认不能为null。例如:
这一句编译错误:var a: Int = null
这样才能成功:var a: Int? = null
这块糖显然来自于C#,但是更加严格了。它是为了避免Java中的null所造成的十亿美元的错误。
集合字面量(collection literals)
Java从来就不愿意用糖吸引小朋友……所以一般快速创建集合是这么写的:
1 | List colors = Arrays.asList("red", "blue"); |
有好事者(Guava)提供了这样的类库:
1 | List colors = ImmutableList.of("red", "blue"); |
其实Java 8提供的Stream也许已经能够满足你的需求:
1 | Stream colors = Stream.of("red", "blue"); |
Java 9很可能会引入集合字面量:
1 | List colors = List.of("red", "blue"); |
Kotlin出手了:
1 | val colors = listOf("red", "blue") |
不过实际上并非是语法层面的糖,listOf只是一个方法而已。我相信是从Scala中偷师的:
1 | val colors = List("red", "blue") |
当然Scala还有自己的经典方式:
1 | val colors = "red" :: "blue" :: Nil |
C#可以这样来初始化集合:
1 | var colors = new ArrayList{"red", "blue"}; |
JavaScript、Ruby、Go等直接扩展了数组,所以就更省心了。
when表达式(when expression)
when表达式有点像是不需要break的switch…case:
1 | when (x) { |
但它还可以做得更多:
1 | when { |
可以把它作为扩展版if…else来用。它有点类似Groovy的switch。
is运算符(is operator)
先看代码:
1 | fun getStringLength(obj: Any): Int? { |
在这里,obj is String之后就可以将其作为String来使用了,调用String的length属性。is不仅比instanceof更加短小精悍,而且还会自动将变量转换为is后面的类型。应该是出于C#而胜于C#。C#提供了is和as,但是as在Kotlin中显然就毫无用武之地了。
范围运算符(range)
Java如果要循环打印1至9,一般就是这样:
1 | // Java 8以前 |
怎么知道+号上映射的是plus方法呢?还是得参考官方文档。
包别名(package alias)
Kotlin支持为包指定别名,对代码洁癖患者可能会起到一定的疗效:
1 | import java.math.BigDecimal as bd |
Python、Groovy也都是这样:
1 | import java.util.List as UtilList |
类型别名(type alias)
Kotlin还支持为类型指定别名:
1 | typealias Row = List |
这应该是来自Scala的Type:
1 | type Row = List[Int] |
扩展函数(extension function)
扩展函数允许为一个类增加公有静态方法,调用时就好像这个方法是原生的一样。
1 | fun String.greetingsWith(greeting: String) { |
虽然JavaScript也能轻易做到,但我还是强烈地认为它来自于C#,毕竟都是一脉相承。kotlin更近一步支持扩展属性:
1 | val String.halfLength: Int |
函数扩展(function expansion)
如果函数的最后一个参数类型是个函数,可以通过大括号来传值:
1 | fun calculate(a: Int, cal: (Int) -> Int): Int { |
这应该是来自ruby的block:
1 | def calculate(a, &cal) |
默认参数(default arguments)
Kotlin支持为参数指定一个默认值:
1 | fun main(args: Array) { |
Java一般是靠重载来实现默认参数。许多其它的语言都支持默认参数。例如,JavaScript在ES6上也支持默认参数了:
1 | function multiply(a, b = 1) { |
单例对象(singleton)
1 | object Document { |
这应该是来自于Scala,连关键字都一模一样。
伴生对象(companion object)
伴生对象经常用于Factory。这一点相信也是来自于Scala。
1 | class MyClass { |
getter和setter(getter and setter)
Kotlin像C#一样支持属性(property)。我们来看两个例子:
1 | val isEmpty: Boolean |
数据类(data class)
告别繁琐的Java数据类的时代到来了:
1 | data class User(val name: String, val age: Int) |
看到最前面的data,我突然觉得这是源于lombok…
没有原始类型(no raw types)
Kotlin没有原始类型,一切皆对象:1.inc()。这一点与Ruby、Scala相同。
没有受检查异常(no checked exception)
Kotlin并没有受检查异常。因为在大型的项目中,它对代码质量的提升极其有限,但是却大大降低了效率。C#十多年前就是这样了,有兴趣的读者可以参考这篇文章。
没有static(no static member)
Kotlin也没有static的成员。这一点与Scala相同。在Kotiln中可以使用包级别的函数,或者是伴生对象来实现static的效果。
多返回值(multiple return values)
可以从这一个例子中看到Kotlin是如何实现函数的多返回值的。我猜应该是受到了Go语言的启发。
嵌套块注释(nested block comment)
Kotlin支持块注释/* … */的嵌套。这可能是来自Haskell或F#。
在线版IDE
如果不想安装那么多语言,可以试试这个在线版IDE,它支持包括Kotlin在内的几十种语言。