汇总一些杂七杂八的点,不值得单独开一篇整理了。
主要内容包括
- 解构声明
- 生成数列的
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 |