介绍
除了自定义扩展之外,Kotlin中也定义了很多的扩展函数,而这些扩展函数的接收类型是范型,也就是所有对象都可以使用。这些标准的扩展函数都放在了Standard.kt
中。
从Kotlin的语言介绍中,可以知道,Kotlin在空指针以及null对象的控制、语句表达上有很多优势,很多优势也来源于Kotlin的扩展函数的支持。
接下来会介绍:
- let
- apply
- run
- takeIf
- takeUnless
- with
也会看看,这些函数是如何让空对象的代码更加优雅的表达。
let函数
首先来详细介绍一下let函数的定义,以及它是如何运行的。
/**
* Calls the specified function [block] with `this` value as its argument and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
- 函数定义:
public inline fun <T, R> T.let(block: (T) -> R): R {
- 使用
inline
关键字来标志这个函数是一个内联函数 -
<T,R>
代表函数参数范型 -
T.let
代表它是一个扩展函数,而接收参数是一个泛型 -
block:(T)->R
代表函数的参数是一个代码块,而这个代码块接收参数T,并且返回R类型的对象 -
:R
代表整个let
函数返回类型是R类型
其中很重要的一个概念是:Kotlin中,所有的东西都是对象
,所以代码块也是一个对象,可以使用变量引用。
- 函数体:
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
-
contract
代码块:主要为了告诉编译器,Lambda表达式会马上执行,并且只执行一次,因为在编译器编译的时候会对某些变量判空时,判断是否可以编译通过并且运行。 -
block(this)
:执行代码块中的代码,并且将调用扩展函数的对象作为参数传入,返回结果
let函数举例
例如下面的函数,如果student
不为空的话,则会打印名字和年龄。
fun acceptStudent(student: Person?) {
student?.let {
println("Student Name:${it.mName}")
println("Student Age:${it.mAge}")
}
}
而如果这段代码用Java写的话,则是
if(student!=null) {
println("student name:"+student.mName);
println("student age:"+student.mAge)
}
apply函数
apply函数的定义如下:
/**
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*/
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
可以看到它和let
函数差不多,只有一些区别:
-
block
代码块中没有参数,仅仅只执行代码块的代码,但是可以使用this
关键字来指向本对象 - 函数的返回值是
this
,而block
代码块没有返回值
因为它返回的this,也可以配合let
扩展函数来使用
fun acceptStudent(student: Person?) {
student?.apply {
println("Apply Name...${this.mName}")
}.let {
println("let Age...${it?.mAge}")
}
}
run函数
run函数的原型是:
/**
* Calls the specified function [block] with `this` value as its receiver and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
可以看到,这个函数会执行代码块中的代码,并且将代码块执行的结果作为返回值返回。
例如:
在gardenPlantTest
函数中,在run
代码块中打印完字符串plant
,如果plant
不为空返回字符串长度,否则返回0,而result
中保存的也就是代码块中返回的字符串长度
class GardenPlant {
lateinit var plant: String;
fun gardenPlantTest() {
var result = plant.run {
println("Plant String...$this")
plant?.length
}
println("Plant Length:$result")
}
}
takeIf函数
takeIf
的函数原型:
将扩展对象T
作为参数,执行predicate
代码块的代码,如果在代码块中返回true,则返回对象T
,如果返回false,则返回null
/**
* Returns `this` value if it satisfies the given [predicate] or `null`, if it doesn't.
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (predicate(this)) this else null
}
例如:
如果plant
字符串大于10,就可以采用,如果不行的话,则返回null,下面的代码块中只是表示可以在代码块中使用if
等条件判断语句,只要最后返回的结果是boolean
值就可以。
class GardenPlant {
lateinit var plant: String;
fun gardenPlantTest() {
val filetrPlant = plant.takeIf {
if (plant.length > 10) {
plant.length > 10
} else {
plant.length < 10
}
}
println("Plant String Length >9 :$filetrPlant")
}
}
在代码块中返回值,不能使用
return
关键字,因为return
关键字会返回整个函数,而不是代码块
takeUnless函数
takeUnless
函数原型如下:
与takeIf
不一样的是,返回的取值取反,来进行判断是否采用。
/**
* Returns `this` value if it _does not_ satisfy the given [predicate] or `null`, if it does.
*/
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (!predicate(this)) this else null
}
with函数
with
函数不是一个扩展函数,它的原型如下:
这个函数主要会接收一个对象,然后调用该对象的扩展代码块,然后返回代码块中的值。
/**
* Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
*/
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
例如:
在plant
对象中替换完字符后,返回length
,并且赋值给plantLength
class GardenPlant {
lateinit var plant: String;
fun gardenPlantTest() {
var plantLength = with(plant) {
replace('w', 'r')
length
}
}
}
网友评论