kotlin 高阶函数、内联函数

本文探讨了Kotlin中的高阶函数概念,它接受函数作为参数或返回函数。通过示例展示了高阶函数的基本使用,并指出其在Java中的实现原理。接着,文章深入讨论了内联函数,解释了内联如何减少运行时内存开销,并说明了noinline、return在内联函数中的特殊行为。

1.高阶函数

定义:一个函数接收另一个函数作为参数,或者返回值的类型是另一个函数,那么该函数就称为高阶函数。

基本格式如下:

fun example(func:(String,Int)->Unit){
    func("holle",123)
}

例子:

    fun plus(num1: Int, num2: Int) = num1 + num2

    fun minus(num1: Int, num2: Int) = num1 - num2

    fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int) {
        operation(num1,num2)
    }

    //调用方式
    num1AndNum2(2,1,::plus)
    num1AndNum2(2,1,::minus)

lambda调用方式:

        num1AndNum2(2,1){
            n1,n2->n1+n2
        }

2.内联函数

将上述高阶函数转化成java如下:

//转化成java,通过创建类的实力并调用里面的方法
public static int num1AndNum2(int num1,int num2,Function operation){
    int result = (int) operation.invoke(num1,num2);
    return result;
}

int result = num1AndNum2(2,1,new Function(){
    @Override
    public Integer invoke(Integer n1,Integer n2){
        return n1+n2;
    }
});

上述代码可以看出lambda表达式是转化成匿名内部类的形式完成的高阶函数,实质上每次调用都会创建新的匿名类的实例中的方法

内联函数:kotlin编译器会将内联函数中的代码在编译的时候自动替换到调用它的地方,这样不存在内存开销。

    //在函数前面添加inline 就成为内联函数
    inline fun num1AndNum2(num1: Int, num2: Int, operation: (Int, Int) -> Int) {
        Log.v("TAG",operation(num1,num2).toString())
    }

    //kotlin编译前
    num1AndNum2(2,1){
        n1,n2->n1+n2
    }

    //kotlin编译后
    Log.v("TAG",(2+1).toString())

noinline 让内联函数局部函数不会被内联

内联函数lambda表达式中的return全局返回

高阶函数lambda表达式中的return局部返回

crossinline 确保内联函数不会调用return

你总结的这几点确实是 Kotlin 内联函数中非常核心的概念。为了让你能快速把握它们之间的联系与区别,我先把关键信息整理成一个表格。

关键字/概念

控制目标

对 Lambda 参数的影响

主要用途

inline

函数本身

默认内联所有 Lambda 参数

提升性能,支持非局部返回

noinline

特定的 Lambda 参数

禁止指定的 Lambda 参数被内联

允许被标记的 Lambda 被当作普通对象使用(如存储、传递)

crossinline

Lambda 参数的调用方式

允许 Lambda 被间接调用,但禁止其使用非局部返回 (return)

保证 Lambda 在间接调用上下文(如嵌套 Lambda、新线程)中的安全性

下面我们来详细解读表格中的内容,特别是你提到的 return 语句行为。

🔍 详解核心概念

1. inline与“非局部返回”

当函数被声明为 inline时,编译器会将函数体(包括其 Lambda 参数里的代码)直接“复制”到调用处。这带来的一个关键特性是,在传递给内联函数的 Lambda 表达式内部,可以直接使用 return语句,而这个 return会从调用该内联函数的那个外层函数中返回。这就是所谓的 ​​“非局部返回”​

inline fun runInline(block: () -> Unit) {
    println("Before block")
    block()
    println("After block") // 如果block中直接return,这行不会执行
}

fun main() {
    runInline {
        println("Inside lambda")
        return // 这个 return 会使 main 函数在此处返回!
    }
    println("This will not be printed") // 这行不会执行
}

输出:​


复制

Before block
Inside lambda

如你所见,Lambda 中的 return直接结束了 main函数,这就是“全局返回”的含义

2. 高阶函数与“局部返回”

对于非内联的高阶函数,其 Lambda 参数在底层会被编译成一个函数对象。因此,在 Lambda 内部直接写 return是不允许的,因为这会引起歧义(这个 return应该从哪个函数返回?)。在这种情况下,你只能进行局部返回,即使用 return@label语法来明确指定返回到当前 Lambda 的调用者,而不会影响外层函数

// 这是一个非内联函数(没有 inline 关键字)
fun runNormal(block: () -> Unit) {
    println("Before block")
    block()
    println("After block")
}

fun main() {
    runNormal {
        println("Inside lambda")
        // return // 编译错误:'return' is not allowed here
        return@runNormal // 正确:局部返回,仅退出当前 Lambda
    }
    println("This WILL be printed") // 这行会正常执行
}

输出:​

Before block
Inside lambda
After block
This WILL be printed
3. noinline的用途

在一个 inline函数中,如果你有多个 Lambda 参数,但其中某个参数你不希望它被内联,就可以用 noinline来修饰它。这通常是因为你需要将这个 Lambda 当作一个真正的函数对象来使用,例如:将它赋值给变量、作为返回值、或者传递给另一个非内联函数

// 假设这是一个普通的,非内联的函数
fun processFunction(fn: () -> Unit) {
    // ... 存储或延迟执行 fn
}

inline fun example(
    inlineBlock: () -> Unit, 
    noinline normalBlock: () -> Unit // 这个参数不会被内联
) {
    inlineBlock() // 会被内联展开
    processFunction(normalBlock) // 作为对象传递,需要 noinline
    // val storedBlock = normalBlock // 也可以存储起来
}

如果不加 noinline,尝试将 inlineBlock传递给 processFunction会导致编译错误,因为 inlineBlock已经被“溶解”为一段代码,不再是一个可传递的对象了

4. crossinline的约束

crossinline用于当你需要在一个内联函数中间接调用其 Lambda 参数时,例如将参数包在另一个 Lambda(如 Runnable)或内部类中。它向编译器保证:“这个 Lambda 参数不会进行非局部返回”,从而避免了潜在的逻辑混乱和编译错误

加了 crossinline后,该 Lambda 内就不能直接使用 return了,但依然可以使用 return@label进行局部返回。

inline fun runCrossinline(crossinline block: () -> Unit) {
    // 将 block 放在 Runnable 中执行,这是一种间接调用
    Runnable {
        block() // 如果没有 crossinline,这里编译会报错
    }.run()
}

fun main() {
    runCrossinline {
        println("Inside lambda")
        // return // 编译错误!crossinline 禁止非局部返回
        return@runCrossinline // 正确:局部返回是允许的
    }
}

💎 总结与记忆窍门

简单来说,这三个关键字共同精细地控制着内联函数和 Lambda 表达式的行为:

  • inline是发动机​:它开启了内联优化和非局部返回的能力。

  • noinline是刹车​:在需要保留函数对象特性的地方,踩下刹车,禁止内联。

  • crossinline是安全带​:在间接调用的危险场景中,系上安全带,禁止危险的“非局部返回”,确保安全。

希望这个解释能帮你理清思路!如果你对某个特定场景还有疑问,我们可以继续探讨。

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龚礼鹏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值