5.3 惯用法
原文链接: https://kotlinlang.org/docs/idioms.html
5.3 惯用法
这里汇集了 Kotlin 中一些零散但常用的惯用法。如果你有喜欢的惯用法,欢迎通过发送 pull request 来贡献。
创建 DTO(POJO/POCO)
1
| data class Customer(val name: String, val email: String)
|
它提供一个具有以下功能的 Customer 类:
- 所有属性的 getter(如果是
var 还有 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 }
|
了解 Java 与 Kotlin 过滤之间的区别。
检查集合中是否存在某个元素
1
2
3
| if ("john@example.com" in emailsList) { ... }
if ("jane@example.com" !in emailsList) { ... }
|
字符串插值
了解 Java 与 Kotlin 字符串拼接之间的区别。
1
2
3
4
5
6
7
8
9
| // Reads a string and returns null if the input can't be converted into an integer. For example: Hi there!
val wrongInt = readln().toIntOrNull()
println(wrongInt)
// null
// Reads a string that can be converted into an integer and returns an integer. For example: 13
val correctInt = readln().toIntOrNull()
println(correctInt)
// 13
|
更多信息请参阅读取标准输入。
实例检查
1
2
3
4
5
| when (x) {
is Foo -> ...
is Bar -> ...
else -> ...
}
|
只读列表
1
| val list = listOf("a", "b", "c")
|
只读映射
1
| val map = mapOf("a" to 1, "b" to 2, "c" to 3)
|
访问映射条目
1
2
| println(map["key"])
map["key"] = value
|
遍历映射或键值对列表
1
2
3
| for ((k, v) in map) {
println("$k -> $v")
}
|
k 和 v 可以是任何方便的名称,例如 name 和 age。
遍历区间
1
2
3
4
5
| for (i in 1..100) { ... } // closed-ended range: includes 100
for (i in 1..<100) { ... } // open-ended range: does not include 100
for (x in 2..10 step 2) { ... }
for (x in 10 downTo 1) { ... }
(1..10).forEach { ... }
|
惰性属性
1
2
3
| val p: String by lazy { // the value is computed only on first access
// compute the string
}
|
扩展函数
1
2
3
| fun String.spaceToCamelCase() { ... }
"Convert this to camelcase".spaceToCamelCase()
|
创建单例
1
2
3
| object Resource {
val name = "Name"
}
|
用内联值类实现类型安全的值
1
2
3
4
5
| @JvmInline
value class EmployeeId(private val id: String)
@JvmInline
value class CustomerId(private val id: String)
|
如果你不小心把 EmployeeId 和 CustomerId 弄混,就会触发编译错误。
注意: @JvmInline 注解只对 JVM 后端有必要。
实例化抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| abstract class MyAbstractClass {
abstract fun doSomething()
abstract fun sleep()
}
fun main() {
val myObject = object : MyAbstractClass() {
override fun doSomething() {
// ...
}
override fun sleep() { // ...
}
}
myObject.doSomething()
}
|
非空简写
1
2
3
| val files = File("Test").listFiles()
println(files?.size) // size is printed if files is not null
|
非空否则简写
1
2
3
4
5
6
7
8
9
10
11
| val files = File("Test").listFiles()
// For simple fallback values:
println(files?.size ?: "empty") // if files is null, this prints "empty"
// To calculate a more complicated fallback value in a code block, use `run`
val filesSize = files?.size ?: run {
val someSize = getSomeSize()
someSize * 2
}
println(filesSize)
|
为 null 时执行某个表达式
1
2
| val values = ...
val email = values["email"] ?: throw IllegalStateException("Email is missing!")
|
获取可能为空的集合的第一个元素
1
2
| val emails = ... // might be empty
val mainEmail = emails.firstOrNull() ?: ""
|
了解 Java 与 Kotlin 获取第一个元素之间的区别。
非空时执行
1
2
3
4
5
| val value = ...
value?.let {
... // execute this block if not null
}
|
非空时映射可空值
1
2
3
4
| val value = ...
val mapped = value?.let { transformValue(it) } ?: defaultValue
// defaultValue is returned if the value or the transform result is null.
|
在 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
| val y = if (x == 1) {
"one"
} else if (x == 2) {
"two"
} else {
"other"
}
|
以构建器风格使用返回 Unit 的方法
1
2
3
| fun arrayOfMinusOnes(size: Int): IntArray {
return IntArray(size).apply { fill(-1) }
}
|
单表达式函数
它等价于
1
2
3
| fun theAnswer(): Int {
return 42
}
|
这可以与其他惯用法有效地结合,从而得到更短的代码。例如与 when 表达式结合:
1
2
3
4
5
6
| fun transform(color: String): Int = when (color) {
"Red" -> 0
"Green" -> 1
"Blue" -> 2
else -> throw IllegalArgumentException("Invalid color param value")
}
|
在对象实例上调用多个方法(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()
}
|
1
2
3
4
5
| val myRectangle = Rectangle().apply {
length = 4
breadth = 5
color = 0xFAFAFA
}
|
这对于配置那些不在对象构造器中的属性很有用。
Java 7 的 try-with-resources
1
2
3
4
| val stream = Files.newInputStream(Paths.get("/some/file.txt"))
stream.buffered().reader().use { reader ->
println(reader.readText())
}
|
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)
|
交换两个变量
1
2
3
| var a = 1
var b = 2
a = b.also { b = a }
|
把代码标记为未完成(TODO)
Kotlin 标准库有一个 TODO() 函数,它总是抛出 NotImplementedError。它的返回类型是 Nothing,因此无论期望什么类型都可以使用。还有一个接受原因参数的重载:
1
| fun calcTaxes(): BigDecimal = TODO("Waiting for feedback from accounting")
|
IntelliJ IDEA 的 Kotlin 插件理解 TODO() 的语义,并会在 TODO 工具窗口中自动添加一个代码指针。
接下来做什么?