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): Unit2. 定义局部变量
分配一次(只读)局部变量:
val a: Int = 1
val b = 2可变变量:
var x = 53. 使用字符串模板
模板表达式以美元符号($)开头以及简单名称组成:
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 bif分支可以是块,并且最后一个表达式是块的值:
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 String7. 使用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 <T> 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 <T, R> List<T>.map(transform: (T) -> R): List<R> { val result = arrayListOf<R>() for (item in this) result.add(transform(item)) return result }该函数可以如下调用:(请注意,如果 lambda 是该调用的唯一参数,则调用中的圆括号可以完全省略。)
val doubled = ints.map { value -> value * 2 }it:单个参数的隐式名称一个有用的约定是,如果函数字面值只有一个参数,
那么它的声明可以省略(连同->),其名称是itints.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
}