java调用kotlin的内联函数,Kotlin内联函数

Kotlin里使用关键 inline 来表示内联函数,那么到底什么是内联函数呢,内联函数有什么好处呢?

1. 什么是内联inline?

在 Java 里是没有内联这个概念的,所有的函数调用都是普通方法调用,如果了解 Java 虚拟机原理的,可以知道 Java 方法执行的内存模型是基于 Java 虚拟机栈的:每个方法被执行的时候都会创建一个栈帧(Stack Frame),用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧入栈、出栈的过程。

也就是说每调用一个方法,都会对应一个栈帧的入栈出栈过程,如果你有一个工具类方法,在某个循环里调用很多次,那就会对应很多次的栈帧入栈、出栈过程。这里首先要记住的一点是,栈帧的创建及入栈、出栈都是有性能损耗的。下面以一个例子来说明,看段代码片段:

fun test() {

//多次调用 sum() 方法进行求和运算

println(sum(1, 2, 3))

println(sum(100, 200, 300))

println(sum(12, 34))

//....可能还有若干次

}

/**

* 求和计算

*/

fun sum(vararg ints: Int): Int {

var sum = 0

for (i in ints) {

sum += i

}

return sum

}

在测试方法 test() 里,我们多次调用了 sum() 方法。为了避免多次调用 sum() 方法带来的性能损耗,我们期望的代码类似这样子的:

fun test() {

var sum = 0

for (i in arrayOf(1, 2, 3)) {

sum += i

}

println(sum)

sum = 0

for (i in arrayOf(100, 200, 300)) {

sum += i

}

println(sum)

sum = 0

for (i in arrayOf(12, 34)) {

sum += i

}

println(sum)

}

3次数据求和操作,都是在 test() 方法里执行的,没有之前的 sum() 方法调用,最后的结果依然是一样的,但是由于减少了方法调用,虽然代码量增加了,但是性能确提升了。那么怎么实现这种情况呢,一般工具类有很多公共方法,我总不能在需要调用这些公共方法的地方,把代码复制一遍吧,内联就是为了解决这一问题。

定义内联函数:

inline fun sum(vararg ints: Int): Int {

var sum = 0

for (i in ints) {

sum += i

}

return sum

}

如上所示,用关键字 inline 标记函数,该函数就是一个内联函数。还是原来的 test() 方法,编译器在编译的时候,会自动把内联函数 sum() 方法体内的代码,替换到调用该方法的地方。查看编译后的字节码,会发现 test() 方法里已经没了对 sum() 方法的调用,凡是原来代码里出现 sum() 方法调用的地方,出现的都是 sum() 方法体内的字节码了。

2. noinline

如果一个内联函数的参数里包含 lambda表达式,也就是函数参数,那么该形参也是 inline 的,举个例子:

inline fun test(inlined: () -> Unit) {...}

这里有个问题需要注意,如果在内联函数的内部,函数参数被其他非内联函数调用,就会报错,如下所示:

//内联函数

inline fun test(inlined: () -> Unit) {

//这里会报错

otherNoinlineMethod(inlined)

}

//非内联函数

fun otherNoinlineMethod(oninline: () -> Unit) {

}

要解决这个问题,必须为内联函数的参数加上 noinline 修饰,表示禁止内联,保留原有函数的特性,所以 test() 方法正确的写法应该是:

inline fun test(noinline inlined: () -> Unit) {

otherNoinlineMethod(inlined)

}

3. crossinline

首先来理解一个概念:非局部返回。我们来举个栗子:

fun test() {

innerFun {

//return 这样写会报错,非局部返回,直接退出 test() 函数。

return@innerFun //局部返回,退出 innerFun() 函数,这里必须明确退出哪个函数,写成 return@test 则会退出 test() 函数

}

//以下代码依旧会执行

println("test...")

}

fun innerFun(a: () -> Unit) {

a()

}

非局部返回我的理解就是返回到顶层函数,如上面代码中所示,默认情况下是不能直接 return 的,但是内联函数确是可以的。所以改成下面这个样子:

fun test() {

innerFun {

return //非局部返回,直接退出 test() 函数。

}

//以下代码不会执行

println("test...")

}

inline fun innerFun(a: () -> Unit) {

a()

}

也就是说内联函数的函数参数在调用时,可以非局部返回,如上所示。那么 crossinline 修饰的 lambda 参数,可以禁止内联函数调用时非局部返回。

fun test() {

innerFun {

return //这里这样会报错,只能 return@innerFun

}

//以下代码不会执行

println("test...")

}

inline fun innerFun(crossinline a: () -> Unit) {

a()

}

4. 具体化的类型参数 reified

这个特性我觉得特别牛逼,有了它可以少些好多代码。在 Java 中是不能直接使用泛型的类型的,但是在 Kotlin 中确可以。举个栗子:

inline fun startActivity() {

startActivity(Intent(this, T::class.java))

}

使用时直接传入泛型即可,代码简洁明了:

startActivity()

5. 小结

网上很多学习教程对内联函数的讲解都是千篇一律,说实话刚开始很难理解。本文尝试着用最简单的例子,来讲清楚什么是内联函数。在Java中我们一般会有很多工具类、工具方法,在Kotlin中则没有了工具类一说,通常都是将工具方法设计成顶层的内联函数来使用。

你可能感兴趣的:(java调用kotlin的内联函数,Kotlin内联函数)