kotlin中的启发

开始之前先祭奠下我那块在天国的硬盘,由于Gogo的不慎操作(我也就是强制关机了一下而已啊喂),这块买来只有三个月的硬盘还在换新的路上。

哦,还有秘封活动记录第二话快出了,Gogo又得吃土去买了。

前言

每学一门新的语言,总会从中得到一些启发。
若是没有产生新的想法,那这门语言也就不值得学。

现在许多语言都十分相似,刚接触编程时的新鲜感似乎早就不见。
然而Gogo在学Kotlin时却产生了“哇,这才是学习一门语言”的想法。
归根到底,这是由于Kotlin诸多的语法特性,极大的丰富了Gogo的见识。

本文并非kotlin的教程,仅仅是谈论kotlin对我的启发。

语句即是表达式

这一条Gogo很早以前便听说过一二,毕竟有不少人在说Lisp怎么怎么好,Gogo本身也对此有些兴趣。没想到却在kotlin中首次学到这种语法。

例如Kotlin中的 If 既可以是语句也可以是表达式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//语句
if(a > 10) {
println("now a > 10 ")
b = 15
} else {
b = 20
}

//表达式
b = if(a > 10) {
println("now a > 10 ")
15
} else {
20
}

每一个代码块的最后一行将会作为表达式的结果。
还有个 when 语法替代了 switch ,并且看起来更加简洁。

1
2
3
4
5
6
7
b = when(a) {
10 -> {
println("now a = 10 ")
15
}
else -> 20
}

甚至函数的返回值也可以用表达式简写

1
2
3
4
fun b(a: Int): Int = when(a) {
> 10 -> 15
else -> 20
}

那么表达式到底有什么意义?可以使代码更加清晰,书写、阅读更加方便。

用lambda扩展语法

kotlin允许我们用lambda函数制造新的语法。
lambda早已经不是什么稀奇的物种,但是kotlin允许将最后一个lambda写在函数外面,这就为创造新的表达方式提供可能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun <T> lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}

//调用
val result = lock(l){
// do something
}

用这种方式就能把一些常用的句式封装起来。
既可以避免失误,还让代码更加清晰易懂。还有下面这个,可读性很好。

1
2
3
4
5
6
7
8
fun <T> unless(condition: Boolean, body: () -> Unit): Unit {
if(!condition) { body() }
}

//调用
unless(a > 10) {
println("a is not bigger than 10")
}

扩展函数扩充对象方法

在kotlin中可以为别的类添加方法。
这是Gogo第一次见到能给别的类添加方法,感觉十分新奇。

例如 KotlinTest 中可以用这种方法写简单的测试。

1
2
3
4
5
6
7
8
9
class StringSpecExample : StringSpec() {
init {

"should add" {
3 + 4 shouldBe 7
}

}
}

重点是”should add”那一行,那行实际上是一个扩展函数调用。
由于本文并非讨论这种语法的实现方式,因此我不介绍它的实现。
这种调用的实现原理大概是编译成这样。

1
2
3
4
5
6
fun stringInvoke(str: String, body: () -> Unit): Unit {
//实现本方法
}

//调用时相当于这样
stringInvoke("should add", { 3 + 4 shouldBe 7 })

虽然明白原理后知道了仍然只是个语法糖,但是初看却让我觉得十分给力。

空安全

空安全我也是第一次在kotlin中见到。空指针异常几乎是最常见的异常,而kotlin试图消灭空指针。

kotlin中每种类型都可能指定能不能为空,例如 String 表示非空的String,而 String? 表示允许为null的String。对于可能为空值的类型,kotlin强制要求判断是否为空。

kotlin还有一些其他的语法来操作null

1
2
3
4
5
6
7
8
var str: String? // str 是一个可能为null的字符串

val len: Int? = str?.length //如果str非空,则返回length,如果为空就返回null
val len: Int = str?.length ?: 0 //如果str?.length为null就返回0

str.let {
println(it) //如果str不为空,就执行代码块中的操作。
}

值得一提的是判断后还会有自动的类型转换,也是个十分好用的功能。

1
2
3
4
5
6
7
val a: Int

a = str.length //编译失败,str可能为null

if(str != null) {
a = str.length //编译通过,str会自动从String?转换成String
}

启发

虽然kotlin看上去有一堆语法特性,然而仔细思考一下就能发现全是语法糖,实际上与其他的语言并没有什么本质区别。

但它让我用一种新的方式思考代码。
我很早以前就听说过 “代码是写给人来读的,只是顺带让机器执行” 这个道理。学了Kotlin后我更是深有体会。
那么多新奇的语法,只需要在编译的时候处理一下就能支持,既然这样,就应该致力于创造一门语法简洁、逻辑清晰、功能丰富的语言。

对比Kotlin和Java,这点尤其突出。虽然我一开始学的正是Java,但现在已经很少写了。
为何?有了Kotlin这么方便的语言,为什么要去写Java那繁杂、笨拙的语法?
反正编译以后功能完全相同,还能相互兼容,为什么要花更多的时间浪费在写一长串代码上,而不是思考上?
我认为Kotlin的做法很好,让代码尽量简洁易读,其他的工作都交给编译器去做。

学kotlin的过程中,我这个低端的Java程序员也很容易看出,kotlin的许多改进都是Java的痛处。作为目前TIOBE上长期排名第一的语言,Java固然强大,但也有很多的问题。而kotlin就像是根据它的缺陷改进出来的一门理想的语言。

kotlin中一些理念来自于总结Java以及其他编程语言的经验,
还有一些可能来自Lisp,也许我是应该去见识见识Lisp这门充满传奇色彩的语言了。