遇见 Kotlin

记得有一次,朋友问我:“你为什么要学那么多种编程语言?”
“因为我想创造一门自己的编程语言。”
“创造自己的语言?为什么?”
“因为我觉得创造语言很有趣啊,这是每个程序猿都应该有的梦想。当然最重要的,还是目前为止我还没有遇到一款真正让我心动的语言。”
“-_-!”

所谓的 没有“女朋友”不要紧,自己造一个出来就好了。

后来,我学了 Kotlin,我兴奋地告诉朋友,“Kotlin 是我遇到的世界上最好的编程语言!”
“那你还要继续创造自己的语言吗?”
“不了,因为我有了 Kotlin。 (*^-^*)”

在这里我想向大家安利一款非常好用的编程语言 - Kotlin。

什么是 Kotlin

Kotlin 由 JetBrains 公司设计并开源,是一种基于 JVM 的强类型编程语言,其代码也可以被转译成 JavaScript。

我们知道 Java 跨平台的关键是因为 JVM,它可以运行与机器无关的 Java 字节码。也因为这一点,诞生了很多可以编译成 Java 字节码的编程语言 Kotlin,Scala,Groovy 等——当然个人认为这些语言的诞生,也与 Java 的不思进取有关。

让我们来看一下 Kotlin 的发展历程。

2011年7月,JetBrains 推出 Kotlin 项目,这是一个面向 JVM 的新语言,它已被开发一年之久。
2012年2月,Kotlin 以 Apache 2 许可证开源。Jetbrains 希望 Kotlin 能够推动 IntelliJ IDEA 的销售。
2016年2月15日,Kotlin v1.0 发布,这被认为是第一个官方稳定版本,并且开始长期的向后兼容。
2017年3月1日,Kotlin v1.1 发布,引入了对 JavaScript 和协程的支持。
2017年4月4日,Kotlin Native 技术预览版发布,这意味着 Kotlin 已经可以绕过 JVM 直接编译成机器码。
2017年5月 Google I/O 大会,Google 宣布在 Android 上为 Kotlin 提供一等支持。
——源自维基百科

先是 Java,然后 Android,然后 JavaScript,然后 Native,然后… Kotlin 的野心正在一步步膨胀。。。

Kotlin 的语法糖

下面是教程部分。

来来来,让我们先来尝一下 Kotlin 的语法糖。

1
2
3
4
5
// Kotlin 代码
fun getFileName(file: File?, defaultValue: String): String {
val fileName = file?.name
return fileName ?: defaultValue
}
1
2
3
4
5
6
7
// Java 代码
public class DemoKt {
public static String getFileName(final File file, final String defaultValue) {
final String fileName = file != null ? file.getName() : null;
return fileName != null ? fileName : defaultValue;
}
}

上面两段代码是等价的,可以看出,因为语法糖的存在,Kotlin 明显更加简洁。

fun 关键词用来定义一个函数,Kotlin 中的函数和变量可以不依赖于类而存在,也就是说,你可以直接把它们提到 class 外面,就像上面的代码一样。见 Kotlin 定义函数

函数参数 file: File?,表示传入的 file 变量有可能为 null。在你使用 file 这个变量的时候,编译器会强制要求你去做非空判断。如果去掉问号,则表示 file 永远不可能为空,如 defaultValue: String 中 defaultValue 就不可能为空。见 Kotlin 空安全

file?.name 表示如果 file 为 null,则值为 null,否则为 file.name。而 file.name 则等同于 file.getName()。

val 关键词用来定义一个不可变的变量,该变量只能被赋值一次,类似于 final。与之相对应的还有一个 var 关键词,用来定义一个可变的变量。

Kotlin 会根据变量所赋的值自动推导出它的类型,比如上例中,fileName 的类型为 String?(自推导类型)

fileName ?: defaultValue 表示如果 fileName 不为空则值为 fileName,否则为 defaultValue。

Kotlin 代码在简洁的同时,也不损失其可阅读性。实际上,Kotlin 的代码可以更加简洁。

1
2
// Kotiln 代码
fun getFileName(file: File?, defaultValue: String) = file?.name ?: defaultValue

如果一个函数体只包含一条语句,那么可以使用等号(=)来替代 return,并可以省略返回值类型。

Kotlin 中缀函数

1
2
3
4
5
6
// Kotlin 代码
val map = mapOf(
"key1" to "value1",
"key2" to "value2",
"key3" to "value3"
)
1
2
3
4
5
// Java 代码
Map<String, String> map = new HashMap<>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");

mapOf 是 Kotlin 定义的一个全局函数,它的参数是个数不限的 Pair,也就是说,诸如 "key" to "value" 之类的表达式的值都是 Pair<String, String>。mapOf 的返回值就是一个 Map。

这里的 to 并不是一个关键词,而是一个中缀函数,"key" to "value",实际上是在调用 "key".to("value")

等等,不好意思忘说了,String 类没有 to 这个方法,这是 Kotlin 通过扩展函数给 String 额外添加的方法。请看下面这段 Kotlin 代码。

1
2
3
4
// Kotlin 代码
infix fun <A, B> A.to(b: B): Pair<A,B> {
return Pair(this, b)
}

