欢迎前往我的CSDN
Kotlin提供了一个系统库,是Java库的增强,很多函数适配了Java的类型和方法,同时使用Kotlin的语法,下面我们来研究下广泛使用的函数。
Apply
- apply 是 Any 的扩展函数, 因而所有类型都能调用。
- apply 接受一个lambda表达式作为参数,并在apply调用时立即执行,apply返回原来的对象。
- apply 主要作用是将多个初始化代码链式操作,提高代码可读性。
参考以下实例我们来分析下:
fun main(args: Array<String>) {
val runnable = Runnable {
print("is running")
}
val thread = Thread(runnable)
thread.isDaemon = true
thread.start()
}
我们用标准库函数Apply来改写代码:
fun main(args: Array<String>) {
val runnable = Runnable {
print("is running")
}
Thread(runnable).apply { isDaemon = true }.start()
}
看看代码是不是减少了不少啊,我们创建了一个Runnable实例,然后创建了一个线程运行这个实例,在闭包
中我们配置线程实例为一个守护线程。闭包中的代码会在线程实例上运行,并且可以直接在返回值上调用start()方法。
let
let默认当前这个对象作为闭包的it参数,返回值是函数的最后一行或者指定return结果。
let 和 apply 类似, 唯一的不同是返回值,let返回的不是原来的对象,而是闭包里面的值。
fun main(args: Array<String>) {
println(testLet())
}
fun testLet():Int{
"Alfred".let {
println(it)
println(it.length)
return 999 //指定返回值为999
}
}
结果:
Alfred
6
999
fun main(args: Array<String>) {
val name = "Alfred".let {
println(it)
println(it.length)
"张三" //没指定返回值时,默认返回值为闭包的最后一行
}
println(name)
}
结果:
Alfred
6
张三
with
有时候我们在执行对象的多个方法时,必须要写很多次对象名,显得很啰嗦。比如:
class Persion(val name: String, val age: Int, val sex: String) {
fun showName() {
println(name)
}
fun showAge() {
println(age)
}
fun showSex() {
println(sex)
}
}
fun main(args: Array<String>) {
val persion = Persion("alfred",29,"男")
persion.showName()
persion.showSex()
persion.showAge()
}
执行结果:
alfred
男
29
persion这个实例名是不是重复写了很多遍啊。虽然很整齐啊,但是不推崇。
下面我们使用with来简化下代码:
fun main(args: Array<String>) {
val persion = Persion("alfred",29,"男")
with(persion){
showName()
showSex()
showAge()
}
}
看看是不是相对来说简化了,也挺清晰易懂的。
run
run函数跟apply函数很像,只不过apply返回当前自身对象,而run返回最后一行或指定值,run 就是with与let组合。
fun main(args: Array<String>) {
val name = "Alfred".run {
println(this)
println(this.length)
"张三" //返回最后一行
}
println(name)
println(testRun())
}
fun testRun():String{
"Alfred".run {
return "jack" //指定返回
}
}
结果:
Alfred
6
张三
jack
lazy
lazy是另一个很有用的函数,可以把非常耗费资源的操作延迟到第一次调用时加载。比如:
fun main(args: Array<String>) {
val name = lazy {
for (i in 1..10) {
print("# ")
}
"alfred"
}
println(name.value)
println(name.value)
}
结果:
# # # # # # # # # # alfred
alfred
第一次调用println(name.value)时,才会初始化name,先执行for循环打印10次#,再初始化值为alfred。第二次调用println(name.value)时,name已经赋值为alfred,所以直接打印alfred。
总结: 第一次调用时,lazy 闭包会调用。一般用在单例模式。
use
use与try语句类似,实现了Closeable接口的对象可调用use函数,use函数会自动关闭调用者(无论中间是否出现异常)。下面我们看下源码:
/**
* Executes the given [block] function on this resource and then closes it down correctly whether an exception
* is thrown or not.
*
* @param block a function to process this [Closeable] resource.
* @return the result of [block] function invoked on this resource.
*/
@InlineOnly
@RequireKotlin("1.2", versionKind = RequireKotlinVersionKind.COMPILER_VERSION, message = "Requires newer compiler version to be inlined correctly.")
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
when {
apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
this == null -> {}
exception == null -> close()
else ->
try {
close()
} catch (closeException: Throwable) {
// cause.addSuppressed(closeException) // ignored here
}
}
}
}
可以看出,use函数内部实现也是通过try-catch-finally块捕捉的方式,所以不用担心会有异常抛出导致程序退出。
close操作在finally里面执行,所以无论是正常结束还是出现异常,都能正确关闭。
看个例子:
fun main(args: Array<String>) {
val io = Files.newInputStream(Paths.get("/data/home/alfred/TestKotlin/.idea/encodings.xml"))
val info = io.use {
it.bufferedReader().readLines()
}
println(info)
}
结果:
[<?xml version="1.0" encoding="UTF-8"?>, <project version="4">, <component name="Encoding">, <file url="PROJECT" charset="UTF-8" />, </component>, </project>]
use 无论如何都会将 io close, 避免了写复杂的 try-catch-finally 代码。Kotlin的IO流操作变得行云流水。
repeat
顾名思义,repeat 接受函数和整数作为参数,函数会被调用 指定 次,这个函数避免写循环。
还记得lazy初始化那个打印#的例子吗?我们把for循环改写下:
fun main(args: Array<String>) {
val name = lazy {
repeat(10) { print("# ") }
"alfred"
}
println(name.value)
println(name.value)
}
结果:
# # # # # # # # # # alfred
alfred
简单吧!! 语法可有抽象为:
repeat(次数) {
重复语句
}
require/assert/check
- require负责检查输入的参数,如果有问题,抛出IllegalArgumentException
- check负责检查自身是否万事俱备可以执行了,如果不是,抛出IllegalStateException
- require + check就是在做前置条件的检查,通过了才可以执行真正的程序逻辑
- assert负责确保程序执行完毕后的结果/内部状态是否符合预期,如果不是,抛出AssertionError
此处只列出相关知识点,有兴趣的话可以参考如下博客,写得非常详尽。
用好 Require,check,assert,写好 Kotlin 代码