欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Kotlin inline扩展函数

程序员文章站 2024-03-21 12:36:16
...

简介:
在Kotlin中的源码标准库(Standard.kt)中提供了一些Kotlin扩展的内置函数可以优化kotlin的编码。Standard.kt是Kotlin库的一部分,它定义了一些基本inline函数。在c/c++中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了inline修饰符,表示为内联函数。栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。

lambda表达式:
lambda表达式的本质其实是匿名函数,因为在其底层实现中还是通过匿名函数来实现的。但是我们在用的时候不必关心起底层实现。不过Lambda的出现确实是减少了代码量的编写,同时也是代码变得更加简洁明了。Lambda作为函数式编程的基础,其语法也是相当简单的。这里先通过一段简单的代码演示没让大家了解Lambda表达式的简洁之处

回调函数:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

回调函数

  • 回调接口可能有一到多个方法需要实现,下面看一下根据不同情况的不同写法
  • 在Kotlin中的实现一个接口通用写法,不管回调接口中有几个回调方法都可以用这种方式写
obj.addCallBack(object : CallBackInterface {
            override fun aa(v: XX) {
				//TODO something
            }
            override fun bb(v: YY) {
				//TODO something
            }
        })
  • 当且仅当回调接口只有一个回调方法的时候我们有更优雅的写法下面看一个例子
dataBinding.frameContainer?.setOnClickListener ({v: View ->
      //todo something
})
 //仅有一个参数并且是函数省略掉小括号
dataBinding.frameContainer?.setOnClickListener { it ->
      //todo something
 }
 //直接省略掉it
dataBinding.frameContainer?.setOnClickListener {
      //todo something
}
  • 回调函数简单介绍这么多,下面看一下kotlin的内联扩展函数

内联函数

上面已经介绍了什么叫做内联函数下面介绍结果常用的内联函数的用法以及他们之间的区别和相似之处

内联函数之 let 和 also

/**
 * Calls the specified function [block] with `this` value as its argument and returns its result.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#let).
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
    contract {this ContracBuilder)
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}
/**
 * Calls the specified function [block] with `this` value as its argument and returns `this` value.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#also).
 */
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
    contract {this ContracBuilder) 
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

看这两个inline函数的注释可以说几乎是相同的,这也是为什么我要把这个两个函数放在一起说的原因。相同部分大概意思简单说一下“以“this”值作为参数调用指定函数”区别在于let函数"returns its result"意思是返回函数块的返回值 “also"函数"returns this value” 这个就直接了直接返回了”this“这就是他们的相同之处和区别

let 和 also 的简单使用

//在let 和 also 中it代表 obj
obj?.let {
	   it.setXxx(1)
	   it.setYyy(2)
	   it.addObj(obj)
}
//?用来判空,如果obj为空直接返回空,如果不为空则执行函数并返回函数的执行结果,其实return block(this) 
//的意思就是把block函数块看作一个整体,执行bolck执行到最后如果有返回值就返回,如果没有就是最后一行
obj?.also {
      it["type1"] = "1"
      it["type2"] = "2"
      it["type3"] = "3"
}
//用法基本相同,返回it

内联函数之 apply 和 run 上源码

/**
 * Calls the specified function [block] with `this` value as its receiver and returns `this` value.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#apply).
 */
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}
/**
 * Calls the specified function [block] with `this` value as its receiver and returns its result.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#run).
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}
/**
 * Calls the specified function [block] and returns its result.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#run).
 */
@kotlin.internal.InlineOnly
public inline fun <R> run(block: () -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

你肯定已经注意到有两个run函数 这连个run函数的区别在于第一个比第二个多了一个泛型T先说这两个run函数的区别,还是看注释,以后要养成习惯看代码先看注释,看完注释心里对接下来要看的东西有个大概的概念,接下来才能有的放矢。with this value as its receiver是注释当中最大的区别这里,什么意思呢,我的理解是调用接收对象的值,这句话就是说在函数中可以调用泛型T的值,而另一个run函数没有接收方对象,那肯定就无法调用他的值了。仅仅是执行block然后返回block的值。
接下来对比带泛型T的runapply
从源码结构上看apply和run很像,他们的不同之处在于run返回block执行的结果,而apply返回的是传入的这个对象本身

apply 和 run(带泛型) run的简单使用

 val userInfoMap = hashMapOf<Int ,String>().apply{
            put(0 , "1")
            put(1 , "2")
            put(2 , "3")
            put(3 , "4")
        }
//apply 这里返回的是创建的这个HashMap本身

val service = MultiportService("https://example.kotlinlang.org", 80)
val result = service.run {
//	it.port = 8080
//	it.query(it.prepareRequest() + " to port ${it.port}")
// 这里的it代表的就是是service
    port = 8080
    query(prepareRequest() + " to port $port")
}
//run 返回 query(prepareRequest() + " to port $port")结果
//看着是不是有点儿眼熟,类似于let ,只是省略掉了it,功能是完全相同的

val letResult = service.let {
    it.port = 8080
    it.query(it.prepareRequest() + " to port ${it.port}")
}
//let 返回 query(prepareRequest() + " to port $port")结果

//另一种run不含receiver 返回赋值给变量
val hexNumberRegex = run {
    val digits = "0-9"
    val hexDigits = "A-Fa-f"
    val sign = "+-"
    Regex("[$sign]?[$digits$hexDigits]+")
}
for (match in hexNumberRegex.findAll("+1234 -FFFF not-a-number")) {
    println(match.value)
}
//这个用起来比较简单

内联函数之 takeIf 和 takeUnless 源码

/**
 * 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
}

/**
 * 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
}

老方法,看源码的注释 takeIf注释的大概意思就是条件成立返回对象本身,条件不成立返回空,我的理解takeIf就是一个变异的 if takeUnless跟takeIf相反。这两个方法用处不大

takeIf 和 takeUnless的简单使用

val number = Random.nextInt(100)
val evenOrNull = number.takeIf { it % 2 == 0 }
val oddOrNull = number.takeUnless { it % 2 == 0 }
println("number: $number")
println("result: $evenOrNull, odd: $oddOrNull")
//结果
number: 43
result: null, odd: 43
//这两个函数需要注意的一个点 用他们返回值继续操作的时候注意判空,很明显他们的返回值可能是空的
val str = "Hello"
val caps = str.takeIf { it.isNotEmpty() }?.toUpperCase()
//val caps = str.takeIf { it.isNotEmpty() }.toUpperCase() //编译通不过
println(caps)

内联函数之 with 源码

/**
 * Calls the specified function [block] with the given [receiver] as its receiver and returns its result.
 *
 * For detailed usage information see the documentation for [scope functions](https://kotlinlang.org/docs/reference/scope-functions.html#with).
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

看注释 分为两部分,一个是 接收者,一个是函数,返回值结果是函数执行的结果。它不是以扩展的形式存在的。它是将某对象作为函数的参数,在函数块内可以通过 this 指代该对象。返回值为函数块的最后一行或指定return表达式

相关标签: kotlin