Kotlin——高阶函数

本文深入探讨了高阶函数的概念,包括其定义、函数类型定义以及常见用途,如apply函数的应用实例和自定义apply函数的实现。此外,还讲解了内联函数如何减少内存开销。通过实例演示,帮助读者理解并熟练运用这些高级编程技术。
摘要由CSDN通过智能技术生成

1. 高阶函数

1.1 高阶函数定义

高阶函数是将函数用作参数或返回值的函数。
如果一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数,那么该函数称为高阶函数

1.2 函数类型定义

函数类型定义的基本规则:

	/* methodName函数名
	 * (Int,String) 函数的参数类型
	 * Unit 函数返回值 === void
	 */
	methodName : (Int,String) -> Unit

将函数添加到另外一个函数的参数中:

	fun test(block : (String , Int) -> Unit) {
    	block("test",123)
	}

这样test函数就是一个高阶函数了,调用的时候:

	test { name, age ->
        println("name:$name,age:$age")
    }
    // 打印结果
    name:test,age:123

其中,test后的大括号括起来的内容,都是block函数的实现
lambda
再举一个带参数的例子:

// 最后一个Unit代表test2函数的返回值,可以是Int,Stirng等等,这样就需要再方法中实现返回值
fun test2(string: String, example: (String) -> Unit): Unit {
    example(string)// 将string传递给example作为参数
}

// 调用
test2("TEST") {
	// it是lambda隐藏了参数名,也可以自定义参数名,一般默认不写就是it
    println("it : $it")
}

// 自己写了参数名
test2("TEST2") {name->
	println("name : $name")
}

// 打印结果
it : TEST
name : TEST2

1.3 高阶函数的用途

高阶函数允许让函数类型参数决定函数的执行逻辑,即使同一个高阶函数,传入的函数类型参数不同(也可以相同,主要取决于高阶函数的具体实现也就是{}中的代码),那么函数的执行逻辑和返回结果可能也是完全不同的。

带参数和返回值的高阶函数(传入相同参数,不同实现方法):

fun test3(a: Int, b: Int, num: (Int, Int) -> Int): Int {
    return num(a, b)
}

// 调用,在{}中的实现方法可以自己写,可以两数相加,也可以相乘,相除,相减等等
val addNum = test3(1, 2) { a, b ->
	a + b
}
val rideNum = test3(1, 2) { a, b ->
	println("函数作为参数的实现方法:a : $a , b : $b")
    a * b // lambda最后一行作为高阶函数的返回值返回
}
// ::addNum 这是一种函数引用的写法,表示将函数addNum()来作为参数传递给高阶函数
val addNum2 = test3(1, 2, ::addNum)

fun addNum(a: Int, b: Int): Int {
    return a + b
}

println("addNum : $addNum")
println("rideNum : $rideNum")
println("addNum2 : $addNum2")

// 打印结果
addNum : 3
rideNum : 2
addNum2 : 3

其中出现了一种新的写法::addNum,这是一种函数引用写法,表示将函数addNum() 作为参数传递给高阶函数。
如果函数在另外一个类中定义,假如在Test类中,有addNum()方法,则需要先创建类的对象,然后用test::addNum作为高阶函数的参数

val test : Test = Test()
test3(1, 2, test::addNum())

rideNum中可以看到,Lambda表达式的最后一行代码的返回值作为函数的返回值返回。

2. 高阶函数高级用法

2.1 apply函数

apply函数扩展了所有的泛型对象,在闭包范围内可以任意调用该对象的任意方法,并在最后返回该对象。

主要的作用:是可以用来简化初始化对象的功能
特别需要注意的是apply函数中表示对象本身使用的是this关键字而不是it

	val stringBuilder = StringBuilder("000")
	str
    // apply 方法返回this,也就是 StringBuilder,即常量 s 是 StringBuilder类型
    val s = stringBuilder.apply {
        append("aaa")// === stringBuilder.append("aaa") 省略了this
        append("bbb")
    }
    println("S == $s")
     
    // 打印结果
    S == 000aaabbb

我们来看一下apply函数的源码:
意思是,为泛型T增加扩展函数apply,返回值为泛型本身类型,使用 T.() 表示在T类中定义的函数类型,那么在传入Lambda表达式时将会自动拥有T的上下文 this

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

2.2 写一个自己的apply函数

StringBuilder增加扩展函数myApply,扩展函数接收一个函数类型的参数,并且返回值为StringBuilder

这里和前面的函数作为参数有不一样的地方,即在括号前加了StringBuilder.,其实这才是完整的函数类型的定义规则,加上T.表示在哪个类中定义函数类型。使用StringBuilder.表示在StringBuilder类中定义的函数类型,那么在传入Lambda表达式时将会自动拥有StringBuilder的上下文this。

// 为StringBuilder增加扩展函数myApply
fun StringBuilder.myApply(block: StringBuilder.() -> Unit): StringBuilder {
    block()
    return this// this 就代表.()前面的StringBuilder
}

// 调用
val s2 = stringBuilder.myApply {
	this.append("ccc") // === append("ccc")
}
println("s2 == $s2") // 000aaabbbccc

图中框起来的部分this:StirngBuilder,即为自动拥有的上下文,也就是stringBuilder常量(此处有个问题,stringBuilder定义的是val常量,为什么可以通过append修改?查了一下和基本数据类型与引用数据类型有关,后续研究)
类型
自己定义的方法,因为写死了StringBuilder,可以替换成泛型,就和源码一样了,不过此处少了inline修饰

fun <T> T.myApply2(block: T.() -> Unit): T {
    block()
    return this
}

3. inline内联函数

内联函数的原理也很简单:Kotlin编译器在编译时把内联函数内代码自动替换到要调用的地方,这样就解决了运行时的内存开销

  1. 第一步,将Lambda表达式的代码替换到函数类型参数调用的地方
    步骤一

  2. 第二步,将内联函数中全部代码替换到函数调用的地方,替换后即为

val rideNum = a + b

步骤二

正是如此内联函数才能完全消除Lambda表达式运行时带来的额外内存开销

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值