Kotlin---标准扩展函数

介绍

除了自定义扩展之外,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)
}
  1. 函数定义:
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中,所有的东西都是对象,所以代码块也是一个对象,可以使用变量引用。

  1. 函数体:
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
        }
    }
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券