汇总一些杂七杂八的点,不值得单独开一篇整理了。
主要内容包括
- 解构声明
- 生成数列的
Range - 类型转换
- 异常处理等
解构声明
我们创建对象时通常会使用构造函数,他是将一些变量,构造成一个对象,解构就是构造的反操作,解构声明(Destructuring Declaration) 指的是可以将一个对象解构成多个变量。
对象的解构主要依赖于类的 componentN() 函数,函数需要使用 operator 注解标记,这不是一种强制实现,更像是一种约定声明。
之前介绍数据类时说道,数据类中会默认实现 componentN() 函数,因此我们对数据类对象可以直接进行如下操作:
1 | data class DataUser(val name: String, val age: Int) |
借助数据类的这种特性,我们可以从函数中返回多个值,其实本质上,还是借助了数据类可以自动解构的特性。
ps:Pair 类也是数据类。
1 | // 从函数中返回多个值 |
那么对于我们自己创建的类,他不是数据类,如何进行解构,解决方法还是声明 componentN() 函数。
在 componentN() 函数中返回指定的属性,会自动匹配到解构的接受者中,name1 对应的 component1() 的返回值,age1 对应 component2() 返回值。
1 | class MyUser(val name: String, val age: Int) { |
借助 Map 类的遍历,看一个解构声明的具体应用。
1 | for ((key, value) in map) { |
要想实现遍历操作,首先需要实现 iterator
1 | operator fun <K, V> Map<K, V>.iterator(): Iterator<Map.Entry<K, V>> = entrySet().iterator() |
返回一个 Map.Entry<K, V> 的集合,此时需要将 Map.Entry 的 key 和 value 进行解构声明。
1 | operator fun <K, V> Map.Entry<K, V>.component1() = getKey() |
Range 数列
可以使用 .. 运算符生成一个数列,对于 Char,Int,Long 类型数列由 CharProgression,IntProgression,LongProgression 实现,而 Float 和 Double 类型由 ClosedFloatingPointRange<T> 实现,因此在支持性上有所不同。
使用简单的 .. 运算符生成 IntRange 对象
使用运算符 a..b 生成的数列的范围是 [a,b]
1 | val range = 1..10 |
遍历数列,构造数列时,后面的范围要比前面的大,所以下面 4..1 是不会有任何结果的。
1 | for (i in 1..4) { |
使用 .. 并不能构造一个递减数列,此时需要使用 downTo() 函数,构造递减数列。
1 | for (i in 4 downTo 1) { |
使用 step() 函数指定步长,表示在构造数列时的间隔,默认是 1。
1 | // 步长 |
输出结果为,每隔两个会获取一个,所以最后一个数将会是 3 ,因为下一个数是 5,不在范围内,类似的 (1..4 step 2).last 的结果也会是 3。
1 | testRange: test 1..4 step 2 i = 1 |
使用 until 函数构造一个前闭后开的区间,比如下面的代码将会创建一个 [1,4) 区间的数列。
1 | for (i in 1 until 4) { |
反转数列
1 | 1.rangeTo(10).reversed() |
所有的基本类型在生成数列的实现方式不同,但是都支持 .. 运算符生成数列,另外 Byte,Short,Int,Long 在生成数列时都是可以自动转换的。
1 | // byte |
运算符 .. 对应的 rangeTo() 函数,其中 Float 和 Double 的 rangeTo() 函数使用扩展函数,其他为成员函数。
1 | // byte |
函数 downTo(),Float 和 Double 不支持 downTo()
1 | // byte |
函数 step(),Float 和 Double 不支持 step()
1 | // byte |
类型转换
使用 is 关键字进行类型的检测,使用 as 关键字进行类型转换。
1 | val str = "100" |
类型自动转换
当使用 is 关键字进行了类型判断,那么一定作用域内,类型将被自动转换。
比如在判断结构当中
1 | val myNum: Any = 100 |
在 || 和 && 因为遵循短路判断,因此类型也将会被自动转换。
1 | if (myNum is String && myNum.length > 10) { |
在 when 结构中进行了类型判断
1 | when (myNum) { |
并不是所有的属性都可以进行自动的类型转换,需要满足以下条件:
局部的
val变量 - 永远有效 。
val属性 - 如果属性是private或internal的,或者类型检查处理与属性定义出现在同一个模块内,那么智能类型转换是有效的。对于open属性, 或存在自定义get方法的属性, 智能类型转换是无效的。局部的
var变量 - 如果在类型检查语句与变量使用语句之间,变量没有被改变, 而且它没有被Lambda表达式捕获并在Lambda表达式内修改它,那么智能类型转换是有效的。
var属性 - 永远无效(因为其他代码随时可能改变变量值)。
异常处理
在 Kotlin 中与 Java 大致相同,有几个特别的地方。
try{} 可以作为一个表达式,并返回值。
1 | val result = try { |
不支持函数声明时抛出异常,这项改进主要是避免代码中出现大量的异常捕捉代码块。
在 Java 中可以在方法声明时抛出异常,来告知外界使用该函数可能会有异常
1 | public static void test() throws IllegalStateException{ |
在 Kotlin 中这种语法是不支持的。
1 | // error |