1. 定义函数
具有返回Int
类型的两个Int
参数的函数:
fun sum(a: Int, b: Int): Int复制代码
函数与表达主体和推断返回值的类型:
fun sum(a: Int, b: Int) = a + b复制代码
函数返回无意义值:(Unit
返回类型可以省略:)
fun printSum(a: Int, b: Int): Unit复制代码
2. 定义局部变量
分配一次(只读)局部变量:
val a: Int = 1
val b = 2复制代码
可变变量:
var x = 5复制代码
3. 使用字符串模板
模板表达式以美元符号($
)开头以及简单名称组成:
var a = 1
println("a is $a")复制代码
或大括号中的任意表达式
val s = "abc"
val str = "$s.length is ${s.length}"
fun maxOf(a: Int, b: Int): Int
println("max of 0 and 42 is ${maxOf(0, 42)}")复制代码
4. 使用条件表达式
使用if
表达式:
fun maxOf(a: Int, b: Int) = if (a > b) a else b复制代码
if
分支可以是块,并且最后一个表达式是块的值:
val max = if (a > b) {
print("Choose a")
a
} else {
print("Choose b")
b
}复制代码
5. 使用可空值来检查null值
当可能为null
值时,引用必须被明确地标记为可空(null
)
如果str
不包含整数,则返回null
fun parseInt(str: String): Int? {
// ...
}复制代码
在 Kotlin 中,类型系统区分一个引用可以容纳 null (可空引用)还是不能容纳(非空引用)。
例如,String 类型的常规变量不能容纳 null
var a: String = "abc"
a = null // 编译错误复制代码
如果要允许为空,我们可以声明一个变量为可空字符串,写作 String?
:
var b: String? = "abc"
b = null // ok复制代码
现在,如果你调用 a
的方法或者访问它的属性,它保证不会导致 NPE
,这样你就可以放心地使用:
val l = a.length复制代码
但是如果你想访问 b
的同一个属性,那么这是不安全的,并且编译器会报告一个错误:
val l = b.length // 错误:变量“b”可能为空复制代码
但是我们还是需要访问该属性,对吧?有几种方式可以做到
在条件中判空
val l = if (b != null) b.length else -1复制代码
安全的调用操作符
如果任意一个属性(环节)为空,这个链式调用就会返回 nul
b?.length bob?.department?.head?.name复制代码
如果要只对非空值执行某个操作,安全调用操作符可以与
let
一起使用:b?.length?.let { println(it) } / 输出 A 并忽略 null复制代码
Elvis 操作符
当我们有一个可空的引用 r
时,我们可以说“如果 r
非空,我使用它;否则使用某个非空的值 x
”
val l: Int = if (b != null) b.length else -1复制代码
这还可以通过 Elvis 操作符表达,写作 ?:
val l = b?.length ?: -1复制代码
!!
操作符
第三种选择是为 NPE 爱好者准备的。我们可以写 b!!
,这会返回一个非空的 b
值
(例如:在我们例子中的 String
)或者如果 b
为空,就会抛出一个 NPE
异常:
val l = b!!.length复制代码
安全的类型转换as?
如果对象不是目标类型,那么常规类型转换可能会导致 ClassCastException
。
另一个选择是使用安全的类型转换,如果尝试转换不成功则返回 null
val aInt: Int? = a as? Int复制代码
可空类型的集合
如果你有一个可空类型元素的集合,并且想要过滤非空元素,你可以使用 filterNotNull
来实现。
val nullableList: List<Int?> = listOf(1, 2, null, 4)
val intList: List<Int> = nullableList.filterNotNull()复制代码
6. 使用类型检查和自动转换
is
运算符检查表达式是否是类型的实例。
obj is String复制代码
7. 使用for循环
for
循环提供迭代器用来遍历任何东西。 语法如下:
for (item in collection) print(item)复制代码
主体可以是一个块,如下 -
for (item: Int in ints) {
// ...
}复制代码
for
循环数组被编译为一个基于索引的循环,它不会创建一个迭代器对象。如果要遍历具有索引的数组或列表,可以这样做:
for (i in array.indices) {
print(array[i])
}复制代码
或者,可以使用withIndex
库函数:
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}复制代码
8. 使用 while 循环
while
和 do..while
用法和java语言中一样,如下 -
while (x > 0) {
x--
}
do {
val y = retrieveData()
} while (y != null) // y is visible here!复制代码
9. 使用 when 表达式
如果许多情况应该以同样的方式处理,分支条件可使用逗号组合:
when (x) {
0, 1 -> print("x == 0 or x == 1")
else -> print("otherwise")
}复制代码
可以使用任意表达式(不仅仅是常量)作为分支条件
when (x) {
parseInt(s) -> print("s encodes x")
else -> print("s does not encode x")
}复制代码
还可以检查值是否在或不在(in
/!in
)范围内或集合中
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}复制代码
另一个可能性是检查一个值是否(is
/!is
)为指定类型的值。请注意,由于智能转换,可以访问类型的方法和属性,而无需任何额外的检查。
fun hasPrefix(x: Any) = when(x) {
is String -> x.startsWith("prefix")
else -> false
}复制代码
10. 使用范围
使用in
操作符检查数字是否在指定范围内:
fun main(args: Array<String>) {
val x = 10
val y = 9
if (x in 1..y+1) {
println("fits in range")
}
}复制代码
检查一个数字是否超出指定范围:!in
fun main(args: Array<String>) {
val list = listOf("a", "b", "c")
if (-1 !in 0..list.lastIndex) {
println("-1 is out of range")
}
if (list.size !in list.indices) {
println("list size is out of valid list indices range too")
}
}复制代码
迭代范围:step
fun main(args: Array<String>) {
for (x in 1..10 step 2) {
println(x)
}
println("===============================")
for (x in 9 downTo 0 step 3) {
println(x)
}
}复制代码
上面代码,执行结果如下 -
1
3
5
7
9
===============================
9
6
3
0复制代码
检查集合是否包含一个对象,使用in
运算符:
fun main(args: Array<String>) {
val items = setOf("apple", "banana", "kiwi")
when {
"orange" in items -> println("juicy")
"apple" in items -> println("apple is fine too")
}
}复制代码
11. 高阶函数
高阶函数是将函数用作参数或返回值的函数。
这种函数的一个很好的例子是 lock()
,它接受一个锁对象和一个函数,获取锁,运行函数并释放锁
fun lock(lock: Lock, body: () -> T): T {
lock.lock()
try {
return body()
}
finally {
lock.unlock()
}
}复制代码
让我们来检查上面的代码:body
拥有函数类型:() -> T
,
所以它应该是一个不带参数并且返回 T
类型值的函数。
它在 try{: .keyword }-代码块内部调用、被 lock
保护,其结果由lock()
函数返回。
如果我们想调用 lock()
函数,我们可以把另一个函数传给它作为参数(参见函数引用):
fun toBeSynchronized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchronized)复制代码
通常会更方便的另一种方式是传一个 lambda 表达式:
val result = lock(lock, { sharedResource.operation() })复制代码
Lambda 表达式
- lambda 表达式总是被大括号括着,
- 其参数(如果有的话)在
->
之前声明(参数类型可以省略), - 函数体(如果存在的话)在
->
后面。
在 Kotlin 中有一个约定,如果函数的最后一个参数是一个函数,并且你传递一个 lambda 表达式作为相应的参数,你可以在圆括号之外指定它:
lock (lock) { sharedResource.operation() }复制代码
高阶函数的另一个例子是
map()
:fun
List .map(transform: (T) -> R): List { val result = arrayListOf () for (item in this) result.add(transform(item)) return result }复制代码 该函数可以如下调用:(请注意,如果 lambda 是该调用的唯一参数,则调用中的圆括号可以完全省略。)
val doubled = ints.map { value -> value * 2 }复制代码
it
:单个参数的隐式名称一个有用的约定是,如果函数字面值只有一个参数,
那么它的声明可以省略(连同->
),其名称是it
ints.map { it * 2 }复制代码
Kotlin惯用语法
1. 创建DTO(POJO/POCO)
我们经常创建一个类,只能持有数据。 在这样一个类中,一些标准功能通常是从数据中机械推导出来的。 在Kotlin中,这被称为数据类,并被标记为数据:
data class Customer(val name: String, val email: String)复制代码
编译器自动从主构造函数中声明的所有属性导出以下成员:
equals()
/hashCode()
函数对toString()
函数,它由”User(name=John, age=42)
“构成- componentN()函数对应于属性的声明顺序
如果这些函数中的任何一个在类体中显式定义或继承自其基类型,则不会生成该函数。
为了确保生成的代码的一致性和有意义的行为,数据类必须满足以下要求:
- 主构造函数需要至少有一个参数;
- 主构造函数的所有参数需要标记为
val
或var
; - 数据类不能是抽象、开放、密封或者内部的;
- 数据类只能实现接口(在1.1之前)。
复制
在很多情况下,我们需要复制一个对象改变它的一些属性,但其余部分保持不变。 copy() 函数就是为此而生成。对于上文的 User 类,其实现会类似下面这样:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)复制代码
val jack = User(name = "Jack", age = 1)
val olderJack = jack.copy(age = 2)复制代码
数据类和解构声明
为数据类生成的 Component
函数 使它们可在解构声明中使用:
val jane = User("Jane", 35)
val (name, age) = jane
println("$name, $age years of age") // 输出 "Jane, 35 years of age"复制代码
2. 函数参数的默认值
fun foo(a: Int = 0, b: String = "") { ... }复制代码
3. 过滤列表
val positives = list.filter { it > 0 }复制代码
4. 字符串插值
println("Name $name")复制代码
5. 实例检查
when (x) {
is Foo -> ...
is Bar -> ...
else -> ...
}复制代码
6. 遍历映射/列表对
for ((k, v) in map) {
println("$k -> $v")
}复制代码
7. 使用范围
for (i in 1..100) { ... } // closed range: includes 100
for (i in 1 until 100) { ... } // half-open range: does not include 100
for (x in 2..10 step 2) { ... }
for (x in 10 downTo 1) { ... }
if (x in 1..10) { ... }复制代码
8. 只读列表
val list = listOf("a", "b", "c")复制代码
9. 只读映射
val map = mapOf("a" to 1, "b" to 2, "c" to 3)复制代码
10. 访问映射
println(map["key"]) // 打印值
map["key"] = value // 设置值复制代码
11. 懒属性
val p: String by lazy {
// compute the string
}复制代码
12. 扩展函数
un String.spaceToCamelCase() { ... }
"Convert this to camelcase".spaceToCamelCase()复制代码
13. 创建单例
object Resource {
val name = "Name"
}复制代码
14. 如果不为null
的速记
val files = File("Test").listFiles()
println(files?.size)复制代码
15. 如果不为null
和else
的速记
val files = File("Test").listFiles()
println(files?.size ?: "empty")复制代码
16. 如果为null
,执行语句
val data = ...
val email = data["email"] ?: throw IllegalStateException("Email is missing!")复制代码
17. 如果不为null
,执行语句
val data = ...
data?.let {
... // execute this block if not null
}复制代码
18. 在 when
语句上返回
fun transform(color: String): Int {
return when (color) {
"Red" -> 0
"Green" -> 1
"Blue" -> 2
else -> throw IllegalArgumentException("Invalid color param value")
}
}复制代码
19. try/catch
表达式
fun test() {
val result = try {
count()
} catch (e: ArithmeticException) {
throw IllegalStateException(e)
}
// Working with result
}复制代码
20. if
表达式
fun foo(param: Int) {
val result = if (param == 1) {
"one"
} else if (param == 2) {
"two"
} else {
"three"
}
}复制代码
21. 单表达式函数
fun theAnswer() = 42复制代码
这相当于
fun theAnswer(): Int {
return 42
}复制代码
这可以与其他惯用语法有效结合,从而代码更短。 例如,与when-expression
结合:
fun transform(color: String): Int = when (color) {
"Red" -> 0
"Green" -> 1
"Blue" -> 2
else -> throw IllegalArgumentException("Invalid color param value")
}复制代码
22. 在对象实例上调用多个方法(‘with’)
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()
}复制代码
23. 使用可空的布尔值
val b: Boolean? = ...
if (b == true) {
...
} else {
// `b` is false or null
}复制代码