文章目录
  1. 1. 字符串模板(string template)
  2. 2. 分号推断(semicolon inference)
  3. 3. 定义函数(define function)
  4. 4. var/val(local variable)
  5. 5. 可空值(nullable value)
  6. 6. 集合字面量(collection literals)
  7. 7. when表达式(when expression)
  8. 8. is运算符(is operator)
  9. 9. 范围运算符(range)
  10. 10. 运算符重载(operator overloading)
  11. 11. 包别名(package alias)
  12. 12. 类型别名(type alias)
  13. 13. 扩展函数(extension function)
  14. 14. 函数扩展(function expansion)
  15. 15. 默认参数(default arguments)
  16. 16. 单例对象(singleton)
  17. 17. 伴生对象(companion object)
  18. 18. getter和setter(getter and setter)
  19. 19. 数据类(data class)
  20. 20. 没有原始类型(no raw types)
  21. 21. 没有受检查异常(no checked exception)
  22. 22. 没有static(no static member)
  23. 23. 多返回值(multiple return values)
  24. 24. 嵌套块注释(nested block comment)
  25. 25. 在线版IDE

当今的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<String> colors = Arrays.asList("red", "blue");

有好事者(Guava)提供了这样的类库:

1
List<String> colors = ImmutableList.of("red", "blue");

其实Java 8提供的Stream也许已经能够满足你的需求:

1
Stream<String> colors = Stream.of("red", "blue");

Java 9很可能会引入集合字面量

1
List<String> 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表达式有点像是不需要breakswitch…case

1
2
3
4
5
6
7
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 这里可以写多行哦
print("x is neither 1 nor 2")
}
}

但它还可以做得更多:

1
2
3
4
when {
"orange" in items -> println("juicy")
"apple" in items -> println("apple is fine too")
}

可以把它作为扩展版if…else来用。它有点类似Groovy的switch

is运算符(is operator

先看代码:

1
2
3
4
5
6
7
fun getStringLength(obj: Any): Int? {
if (obj is String && obj.length > 0) {
return obj.length
}
return null
}

在这里,obj is String之后就可以将其作为String来使用了,调用Stringlength属性。is不仅比instanceof更加短小精悍,而且还会自动将变量转换为is后面的类型。应该是出于C#而胜于C#。C#提供了isas,但是as在Kotlin中显然就毫无用武之地了。

范围运算符(range

Java如果要循环打印1至9,一般就是这样:

1
2
3
4
5
6
7
// Java 8以前
for (int i = 1; i <= 9; i++) {
System.out.println(i);
}
// Java 8 lambda
IntStream.range(1, 9).forEach(System.out::println);

如果需要步长(step),lambda方案就需要引入filter、map或iterate。Kotlin自带接口ClosedRange,用了点糖:

1
2
3
for (i in 1..9) print(i)
for (i in 1..9 step 2) print(i)
for (i in 9 downTo 1) print(i)

bash早就支持大括号配合..brace expansion)的这种方式了:

1
2
3
4
5
6
7
for i in {1..9}; do echo $i; done
# bash 4支持步长
for i in {1..9..2}; do echo $i; done
# 否则用seq
for i in `seq 1 2 9`; do echo $i; done

不过我更喜欢ruby,支持lambda的写法更加优雅:

1
2
(1..9).each {|x| puts x}
(1..9).step(2) {|x| puts x}

运算符重载(operator overloading

这个是C#和Scala的把戏:

1
2
3
4
data class Point(val x: Int, val y: Int)
operator fun Point.plus(val point: Point) = Point(x + point.x, y + point.y)
Point(1, 2) + Point(3, 4) // Point(x=4, y=6)

怎么知道+号上映射的是plus方法呢?还是得参考官方文档

包别名(package alias

Kotlin支持为包指定别名,对代码洁癖患者可能会起到一定的疗效:

1
import java.math.BigDecimal as bd

Python、Groovy也都是这样:

1
2
import java.util.List as UtilList
import java.awt.List as AwtList

类型别名(type alias

Kotlin还支持为类型指定别名:

1
typealias Row = List<Int>

这应该是来自Scala的Type:

1
type Row = List[Int]

扩展函数(extension function

扩展函数允许为一个类增加公有静态方法,调用时就好像这个方法是原生的一样。

1
2
3
4
5
fun String.greetingsWith(greeting: String) {
println("$greeting, $this")
}
"ggg".greetingsWith("Hello")

虽然JavaScript也能轻易做到,但我还是强烈地认为它来自于C#,毕竟都是一脉相承。kotlin更近一步支持扩展属性:

1
2
3
4
val String.halfLength: Int
get() = length / 2
"ggg".halfLength // 1

函数扩展(function expansion

如果函数的最后一个参数类型是个函数,可以通过大括号来传值:

1
2
3
4
5
fun calculate(a: Int, cal: (Int) -> Int): Int {
return cal(a)
}
calculate(3) { x -> x * x } // 9

这应该是来自ruby的block:

1
2
3
4
5
def calculate(a, &cal)
cal.call a
end
calculate(3) { |x| x * x } # 9

默认参数(default arguments

Kotlin支持为参数指定一个默认值:

1
2
3
4
5
6
7
8
fun main(args: Array<String>) {
show()
show("Good morning")
}
fun show (msg: String = "Hello World") {
println("$msg")
}

Java一般是靠重载来实现默认参数。许多其它的语言都支持默认参数。例如,JavaScript在ES6上也支持默认参数了:

1
2
3
function multiply(a, b = 1) {
return a * b;
}

单例对象(singleton

1
2
object Document {
}

这应该是来自于Scala,连关键字都一模一样。

伴生对象(companion object

伴生对象经常用于Factory。这一点相信也是来自于Scala。

1
2
3
4
5
class MyClass {
companion object Factory { // Factory可省略,省略后可用Companion引用
fun create(): MyClass = MyClass()
}
}

getter和setter(getter and setter

Kotlin像C#一样支持属性(property)。我们来看两个例子:

1
2
3
4
5
6
7
val isEmpty: Boolean
get() = this.size == 0
var counter = 0
private set(value) {
if (value >= 0) field = value
}

数据类(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在内的几十种语言。

文章目录
  1. 1. 字符串模板(string template)
  2. 2. 分号推断(semicolon inference)
  3. 3. 定义函数(define function)
  4. 4. var/val(local variable)
  5. 5. 可空值(nullable value)
  6. 6. 集合字面量(collection literals)
  7. 7. when表达式(when expression)
  8. 8. is运算符(is operator)
  9. 9. 范围运算符(range)
  10. 10. 运算符重载(operator overloading)
  11. 11. 包别名(package alias)
  12. 12. 类型别名(type alias)
  13. 13. 扩展函数(extension function)
  14. 14. 函数扩展(function expansion)
  15. 15. 默认参数(default arguments)
  16. 16. 单例对象(singleton)
  17. 17. 伴生对象(companion object)
  18. 18. getter和setter(getter and setter)
  19. 19. 数据类(data class)
  20. 20. 没有原始类型(no raw types)
  21. 21. 没有受检查异常(no checked exception)
  22. 22. 没有static(no static member)
  23. 23. 多返回值(multiple return values)
  24. 24. 嵌套块注释(nested block comment)
  25. 25. 在线版IDE