Kotlin 内联函数的使用

Kotlin 一个强大之处就在于它的扩展函数,巧妙的运用这些扩展函数可以让你写出的代码更加优雅,阅读起来更加流畅。

内联函数

在写代码的时候难免会遇到这种情况,就是很多处的代码是一样的,于是通常会抽取出一个公共方法来进行调用,这样看起来就会很简洁;但是也出现了一个问题,就是这个方法会被频繁调用,就会很耗费资源。
举个例子:

fun <T> method(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        return body()
    } finally {
        lock.unlock()
    }
}

这里的 method() 方法在调用的时候是不会把形参传递给其他方法的,调用一下:

method(lock, { "我是body的方法体" })// lock是一个Lock对象

对于编译器来说,调用 method() 方法就要将参数 locklambda 表达式 {"我是body的方法体"} 进行传递,就要将 method() 方法进行压栈出栈处理,这个过程就会耗费资源。如果是很多地方调用,就会执行很多次,这样就非常消耗资源了,于是就引入了内联函数。

inline 关键字标记的函数就是内联函数,其原理就是:在编译时期,把调用这个函数的地方用这个函数的方法体进行替换。

标准函数

在 Kotlin 源码的 Standard.kt 文件中提供了一些很好用的内置高阶函数,可以帮助我们写出更优雅的 Kotlin 代码,提高生产力。

let()

定义

let() 函数的定义如下:

public inline fun <T, R> T.let(block: (T) -> R): R

功能

let() 函数的功能:定义一个变量在一个特定的作用域范围内使用,返回值为函数的最后一行或 return 表达式。

with()

定义

with() 函数的定义如下:

public inline fun <T, R> with(receiver: T, block: T.() -> R): R

功能

with() 函数的功能:使用给定的 receiver 作为接收器调用指定的函数 block,在函数内可以通过 this 指代该对象,返回值为函数的最后一行或 return 表达式。

示例

  1. 在自定义 View 中初始化画笔时很多时候会写下以下代码:

    var paint = Paint()
    paint.color = Color.BLACK
    paint.strokeWidth = 1.0f
    paint.textSize = 18.0f
    paint.isAntiAlias = true
    

    如果使用 with() 函数,那么就可以写成这样

    var paint = Paint()
    with(paint) {
     color = Color.BLACK
     strokeWidth = 1.0f
     textSize = 18.0f
     isAntiAlias = true
    }
    

    省去了 paint.,后书写起来感觉会更加自然。

  2. 在声明一些集合的场景,比如:

    var list = mutableListOf<String>()
    list.add("1")
    list.add("2")
    list.add("3")
    

    如果使用 with() 函数,那么就可以写成这样

    var list = with(mutableListOf<String>()) {
     add("1")
     add("2")
     add("3")
     this
    }
    

run()

定义

run() 函数的定义如下:

public inline fun <R> run(block: () -> R): R
public inline fun <T, R> T.run(block: T.() -> R): R

功能

run() 函数的功能:调用指定的函数 block,以闭包形式返回,返回值为函数的最后一行或 return 表达式。

示例

返回 return 表达式,return 后面的代码不再执行(注意写法 @run):

run {
    return@run println("11")
    println("22")
}

apply()

定义

apply() 函数的定义如下:

public inline fun <T> T.apply(block: T.() -> Unit): T

功能

apply() 函数的功能:使用 this 值作为接收器调用指定的函数 block,在函数范围内可以任意调用该对象的任意方法,返回值为 this 值(传入对象的本身)。

示例

  1. 在自定义 View 中初始化画笔时很多时候会写下以下代码:

    var paint = Paint()
    paint.color = Color.BLACK
    paint.strokeWidth = 1.0f
    paint.textSize = 18.0f
    paint.isAntiAlias = true
    

    如果使用 apply() 函数,那么就可以写成这样

    var paint = Paint().apply {
     color = Color.BLACK
     strokeWidth = 1.0f
     textSize = 18.0f
     isAntiAlias = true
    }
    
  2. 在声明一些集合的场景,比如:

    var list = mutableListOf<String>()
    list.add("1")
    list.add("2")
    list.add("3")
    

    如果使用 apply() 函数,那么就可以写成这样

    var list = mutableListOf<Int>().apply {
     add(1)
     add(2)
     add(3)
    }
    

