目录
- 使用Kotlin进行Android开发基本语法与习惯用法
- 一、基本语法
- 二、习惯用法
- 2.1创建 DTOs
- 2.2函数的默认参数
- 2.3过滤 list
- 2.4检测元素是否存在于集合中
- 2.5字符串内插
- 2.6类型判断
- 2.7遍历 map/pair型list
- 2.8使用区间
- 2.9只读 list
- 2.10只读 map
- 2.11访问 map
- 2.12延迟属性
- 2.13扩展函数
- 2.14创建单例
- 2.15 If not null 缩写
- 2.16 If not null and else 缩写
- 2.17 if not null 执行代码
- 2.18 映射可空值(如果非空的话)
- 2.19 返回 when 表达式
- 2.20 对一个对象实例调用多个方法 (with)
- 2.21 配置对象的属性(apply)
- 2.22 try with resources
- 2.23 对于需要泛型信息的泛型函数的适宜形式
- 2.24 交换两个变量
使用Kotlin进行Android开发基本语法与习惯用法
自 2019 年 Google I/O 以来,Kotlin 就成为了 Android 移动开发的首选。
使用 Kotlin 进行 Android 开发,可以受益于:
- 代码更少、可读性更强。花更少的时间来编写代码与理解他人的代码。
- 成熟的语言与环境。自 2011 年创建以来,Kotlin 不仅通过语言而且通过强大的工具在整个生态系统中不断发展。 现在,它已无缝集成到 Android Studio 中, 并被许多公司积极用于开发 Android 应用程序。
- Android Jetpack 与其他库中的 Kotlin 支持。KTX 扩展 为现有的 Android 库添加了 Kotlin 语言特性,如协程、扩展函数、lambdas 与命名参数。
- 与 Java 的互操作性。可以在应用程序中将 Kotlin 与 Java 编程语言一起使用, 而无需将所有代码迁移到 Kotlin。
- 支持多平台开发。不仅可以使用 Kotlin 开发 Android,还可以开发 iOS、后端与 Web 应用程序。 享受在平台之间共享公共代码的好处。
- 代码安全。更少的代码与更好的可读性导致更少的错误。Kotlin 编译器检测这些剩余的错误,从而使代码安全。
- 易学易用。Kotlin 非常易于学习,尤其是对于 Java 开发人员而言。
- 大社区。Kotlin 得到了社区的大力支持与许多贡献,该社区在全世界范围内都在增长。 根据 Google 的说法,Play 商店前 1000 个应用中有 60% 以上使用 Kotlin。
一、基本语法
1.1变量
定义只读局部变量使用关键字 val 定义。只能为其赋值一次。
fun main() {
val a: Int = 1 // 立即赋值
val b = 2 // 自动推断出 `Int` 类型
val c: Int // 如果没有初始值类型不能省略
c = 3 // 明确赋值
println("a = $a, b = $b, c = $c")
}
// a = 1, b = 2, c = 3
可重新赋值的变量使用 var 关键字:
fun main() {
var x = 5 // 自动推断出 `Int` 类型
x += 1
println("x = $x")
}
// x = 6
顶层变量:
val PI = 3.14
var x = 0
fun incrementX() {
x += 1
}
fun main() {
println("x = $x; PI = $PI")
incrementX()
println("incrementX()")
println("x = $x; PI = $PI")
}
// x = 0; PI = 3.14
// incrementX()
// x = 1; PI = 3.14
1.2函数
Kotlin 中的函数使用 fun 关键字声明:
函数参数使用 Pascal 表示法定义,即 name: type。参数用逗号隔开。 每个参数必须有显式类型:
fun double(x: Int, y: Int): Int {
return 2 * x * y
}
函数参数可以有默认值,当省略相应的参数时使用默认值。与其他语言相比,这可以减少重载数量:
fun double(x: Int, y: Int= 2): Int {
return 2 * x * y
}
如果一个默认参数在一个无默认值的参数之前,那么该默认值只能通过使用具名参数调用该函数来使用:
fun double(x: Int = 3, y: Int): Int {
return 2 * x *y
}
// double(y = 2) 使用默认值 x = 3
如果在默认参数之后的最后一个参数是 lambda 表达式,那么它既可以作为具名参数在括号内传入,也可以在括号外传入
fun foo(
bar: Int = 0,
baz: Int = 1,
qux: () -> Unit,
) { /*……*/ }
foo(1) { println("hello") } // 使用默认值 baz = 1
foo(qux = { println("hello") }) // 使用两个默认值 bar = 0 与 baz = 1
foo { println("hello") } // 使用两个默认值 bar = 0 与 baz = 1
带有两个 Int 参数、返回 Int 的函数:
fun sum(a: Int, b: Int): Int {
return a + b
}
将表达式作为函数体、返回值类型自动推断的函数:
fun sum(a: Int, b: Int) = a + b
调用函数使用传统的方法:
val result = double(2)
1.3字符串模板
字符串字面值可以包含模板表达式 ,即一些小段代码,会求值并把结果合并到字符串中。 模板表达式以美元符($)开头,由一个简单的名字构成:
val i = 10
println("i = $i") // 输出“i = 10”
或者用花括号括起来的任意表达式:
val s = "abc"
println("$s.length is ${s.length}") // 输出“abc.length is 3”
1.4条件表达式
在 Kotlin 中,if是一个表达式,即它会返回一个值。 因此就不需要三元运算符(条件 ? 然后 : 否则),因为普通的 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
}
1.5空值与 null 检测
在 Kotlin 中,类型系统区分一个引用可以容纳 null (可空引用)还是不能容纳(非空引用)。 例如,String 类型的常规变量不能容纳 null:
var a: String = "abc" // 默认情况下,常规初始化意味着非空
// a = null // 编译错误
当某个变量的值可以为 null 的时候,必须在声明处的类型后添加 ? 来标识该引用可为空。
var b: String? = "abc" // 可以设置为空
b = null // ok
现在,如果你调用 a 的方法或者访问它的属性,它保证不会导致 NPE,这样你就可以放心地使用:
val l = a.length
但是如果你想访问 b 的同一个属性,那么这是不安全的,并且编译器会报告一个错误:
val l = b.length // 错误:变量“b”可能为空
但是我们还是需要访问该属性,对吧?有几种方式可以做到。
1.5.1在条件中检测 null
首先,你可以显式检测 b 是否为 null,并分别处理两种可能:
val l = if (b != null) b.length else -1
编译器会跟踪所执行检测的信息,并允许你在 if 内部调用 length。 同时,也支持更复杂(更智能)的条件:
val b: String? = "Kotlin"
if (b != null && b.length > 0) {
print("String of length ${b.length}")
} else {
print("Empty string")
}
请注意,这只适用于 b 是不可变的情况(即在检测和使用之间没有修改过的局部变量 ,或者不可覆盖并且有幕后字段的 val 成员),因为否则可能会发生在检测之后 b 又变为 null 的情况。
1.5.2安全的调用
你的第二个选择是安全调用操作符,写作 ?.
val a = "Kotlin"
val b: String? = null
println(b?.length)
println(a?.length) // 无需安全调用
如果 b 非空,就返回 b.length,否则返回 null,这个表达式的类型是 Int?。
安全调用在链式调用中很有用。例如,如果一个员工 Bob 可能会(或者不会)分配给一个部门, 并且可能有另外一个员工是该部门的负责人,那么获取 Bob 所在部门负责人(如果有的话)的名字,我们写作:
bob?.department?.head?.name
如果任意一个属性(环节)为空,这个链式调用就会返回 null。
如果要只对非空值执行某个操作,安全调用操作符可以与 let 一起使用:
val listWithNulls: List<String?> = listOf("Kotlin", null)
for (item in listWithNulls) {
item?.let {
println(it)
} // 输出 Kotlin 并忽略 null
}
安全调用也可以出现在赋值的左侧。这样,如果调用链中的任何一个接收者为空都会跳过赋值,而右侧的表达式根本不会求值:
// 如果 `person` 或者 `person.department` 其中之一为空,都不会调用该函数:
person?.department?.head = managersPool.getManager()
1.5.3Elvis 操作符
当我们有一个可空的引用 b 时,我们可以说“如果 b 非空,我使用它;否则使用某个非空的值”:
val l: Int = if (b != null) b.length else -1
除了完整的 if-表达式,这还可以通过 Elvis 操作符表达,写作 ?:
val l = b?.length ?: -1
如果 ?: 左侧表达式非空,elvis 操作符就返回其左侧表达式,否则返回右侧表达式。 请注意,当且仅当左侧为空时,才会对右侧表达式求值。
请注意,因为 throw 和 return 在 Kotlin 中都是表达式,所以它们也可以用在 elvis 操作符右侧。这可能会非常方便,例如,检测函数参数:
fun foo(node: Node): String? {
val parent = node.getParent() ?: return null
val name = node.getName() ?: throw IllegalArgumentException("name expected")
// ……
}
1.5.4 !! 操作符
第三种选择是为 NPE 爱好者准备的:非空断言运算符(!!)将任何值转换为非空类型,若该值为空则抛出异常。我们可以写 b!! ,这会返回一个非空的 b 值 (例如:在我们例子中的 String)或者如果 b 为空,就会抛出一个 NPE 异常:
val l = b!!.length
因此,如果你想要一个 NPE,你可以得到它,但是你必须显式要求它,否则它不会不期而至。
1.5.5安全的类型转换
如果对象不是目标类型,那么常规类型转换可能会导致 ClassCastException。 另一个选择是使用安全的类型转换,如果尝试转换不成功则返回 null:
val aInt: Int? = a as? Int
1.5.6可空类型的集合
如果你有一个可空类型元素的集合,并且想要过滤非空元素,你可以使用 filterNotNull 来实现:
val nullableList: List<Int?> = listOf(1, 2, null, 4)
val intList: List<Int> = nullableList.filterNotNull()
1.6类型检测与自动类型转换
1.6.1.1 is 与 !is 操作符
我们可以在运行时通过使用 is 操作符或其否定形式 !is 来检测对象是否符合给定类型:
if (obj is String) {
print(obj.length)
}
if (obj !is String) { // 与 !(obj is String) 相同
print("Not a String")
}
else {
print(obj.length)
}
1.6.2 智能转换
在许多情况下,不需要在 Kotlin 中使用显式转换操作符,因为编译器跟踪不可变值的 is-检测以及显式转换,并在需要时自动插入(安全的)转换:
fun demo(x: Any) {
if (x is String) {
print(x.length) // x 自动转换为字符串
}
}
编译器足够聪明,能够知道如果反向检测导致返回那么该转换是安全的:
if (x !is String) return
print(x.length) // x 自动转换为字符串
或者在 && 和 || 的右侧:
// `||` 右侧的 x 自动转换为字符串
if (x !is String || x.length == 0) return
// `&&` 右侧的 x 自动转换为字符串
if (x is String && x.length > 0) {
print(x.length) // x 自动转换为字符串
}
1.7 For 循环
val items = listOf("apple", "banana", "kiwifruit")
for (item in items) {
println(item)
}
val items = listOf("apple", "banana", "kiwifruit")
for (index in items.indices) {
println("item at $index is ${items[index]}")
}
for (i in 1..4) print(i) // 输出“1234”
for (i in 4..1) print(i) // 什么都不输出
if (i in 1..10) { // 等同于 1 <= i && i <= 10
println(i)
}
// 使用 step 指定步长
for (i in 1..4 step 2) print(i) // 输出“13”
for (i in 4 downTo 1 step 2) print(i) // 输出“42”
// 使用 until 函数排除结束元素
for (i in 1 until 10) { // i in [1, 10) 排除了 10
println(i)
}
1.8 when 表达式
when 表达式取代了类Java 语言的 switch 语句。其最简单的形式如下:
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // 注意这个块
print("x is neither 1 nor 2")
}
}
如果 when 作为一个表达式使用,则必须有 else 分支, 除非编译器能够检测出所有的可能情况都已经覆盖了
如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔:
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
}
when 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:
when {
x.isOdd() -> print("x is odd")
y.isEven() -> print("y is even")
else -> print("x+y is odd.")
}
自 Kotlin 1.3 起,可以使用以下语法将 when 的主语(subject,译注:指 when 所判断的表达式)捕获到变量中:
fun Request.getBody() =
when (val response = executeRequest()) {
is Success -> response.body
is HttpError -> throw HttpException(response.status)
}
在 when 主语中引入的变量的作用域仅限于 when 主体。
1.9集合
1.9.1 集合转换
- 映射
映射 转换从另一个集合的元素上的函数结果创建一个集合。 基本的映射函数是 map()。 它将给定的 lambda 函数应用于每个后续元素,并返回 lambda 结果列表。 结果的顺序与元素的原始顺序相同。 如需应用还要用到元素索引作为参数的转换,请使用 mapIndexed()。
val numbers = setOf(1, 2, 3)
println(numbers.map { it * 3 })
println(numbers.mapIndexed { idx, value -> value * idx })
//[3, 6, 9]
//[0, 2, 6]
如果转换在某些元素上产生 null 值,则可以通过调用 mapNotNull() 函数取代 map() 或 mapIndexedNotNull() 取代 mapIndexed() 来从结果集中过滤掉 null 值。
val numbers = setOf(1, 2, 3)
println(numbers.mapNotNull { if ( it == 2) null else it * 3 })
println(numbers.mapIndexedNotNull { idx, value -> if (idx == 0) null else value * idx })
//[3, 9]
//[2, 6]
映射转换时,有两个选择:转换键,使值保持不变,反之亦然。 要将指定转换应用于键,请使用 mapKeys();反过来,mapValues() 转换值。 这两个函数都使用将映射条目作为参数的转换,因此可以操作其键与值。
fun main() {
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
println(numbersMap.mapKeys { it.key.toUpperCase() })
println(numbersMap.mapValues { it.value + it.key.length })
}
//{KEY1=1, KEY2=2, KEY3=3, KEY11=11}
//{key1=5, key2=6, key3=7, key11=16}
- 合拢
合拢 转换是根据两个集合中具有相同位置的元素构建配对。 在 Kotlin 标准库中,这是通过 zip() 扩展函数完成的。 在一个集合(或数组)上以另一个集合(或数组)作为参数调用时,zip() 返回 Pair 对象的列表(List)。 接收者集合的元素是这些配对中的第一个元素。 如果集合的大小不同,则 zip() 的结果为较小集合的大小;结果中不包含较大集合的后续元素。 zip() 也可以中缀形式调用 a zip b 。
fun main() {
val colors = listOf("red", "brown", "grey")
val animals = listOf("fox", "bear", "wolf")
println(colors zip animals)
val twoAnimals = listOf("fox", "bear")
println(colors.zip(twoAnimals))
}
// [(red, fox), (brown, bear), (grey, wolf)]
// [(red, fox), (brown, bear)]
当拥有 Pair 的 List 时,可以进行反向转换 unzipping——从这些键值对中构建两个列表:
第一个列表包含原始列表中每个 Pair 的键。
第二个列表包含原始列表中每个 Pair 的值。
要分割键值对列表,请调用 unzip()。
fun main() {
val numberPairs = listOf("one" to 1, "two" to 2, "three" to 3, "four" to 4)
println(numberPairs.unzip())
}
// ([one, two, three, four], [1, 2, 3, 4])
- 关联
联 转换允许从集合元素和与其关联的某些值构建 Map。 在不同的关联类型中,元素可以是关联 Map 中的键或值。
基本的关联函数 associateWith() 创建一个 Map,其中原始集合的元素是键,并通过给定的转换函数从中产生值。 如果两个元素相等,则仅最后一个保留在 Map 中。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers.associateWith { it.length })
}
// {one=3, two=3, three=5, four=4}
为了使用集合元素作为值来构建 Map,有一个函数 associateBy()。 它需要一个函数,该函数根据元素的值返回键。如果两个元素相等,则仅最后一个保留在 Map 中。 还可以使用值转换函数来调用 associateBy()。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers.associateBy { it.first().toUpperCase() })
println(numbers.associateBy(keySelector = { it.first().toUpperCase() }, valueTransform = { it.length }))
}
// {O=one, T=three, F=four}
// {O=3, T=5, F=4}
- 打平
如需操作嵌套的集合,则可能会发现提供对嵌套集合元素进行打平访问的标准库函数很有用。
第一个函数为 flatten()。可以在一个集合的集合(例如,一个 Set 组成的 List)上调用它。 该函数返回嵌套集合中的所有元素的一个 List。
fun main() {
val numberSets = listOf(setOf(1, 2, 3), setOf(4, 5, 6), setOf(1, 2))
println(numberSets.flatten())
}
// [1, 2, 3, 4, 5, 6, 1, 2]
另一个函数——flatMap() 提供了一种灵活的方式来处理嵌套的集合。 它需要一个函数将一个集合元素映射到另一个集合。 因此,flatMap() 返回单个列表其中包含所有元素的值。 所以,flatMap() 表现为 map()(以集合作为映射结果)与 flatten() 的连续调用。
data class StringContainer(val values: List<String>)
fun main() {
val containers = listOf(
StringContainer(listOf("one", "two", "three")),
StringContainer(listOf("four", "five", "six")),
StringContainer(listOf("seven", "eight"))
)
println(containers.flatMap { it.values })
}
// [one, two, three, four, five, six, seven, eight]
- 字符串表示
如果需要以可读格式检索集合内容,请使用将集合转换为字符串的函数:joinToString() 与 joinTo()。
joinToString() 根据提供的参数从集合元素构建单个 String。 joinTo() 执行相同的操作,但将结果附加到给定的 Appendable 对象。
当使用默认参数调用时,函数返回的结果类似于在集合上调用 toString():各元素的字符串表示形式以空格分隔而成的 String。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers)
println(numbers.joinToString())
val listString = StringBuffer("The list of numbers: ")
numbers.joinTo(listString)
println(listString)
}
// [one, two, three, four]
// one, two, three, four
// The list of numbers: one, two, three, four
要构建自定义字符串表示形式,可以在函数参数 separator、prefix 与 postfix中指定其参数。 结果字符串将以 prefix 开头,以 postfix 结尾。除最后一个元素外,separator 将位于每个元素之后。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers.joinToString(separator = " | ", prefix = "start: ", postfix = ": end"))
}
// start: one | two | three | four: end
对于较大的集合,可能需要指定 limit ——将包含在结果中元素的数量。 如果集合大小超出 limit,所有其他元素将被 truncated 参数的单个值替换。
fun main() {
val numbers = (1..100).toList()
println(numbers.joinToString(limit = 10, truncated = "<...>"))
}
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, <...>
最后,要自定义元素本身的表示形式,请提供 transform 函数。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers.joinToString { "Element: ${it.toUpperCase()}"})
}
// Element: ONE, Element: TWO, Element: THREE, Element: FOUR
1.9.2 过滤
- 按谓词过滤
基本的过滤函数是 filter()。当使用一个谓词来调用时,filter() 返回与其匹配的集合元素。对于 List 和 Set,过滤结果都是一个 List,对 Map 来说结果还是一个 Map。
fun main() {
val numbers = listOf("one", "two", "three", "four")
val longerThan3 = numbers.filter { it.length > 3 }
println(longerThan3)
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
val filteredMap = numbersMap.filter { (key, value) -> key.endsWith("1") && value > 10}
println(filteredMap)
}
// [three, four]
// {key11=11}
filter() 中的谓词只能检查元素的值。如果想在过滤中使用元素在集合中的位置,应该使用 filterIndexed()。它接受一个带有两个参数的谓词:元素的索引和元素的值。
如果想使用否定条件来过滤集合,请使用 filterNot()。它返回一个让谓词产生 false 的元素列表。
fun main() {
val numbers = listOf("one", "two", "three", "four")
val filteredIdx = numbers.filterIndexed { index, s -> (index != 0) && (s.length < 5) }
val filteredNot = numbers.filterNot { it.length <= 3 }
println(filteredIdx)
println(filteredNot)
}
// [two, four]
// [three, four]
还有一些函数能够通过过滤给定类型的元素来缩小元素的类型:
filterIsInstance() 返回给定类型的集合元素。在一个 List 上被调用时,filterIsInstance() 返回一个 List,从而让你能够在集合元素上调用 T 类型的函数。
fun main() {
val numbers = listOf(null, 1, "two", 3.0, "four")
println("All String elements in upper case:")
numbers.filterIsInstance<String>().forEach {
println(it.toUpperCase())
}
}
// All String elements in upper case:
// TWO
// FOUR
filterNotNull() 返回所有的非空元素。在一个 List<T?> 上被调用时,filterNotNull() 返回一个 List<T: Any>,从而让你能够将所有元素视为非空对象。
fun main() {
val numbers = listOf(null, "one", "two", null)
numbers.filterNotNull().forEach {
println(it.length) // 对可空的 String 来说长度不可用
}
}
// 3 3
- 划分
另一个过滤函数 – partition() – 通过一个谓词过滤集合并且将不匹配的元素存放在一个单独的列表中。因此,你得到一个 List 的 Pair 作为返回值:第一个列表包含与谓词匹配的元素并且第二个列表包含原始集合中的所有其他元素。
fun main() {
val numbers = listOf("one", "two", "three", "four")
val (match, rest) = numbers.partition { it.length > 3 }
println(match)
println(rest)
}
// [three, four]
// [one, two]
- 检验谓词
最后,有些函数只是针对集合元素简单地检测一个谓词:
如果至少有一个元素匹配给定谓词,那么 any() 返回 true。
如果没有元素与给定谓词匹配,那么 none() 返回 true。
如果所有元素都匹配给定谓词,那么 all() 返回 true。注意,在一个空集合上使用任何有效的谓词去调用 all() 都会返回 true 。这种行为在逻辑上被称为 vacuous truth。
fun main() {
val numbers = listOf("one", "two", "three", "four")
println(numbers.any { it.endsWith("e") })
println(numbers.none { it.endsWith("a") })
println(numbers.all { it.endsWith("e") })
println(emptyList<Int>().all { it > 5 }) // vacuous truth
}
//
true
true
false
true
any() 和 none() 也可以不带谓词使用:在这种情况下它们只是用来检查集合是否为空。 如果集合中有元素,any() 返回 true,否则返回 false;none() 则相反。
fun main() {
val numbers = listOf("one", "two", "three", "four")
val empty = emptyList<String>()
println(numbers.any())
println(empty.any())
println(numbers.none())
println(empty.none())
}
//
true
false
false
true
二、习惯用法
2.1创建 DTOs
data class Customer(val name: String, val email: String, var age: Int)
会为 Customer 类提供以下功能:
所有属性的 getters (对于 var 定义的还有 setters)
equals()
hashCode()
toString()
copy()
所有属性的 component1()、 component2()……等等
2.2函数的默认参数
fun foo(a: Int = 0, b: String = "") { …… }
2.3过滤 list
val positives = list.filter { x -> x > 0 }
// 或者可以更短:
val positives = list.filter { it > 0 }
2.4检测元素是否存在于集合中
if ("john@example.com" in emailsList) { …… }
if ("jane@example.com" !in emailsList) { …… }
2.5字符串内插
println("Name $name")
2.6类型判断
when (x) {
is Foo //-> ……
is Bar //-> ……
else //-> ……
}
2.7遍历 map/pair型list
k、v 可以改成任意名字。
for ((k, v) in map) {
println("$k -> $v")
}
2.8使用区间
for (i in 1..100) { …… } // 闭区间:包含 100
for (i in 1 until 100) { …… } // 半开区间:不包含 100
for (x in 2..10 step 2) { …… }
for (x in 10 downTo 1) { …… }
if (x in 1..10) { …… }
2.9只读 list
val list = listOf("a", "b", "c")
2.10只读 map
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
2.11访问 map
println(map["key"])
map["key"] = value
2.12延迟属性
val p: String by lazy {
// 计算该字符串
}
2.13扩展函数
fun String.toMD5(): String {
return try {
// 拿到一个MD5转换器(如果想要SHA1参数换成”SHA1”)
val messageDigest = MessageDigest.getInstance("MD5")
// 输入的字符串转换成字节数组
val inputByteArray: ByteArray = toByteArray()
// inputByteArray是输入字符串转换得到的字节数组
messageDigest.update(inputByteArray)
// 转换并返回结果,也是字节数组,包含16个元素
val resultByteArray = messageDigest.digest()
// 字符数组转换成字符串返回
ByteUtils.bytesToHex(resultByteArray)
} catch (e: NoSuchAlgorithmException) {
""
}
}
"你好啊".toMD5()
2.14创建单例
object Resource {
val name = "Name"
}
2.15 If not null 缩写
val files = File("Test").listFiles()
println(files?.size)
2.16 If not null and else 缩写
val files = File("Test").listFiles()
println(files?.size ?: "empty")
2.17 if not null 执行代码
val value = ……
value?.let {
…… // 代码会执行到此处, 假如data不为null
}
2.18 映射可空值(如果非空的话)
val value = ……
val mapped = value?.let { transformValue(it) } ?: defaultValue
// 如果该值或其转换结果为空,那么返回 defaultValue。
2.19 返回 when 表达式
fun transform(color: String): Int {
return when (color) {
"Red" -> 0
"Green" -> 1
"Blue" -> 2
else -> throw IllegalArgumentException("Invalid color param value")
}
}
2.20 对一个对象实例调用多个方法 (with)
class Turtle {
fun penDown()
fun penUp()
fun turn(degrees: Double)
fun forward(pixels: Double)
}
val myTurtle = Turtle()
with(myTurtle) { // 画一个 100 像素的正方形
penDown()
for (i in 1..4) {
forward(100.0)
turn(90.0)
}
penUp()
}
2.21 配置对象的属性(apply)
这对于配置未出现在对象构造函数中的属性非常有用。
val myRectangle = Rectangle().apply {
length = 4
breadth = 5
color = 0xFAFAFA
}
2.22 try with resources
use
val stream = Files.newInputStream(Paths.get("/some/file.txt"))
stream.buffered().reader().use { reader ->
println(reader.readText())
}
2.23 对于需要泛型信息的泛型函数的适宜形式
// 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)
2.24 交换两个变量
var a = 1
var b = 2
a = b.also { b = a }