kotlin知识体系(四) : inline、noinline、crossinline 关键字对应编译后的代码是怎样的 ?

1. inline、noinline、crossinline 的作用

在 Kotlin 里,inlinenoinlinecrossinline 这几个关键字和高阶函数紧密相关,它们能够对高阶函数的行为进行优化和控制。本文接下来会详细介绍它们的作用和原理。

1.1 inline 关键字

inline 关键字用于修饰高阶函数,其作用是在编译时将函数调用处替换为函数体本身,以此避免函数调用的开销,提高代码的执行效率。

1.1.1 示例代码
// 定义一个内联高阶函数
inline fun inlineFunction(block: () -> Unit) {
    block()
}

fun main() {
    inlineFunction {
        println("This is an inline function call.")
    }
}
1.1.2 代码解释

在上述示例中,inlineFunctioninline 关键字修饰。在编译时,inlineFunction 的调用会被替换为函数体内容,这样就不会有额外的函数调用开销。不过,使用 inline 也会使生成的字节码体积增大,因为函数体被复制到了调用处。

1.2 noinline 关键字

当高阶函数被 inline 修饰时,它的所有函数参数默认也会被内联。要是你不希望某个函数参数被内联,就可以使用 noinline 关键字。

1.2.1 示例代码
// 定义一个内联高阶函数,包含一个 noinline 参数
inline fun mixedFunction(inlineBlock: () -> Unit, noinline noInlineBlock: () -> Unit) {
    inlineBlock()
    noInlineBlock()
}

fun main() {
    mixedFunction(
        { println("This is an inline block.") },
        { println("This is a non - inline block.") }
    )
}
1.2.2 代码解释

在这个例子中,mixedFunction 是内联函数,inlineBlock 会被内联,而 noInlineBlock 由于使用了 noinline 关键字,不会被内联。noinline 通常用于需要将函数参数存储在变量中或者作为其他函数的返回值的情况。

1.3 crossinline 关键字

在使用 inline 修饰高阶函数时,内联函数参数里不允许有非局部返回(即从外层函数返回)。若需要在 Lambda 表达式中使用 return 语句,但又不想使用 noinline 来避免内联,就可以使用 crossinline 关键字。

1.3.1 示例代码
// 定义一个内联高阶函数,包含一个 crossinline 参数
inline fun crossInlineFunction(crossinline block: () -> Unit) {
    val wrapper = {
        block()
    }
    wrapper()
}

fun main() {
    crossInlineFunction {
        // 这里不能使用 return 进行非局部返回,但可以执行其他操作
        println("Inside crossinline block.")
    }
}
1.3.2 代码解释

在这个例子中,crossInlineFunction 是内联函数,block 参数使用了 crossinline 关键字。在 block 中不能使用非局部返回,但可以正常执行其他操作。这样既能保证参数被内联,又能在一定程度上控制返回行为。

综上所述,inlinenoinlinecrossinline 关键字在 Kotlin 中用于控制高阶函数及其参数的内联行为,有助于优化代码性能和控制函数返回逻辑。

2.对应编译后的代码是怎样的 ?

下面通过具体示例,详细分析 Kotlin 中 inlinenoinlinecrossinline 关键字在编译后代码的表现。

2.1 inline 关键字

2.1.1 Kotlin 代码示例
inline fun inlineFunction(block: () -> Unit) {
    println("Before block")
    block()
    println("After block")
}

fun main() {
    inlineFunction {
        println("Inside block")
    }
}
2.1.2 编译后代码分析

在编译时,inline 函数会被内联展开。上述代码编译后,大致等效于以下 Java 代码(Kotlin 编译成 JVM 字节码,这里用 Java 形式便于理解):

public class Main {
    public static void main(String[] args) {
        System.out.println("Before block");
        System.out.println("Inside block");
        System.out.println("After block");
    }
}

inlineFunction 的函数体直接替换了调用处的代码,避免了函数调用的开销。

2.2 noinline 关键字

2.2.1 Kotlin 代码示例
inline fun mixedFunction(inlineBlock: () -> Unit, noinline noInlineBlock: () -> Unit) {
    inlineBlock()
    noInlineBlock()
}

fun main() {
    mixedFunction(
        { println("Inline block") },
        { println("Noinline block") }
    )
}
2.2.2 编译后代码分析

inlineBlock 会被内联展开,而 noInlineBlock 不会。编译后的代码大致如下:

public class Main {
    private static final class NoInlineBlock implements Function0<Unit> {
        public Unit invoke() {
            System.out.println("Noinline block");
            return Unit.INSTANCE;
        }
    }

    public static void main(String[] args) {
        System.out.println("Inline block");
        NoInlineBlock noInlineBlock = new NoInlineBlock();
        noInlineBlock.invoke();
    }
}

inlineBlock 被内联到调用处,而 noInlineBlock 被封装成一个实现了 Function0 接口的类,通过创建对象并调用 invoke 方法来执行。

2.3 crossinline 关键字

2.3.1 Kotlin 代码示例
inline fun crossInlineFunction(crossinline block: () -> Unit) {
    val wrapper = {
        block()
    }
    wrapper()
}

fun main() {
    crossInlineFunction {
        println("Crossinline block")
    }
}
2.3.2 编译后代码分析

crossinline 保证了 Lambda 表达式不会有非局部返回,但仍然会被内联。编译后的代码大致如下:

public class Main {
    public static void main(String[] args) {
        final class Wrapper implements Function0<Unit> {
            public Unit invoke() {
                System.out.println("Crossinline block");
                return Unit.INSTANCE;
            }
        }
        Wrapper wrapper = new Wrapper();
        wrapper.invoke();
    }
}

block 被内联到 wrapper 中,同时由于 crossinline 的存在,避免了非局部返回的问题。

总结来说,inline 关键字使函数体在调用处展开,noinline 阻止特定函数参数内联,crossinline 允许内联的同时限制非局部返回,这些关键字在编译后的代码中体现了不同的处理方式。

出现这个错误的原因是在导入seaborn包时,无法从typing模块中导入名为'Protocol'的对象。 解决这个问题的方法有以下几种: 1. 检查你的Python版本是否符合seaborn包的要求,如果不符合,尝试更新Python版本。 2. 检查你的环境中是否安装了typing_extensions包,如果没有安装,可以使用以下命令安装:pip install typing_extensions。 3. 如果你使用的是Python 3.8版本以下的版本,你可以尝试使用typing_extensions包来代替typing模块来解决该问题。 4. 检查你的代码是否正确导入了seaborn包,并且没有其他导入错误。 5. 如果以上方法都无法解决问题,可以尝试在你的代码中使用其他的可替代包或者更新seaborn包的版本来解决该问题。 总结: 出现ImportError: cannot import name 'Protocol' from 'typing'错误的原因可能是由于Python版本不兼容、缺少typing_extensions包或者导入错误等原因造成的。可以根据具体情况尝试上述方法来解决该问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [ImportError: cannot import name ‘Literal‘ from ‘typing‘ (D:\Anaconda\envs\tensorflow\lib\typing....](https://blog.youkuaiyun.com/yuhaix/article/details/124528628)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

氦客

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

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

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

打赏作者

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

抵扣说明:

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

余额充值