数字类型
和 java 一样,Kotlin 中所有数字类型都是有符号的,也就是说既可以表示正数,也可以表示负数。
安全转换函数
与 java 不一样,kotlin 提供了 toDoubleOrNull 和 toIntOrNull 这样的安全转换函数,如果数值不能正确转换,与其触发异常不如干脆返回 null 值。
示例:toIntOrNull。因为可能会返回空,所有 number 是可空类型。
fun main() {
// "8.98".toInt() 会抛异常
//val number : Int = "8.98".toInt()
val number : Int? = "8.98".toIntOrNull() // TODO 当没有转换成功,则直接返回 null
println(number)
}
** Double 转 Int 与类型格式化**
Double.toInt() 会直接丢弃小数点后的数值,损失了精度。Double.roundToInt() 是四舍五入的方式。
import kotlin.math.roundToInt
fun main() {
println(8.964565.toInt()) // 直接丢弃小数点后的值
println(8.964565.roundToInt()) //四舍五入
}
Double 类型格式化。格式化字符串是一串特殊字符,它决定该如何格式化数据。
示例:
fun main() {
val str = "%.2f".format(0.2694489)
println(str)
}
解释说明:%.2f 即保留2位小数,并且是四舍五入。返回的结果是一个 String。
标准库函数
- apply()
apply 函数可看作一个配置函数,你可以传入一个接收者,然后调用一系列函数来配置它以便使用,如果提供 lambda 给 apply 函数执行,它会返回配置好的接收者。
解释说明:file1 是我们的通常写法,创建一个 File 对象,然后用这个对象来调用 read/write 等方法。file2 的写法就是通过配置函数的方式。将 read/write/execute 等方法配置在 apply() 函数中,然后返回给 file2 对象。将 File 对象传入到 apply 中(即那个 this),然后配置它。配置完之后再把这个 File 对象返回,即 file2(file2 就是接收者对象)。
可以看到,调用一个函数类配置接收者时,变量名就省掉了(即省掉 file1.setReadable() 中的 file1,而是直接调用 setReadable())。这是因为,在 lambda 表达式里,apply 能让每个配置函数都作用于接收者,这种行为有时又叫做相关作用域,因为 lambda 表达式里的所有函数调用都是针对接收者的,或者说,它们是针对接收者的隐式调用。
- let()
let 函数能使某个变量作用于其 lambda 表达式里,让 it 关键字能引用它。let 与 apply 比较,let 会把接收者传给 lambda,而 apply 什么都不传,匿名函数执行完,apply 会返回当前接收者,而 let 会返回 lambda 的最后一行。
示例:求一个集合里第一个数的平方
解释说明:这个 it 就是 first() 获取的集合中的第一个元素,并且把它传入到了 lambda 表达式中。
如果不使用 let() 函数,那么计算一个数的平方,我们可能会这样写:
所以,使用 let 函数会方便很多。
- run()
光看作用域行为,run 和 apply 差不多,但与 apply 不同,run 函数不返回接收者,run 返回的是 lambda 执行结果。
val file = File("D://run.txt")
val txt : Boolean = file.run {
readText().contains("xxx")
}
解释说明:readText() 读取 run.txt 文件的内容,通过 contains() 查看是否包含 "xxx",如果包含则返回 true,不包含则返回 false。
run() 也能用来执行函数引用
fun main() {
val isLong = "The People's Republic of China".run(::isLong)
println(isLong)
}
fun isLong(name : String) : Boolean{
return name.length >= 10
}
同时,支持多个函数链式调用。
示例:
fun main() {
val isLong = "The People's Republic of China".run(::isLong)
println(isLong)
"The People's Republic of China"
.run {::isLong}
.run {::showMessage}
.run {::printMes}
}
fun isLong(name : String) : Boolean{
return name.length >= 10
}
fun showMessage(isLong : Boolean) : String{
return if(isLong){
"Name is too long!"
}else{
"Please rename!"
}
}
fun printMes(Message : String){
println(Message)
}
解释说明:isLong() 的结果(Boolean)作为参数传入 showMeage() 中,showMessage() 中然后的结果(String)又作为参数传入 printMes() 中。
- with()
with 函数是 run 函数的变体,他们的功能行为是一样的,但 with 的调用方式不同,调用 with 时需要值参作为其第一个参数传入。
- also()
also 函数和 let 函数功能相似,和 let 一样,also 也是把接收者作为值参传给 lambda(即那个 it),但有一点不同:also 返回接收者对象,而 let 返回 lambda 结果。因为这个差异,also 尤其适合针对同一原始对象,利用副作用做事,既然 also 返回的是接收者对象,你就可以基于原始接收者对象执行额外的链式调用。
示例:
fun main() {
var fileContexts : List<String>
File("D://also.txt")
.also {
// 先打印 文件名称
println(it.name)
}
.also {
// 再把文件里的元素一行一行的读出来,赋值给 fileContexts
fileContexts = it.readLines()
}
}
解释说明:第一个 also 打印 名字,然后返回 File 对象,所以第二个 also 也是同一个 File 对象再调用。
- takeIf()
和其它标准函数有点不一样,takeIf 函数需要判断 lambda 中提供的条件表达式,给出 true 或 false 结果,如果判断结果为 true,从 takeIf 函数返回接收者对象,如果是 false,则返回 null。如果需要判断某个条件是否满足,再决定是否可以赋值变量或执行某项任务,takeIf 就非常有用。概念上讲,takeIf 函数类似于 if 语句,但它的优势是可以直接在对象实例上调用,避免了临时变量赋值的麻烦。
实例:
val readText = File("D://takeIf.txt")
.takeIf { it.exists() && it.canRead() }
?.readText()
解释说明:当 {it.exists() && it.canRead()}(匿名函数) 结果为 true, 那么这一句 takeIf { it.exists() && it.canRead() } 返回接收者对象(即File),然后再调用 readText();如果{it.exists() && it.canRead()}的返回结果为 false,那么 takeIf() 返回 null。所以这里调用 readText() 函数时用到了安全调用操作符?.。
注意:匿名函数{it.exists() && it.canRead()}是作为了 takeIf() 的参数,这里用的是简略写法。
- takeUnless()
takeIf 辅助函数 takeUnless,只有判断你给定的条件结果是 false 时,takeUnless 才会返回原始接收者对象。
版权归原作者 别偷我的猪_09 所有, 如有侵权,请联系我们删除。