<翻译> Kotlin中的习语

Kotlin 作为一个实践导向的语言,当然是更加注重实践,它里面有很多习语,也就是经常使用的特定写法,来让开发更加快捷,同时也形成了 Kotlin 的风格。

Kotlin 鼓励大家推荐自己喜欢的习语,一个的语言升级有可能会加入,这正好跟 Java 相反,Java 就感觉太死板了。

闲话少说,我们进入正题

创建 DTO(POJOs/POCOs)

1
data class Customer(val name: String, val email: String)

会提供一个带有以下功能的对象:

  1. 为所有属性生成 Getter 方法,如果属性是可变的,也会生成 Setter 方法
  • equals()
  • hashCode()
  • toString()
  • copy()
  • 为所有属性生成 component1(), component2()…

方法参数的默认参数

1
fun foo(a: Int = 0, b: String = "") { ... }

拥有了方法默认参数,就可以只赋值必须的几个参数,而其余使用默认,是不是很爽。

过滤一个列表

1
val positives = list.filter { x -> x > 0 }

或者更简单一点

1
val positives = list.filter { it > 0 }

字符串内插值

1
println("Name $name")

还有更强大的

1
println("Name ${ upperCase(user.Name) }")

表达式的结果填入字符串

检查一个实例是某个类的对象

1
2
3
4
5
when (x) {
is Foo -> ...
is Bar -> ...
else -> ...
}

遍历一个 map

1
2
3
for ((k, v) in map) {
println("$k -> $v")
}

k,v 可以取任意名

取值范围

1
2
3
4
5
for (i in 1..100) { ... }  // 从 1 到 100
for (i in 1 until 100) { ... } // 从 1 到 99
for (x in 2..10 step 2) { ... } // 2 4 6 8 10
for (x in 10 downTo 1) { ... }
if (x in 1..10) { ... }

只读的列表

1
val list = listOf("a", "b", "c")

只读 Map

1
val map = mapOf("a" to 1, "b" to 2, "c" to 3)

访问一个 Map

1
2
println(map["key"])
map["key"] = value

这个都算,我就没见过 map 不这样操作的

懒加载属性

1
2
3
val p: String by lazy {
// compute the string
}

这个才算是棒,在第一次使用的时候初始化属性

扩展函数

1
2
3
fun String.spaceToCamelCase() { ... }

"Convert this to camelcase".spaceToCamelCase()

这也是高能,竟然能对那些司空见惯的对象添加新功能,不知道这是好事还是坏事,凡是自由,都需要限制。

创建单例

1
2
3
object Resource {
val name = "Name"
}

上一篇文章,已经和 Java 的单例对比了,简洁!!

判断非 null 的快捷方式

1
2
3
val files = File("Test").listFiles()

println(files?.size)

判断 files 如果非 null 则返回 size 的值,否则返回 null

判断非 null 以及 null 情况的处理方式 的快捷方式

1
2
3
val files = File("Test").listFiles()

println(files?.size ?: "empty")

判断 files 如果非 null 则返回 size 的值,否则返回 empty

在判断是 null 时执行一个语句

1
2
val data = ...
val email = data["email"] ?: throw IllegalStateException("Email is missing!")

判断输入不是 null 时执行

1
2
3
4
5
val data = ...

data?.let {
... // execute this block if not null
}

把可能为空的变量编程不能为空的变量

1
2
3
val data = ...

val mapped = data?.let { transformData(it) } ?: defaultValueIfDataIsNull

when 语句 作为一个表达式返回

1
2
3
4
5
6
7
8
fun transform(color: String): Int {
return when (color) {
"Red" -> 0
"Green" -> 1
"Blue" -> 2
else -> throw IllegalArgumentException("Invalid color param value")
}
}

try/catch 也可以作为表达式,有返回值

1
2
3
4
5
6
7
8
9
fun test() {
val result = try {
count()
} catch (e: ArithmeticException) {
throw IllegalStateException(e)
}

// Working with result
}

if 语句也可以作为表达式, 有返回值

1
2
3
4
5
6
7
8
9
fun foo(param: Int) {
val result = if (param == 1) {
"one"
} else if (param == 2) {
"two"
} else {
"three"
}
}

对于返回值为 Unit 的函数,可以使用构造器的风格调用

1
2
3
4
5
6
fun arrayOfMinusOnes(size: Int): IntArray {
return IntArray(size).apply {
fill(-1)
... // 其他配置
}
}

现在我才知道,这个 apply 的高能,它就是为了方便对一个对象做一些配置,来个更好的栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

data class Person(var name: String, var age: Int) {
fun wearHat() {
}

fun wearCoat() {
}
}

....

val goodMan = Person("Jin", 27).apply {
wearHat()
wearCoat()
}

单一表达式的函数

1
fun theAnswer() = 42

这个栗子也太简单了,因为 Kotlin 里 If 语句, When 语句, try/catch 语句 都是表达式,所以你懂得。

使用 “with” 在同一个对象上调用多个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Turtle {
fun penDown()
fun penUp()
fun turn(degrees: Double)
fun forward(pixels: Double)
}

val myTurtle = Turtle()
with(myTurtle) { //draw a 100 pix square
penDown()
for(i in 1..4) {
forward(100.0)
turn(90.0)
}
penUp()
}

感觉这和前边那个 apply 类似,都是在一个代码块里,this变成了我们指定的一个对象。不过使用 with 更有过程编程的感觉。

Java7 的 try-with-resources 在 Kotlin 里也有类似的实现

1
2
3
4
val stream = Files.newInputStream(Paths.get("/some/file.txt"))
stream.buffered().reader().use { reader ->
println(reader.readText())
}

而 Java7 的 try-with-resource 是这样的

1
2
3
4
try (BufferedReader br =
new BufferedReader(new FileReader(path))) {
return br.readLine();
}

还是 Kotlin 的比较好看

对于一个需要泛型类型信息的泛型函数的方便方式

1
2
3
4
5
6
//  public final class Gson {
// ...
// public <T> T fromJson(JsonElement json, Class<T> classOfT) throws JsonSyntaxException {
// ...

inline fun <reified T: Any> Gson.fromJson(json: JsonElement): T = this.fromJson(json, T::class.java)

我对泛型还真是不太了解,暂不评价,我该去学习泛型了。

一个可能为 null 的 Boolean 也能直接被 if 条件判断

1
2
3
4
5
6
val b: Boolean? = ...
if (b == true) {
...
} else {
// `b` is false or null
}

以下是一些缩写,有兴趣可以 wiki 一下:

  • DTO – Data Transfer Object
  • POJO – Plain old Java Object
  • POCO – Plain Old CLR Object
  • CLR – Comman Language Runtime