kotlin知识体系(四) : inline、noinline、crossinline 关键字对应编译后的代码是怎样的 ?
1. inline、noinline、crossinline 的作用
在 Kotlin 里,inline
、noinline
和 crossinline
这几个关键字和高阶函数紧密相关,它们能够对高阶函数的行为进行优化和控制。本文接下来会详细介绍它们的作用和原理。
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 代码解释
在上述示例中,inlineFunction
被 inline
关键字修饰。在编译时,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
中不能使用非局部返回,但可以正常执行其他操作。这样既能保证参数被内联,又能在一定程度上控制返回行为。
综上所述,inline
、noinline
和 crossinline
关键字在 Kotlin 中用于控制高阶函数及其参数的内联行为,有助于优化代码性能和控制函数返回逻辑。
2.对应编译后的代码是怎样的 ?
下面通过具体示例,详细分析 Kotlin 中 inline
、noinline
和 crossinline
关键字在编译后代码的表现。
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
允许内联的同时限制非局部返回,这些关键字在编译后的代码中体现了不同的处理方式。