Kotin 没有 new 这个关键词,在新建一个对象时,直接调用构造函数就好。

使用 infix 这个关键词,可以把一个单参数函数变成中缀符号来调用。fun <A, B> 这里是表示泛型。

A.to 这种申明函数的方式,表示扩展函数,它可以动态扩展一个类的方法。这里是给所有的类都新增了一个中缀函数 to。

类与构造函数

1
2
3
4
// Kotlin 代码
open class Person(var name:String) {
val friends: List<Person> = ArrayList<Person>()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Java 代码
public class Person {
private final String name;
private final List<Person> friends;

public Person(String name) {
this.name = firstName;
this.friends = new ArrayList<>();
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public List<Person> getFriends() {
return friends;
}
}

lambda 表达式

1
2
3
4
5
// Kotlin 代码
Observable.just(1)
.map { i -> i + 1 }
.doOnComplete { println("complete") }
.subscribe { println(it) }
1
2
3
4
5
// Java 代码
Observable.just(1)
.map(i => i + 1)
.doOnComplete(() => System.out.println("complete"))
.subscribe(i => System.out.println(i))

Kotlin 中的 lambda 表达式和 Java 很像,不过使用的是花括号和箭头。

  • 在 lambda 没有参数时可以省略箭头
  • lambda 只有一个参数时也可省略箭头,被省略的参数为 it

在调用函数时,如果传入的最后一个参数是 labmda 表达式,则可以将 lambda 提到小括号外面。

Kotlin 的 lambda 表达式非常直观,它的设计就是为 DSL 而生的。

Kotlin DSL

如果你看到类似这样奇怪的代码,不要怀疑,它就是 Kotlin:

1
2
3
4
5
6
7
8
9
10
html { 
head {
title { +"XML encoding with Kotlin" }
}
body {
h1 { +"XML encoding with Kotlin" }
p { +"this format is now type-safe" }
a(href="http://jetbrains.com/kotlin") { +"Kotlin" }
}
}

是不是很像 HTML?没错,它就是用来生成 HTML 的。Kotlin 本身就可以用来写 JS,现在 HTML 都能写了,要是能再来一个 CSS 就更好了。于是去 github 上搜了一下,没想到还真有 CSS 的 DSL,见 Aza-Kotlin-CSS

还有这样的,用来写 Android 布局的 DSL。。。(这货叫 anko,据说它的渲染速度是 XML 的 400%)

1
2
3
4
5
6
verticalLayout {
val name = editText()
button("Say Hello") {
onClick { toast("Hello, ${name.text}!") }
}
}

类似的还有 Android Drawable DSLKuery

Android 中的视图绑定

在 Android 没有 ButterKnife 之前,每一个 View 变量都需要一个个 findViewById,自己写出来的代码自己都不想看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Java 代码,未使用 ButterKnife
public class MyActivity extends BaseActivity {
private TextView tv_hello;
private TextView tv_world;
private Button btn_submit;

@Override void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.id.activity_my);
tv_hello = (TextView) findViewById(R.id.tv_hello);
tv_world = (TextView) findViewById(R.id.tv_world);
btn_submit = (Button) findViewById(R.id.btn_submit);
tv_hello.setText("hello");
tv_world.setText("world");
btn_submit.setOnClickListener(new View.OnClickListener() {
@Override onClick(View view) {

}
});
}
}

在有了 ButterKnife 之后,情况终于得到改善。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Java 代码,使用了 ButterKnife
public class MyActivity extends BaseActivity {
@BindView(R.id.tv_hello) TextView tv_hello;
@BindView(R.id.tv_world) TextView tv_world;
@BindView(R.id.btn_submit) Button btn_submit;

@Override void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.id.activity_my);
ButterKnife.bind(this);
tv_hello.setText("hello");
tv_world.setText("world");
btn_submit.setOnClickListener(new View.OnClickListener() {
@Override onClick(View view) {

}
});
}
}

如果使用 Kotlin 呢?你连变量都不用定义了。。。

1
2
3
4
5
6
7
8
9
10
11
12
// Kotlin 代码,通过扩展属性,编译器会自定给你定义这些变量
public class MyActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.id.activity_my)
tv_hello.text = "hello"
tv_world.text = "world"
btn_submit.setOnClickListener {

}
}
}

Kotlin vs Java

最后,来一波总结 - Kotlin vs Java。

首先,Java 有的功能,Kotlin 都有。Java 能用的功能,Kotlin 都能。Kotlin 百分之百兼容 Java。

相比于 Java,Kotlin 更加安全可靠,并拥有更多的语法糖。

  • Kotlin 有空安全检查,能直接避免掉大部分的空指针异常。
  • Kotlin 支持类型检查和自动转换
  • Kotlin 中有属性的概念(get, set),字段必须被变为属性之后才能被访问。
  • Koltin 中的函数是第一类型,这意味着函数可以直接当做变量来使用。
  • Kotlin 中的 lambda 表达式更加简洁,更适合用来生产 DSL。
  • Kotlin 支持操作符重载,并支持中缀函数。
  • Kotlin 支持函数扩展和属性扩展。

这么棒的语言,各位,还不快去试试?

Kotlin 的相关资源