注意 apply()run() 函数的区别,run() 函数返回的是最后一行,apply() 函数返回的是对象本身。由 apply() 函数的定义可以看出 apply() 函数适用于那些对象初始化需要给其属性赋值的情况。

also()

定义

also() 函数的定义如下:

public inline fun <T> T.also(block: (T) -> Unit): T

功能

also() 函数的功能:使用 this 值作为参数调用指定的函数 block,在函数范围内可以通过 it 指代该对象,返回值为 this 值(传入对象的本身)。

示例

在自定义 View 中初始化画笔时很多时候会写下以下代码:

var paint = Paint()
paint.color = Color.BLACK
paint.strokeWidth = 1.0f
paint.textSize = 18.0f
paint.isAntiAlias = true

如果使用 apply() 函数,那么就可以写成这样

var textView = Paint().also {
    it.color = Color.BLACK
    it.strokeWidth = 1.0f
    it.textSize = 18.0f
    it.isAntiAlias = true
}

功能

let() 函数的功能:使用 this 值作为参数调用指定的函数 block,在函数范围内可以通过 it 指代该对象,返回值为函数的最后一行或 return 表达式。

示例

let() 函数有点类似于 run() 函数,let() 函数在使用中可用于空安全验证,变量?.let{}

var paint: Paint?

paint?.let {
    it.color = Color.BLACK
    it.strokeWidth = 1.0f
    it.textSize = 18.0f
    it.isAntiAlias = true
}

takeIf()

定义

takeIf() 函数的定义如下:

public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T?

功能

takeIf() 函数的功能:使用 this 值作为参数判断是否满足给定的 predicate,如果满足则返回 this,否则返回 null

takeUnless()

定义

takeUnless() 函数的定义如下:

public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T?

功能

takeUnless() 函数的功能:使用 this 值作为参数判断是否满足给定的 predicate,如果不满足则返回 this,否则返回 null,和 takeIf() 函数正好相反。

repeat()

定义

repeat() 函数的定义如下:

public inline fun repeat(times: Int, action: (Int) -> Unit)

功能

repeat() 函数的功能:连续执行指定次数的指定函数 action,其实就是简化了 for 循环。

函数选择

Kotlin内联函数选择

函数区别

函数名 定义inline的结构 函数体内使用的对象 返回值 是否是扩展函数 适用的场景
let fun <T, R> T.let(block: (T) -> R): R = block(this) it指代当前对象 闭包形式返回 适用于处理不为null的操作场景
with fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block() this指代当前对象或者省略 闭包形式返回 适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上
run fun <T, R> T.run(block: T.() -> R): R = block() this指代当前对象或者省略 闭包形式返回 适用于let,with函数任何场景
apply fun T.apply(block: T.() -> Unit): T { block(); return this } this指代当前对象或者省略 返回this 适用于run函数的任何场景,一般用于初始化一个对象实例的时候,操作对象属性,并最终返回这个对象;动态inflate出一个XML的View的时候需要给View绑定数据也会用到;一般可用于多个扩展函数链式调用;数据model多层级包裹判空处理的问题
also fun T.also(block: (T) -> Unit): T { block(this); return this } it指代当前对象 返回this 适用于let函数的任何场景,一般可用于多个扩展函数链式调用

评论
 上一篇
Java ClassLoader 详解 Java ClassLoader 详解
Java 类加载器(Java ClassLoader)是 Java 运行时环境(Java Runtime Environment)的一部分,负责动态加载 Java 类到 Java 虚拟机的内存空间中。类通常是按需加载,即第一次使用该类时才加
2019-05-02
下一篇 
推荐十大经典排序算法 推荐十大经典排序算法
排序算法是《数据结构与算法》中最基本的算法之一。 排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。常见的内部排序算法有:插入排序、希
2019-04-27
  目录