小憩第55篇原创文章
Kotlin语言相信大家已经玩的很溜了,但大家有没有注意到它内部源码大量使用了inline,那么Kotlin为什么要使用inline?它的作用又是什么呢?
如果你只是注意到了,但从来没有进行深入探究,相信这篇文章能够帮你找到答案。
inline
inline
是作用在函数方法上面的,例如Kotlin
的let
方法
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
那它的作用是什么呢?
inline
主要是对闭包block
做优化,为了对比它做的优化,我对应定义一个没有inline
的方法
public fun <T, R> T.ret(block: (T) -> R): R {
return block(this)
}
然后我同时调用这两个方法
代码语言:javascript复制class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
let {
it.a()
}
ret {
it.b()
}
}
fun a() {
}
fun b() {
}
fun <T, R> T.ret(block: (T) -> R): R {
return block(this)
}
}
再通过AS
的Show Kotlin Bytecode
,来看它们反编译的二进制代码
public final class MainActivity extends AppCompatActivity {
private HashMap _$_findViewCache;
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(1300009);
boolean var3 = false;
boolean var4 = false;
// inline修饰的let
MainActivity it = (MainActivity)this;
int var6 = false;
it.a();
// 没有inline修饰的ret
this.ret(this, (Function1)null.INSTANCE);
}
public final void a() {
}
public final void b() {
}
public final Object ret(Object $this$ret, @NotNull Function1 block) {
Intrinsics.checkParameterIsNotNull(block, "block");
return block.invoke($this$ret);
}
...
}
不懂的还是要看源码,程序员的终结武器
在这里我们发现通过inline
修饰的方法,会通过平坦式的方式直接在后面按执行顺依次调用。
而没有使用inline
修饰的方法,则会为block
方法创建一个Function1
实例。
简单的理解就是未使用inline
修饰的方式,会对带有函数式参数的方法,创建对于函数的实例,再将这个实例传递到方法参数中。该参数方法最终在原方法的内部被显示调用。
所以inline
做的优化就是将带有函数参数的方法简化成没函数式参数的直接调用。好处是提高程序的性能。
当然需要注意的是,避免使用inline
内联大型函数,减少方法中代码的增长。
非局部返回
inline
还有一个好处是,对于while
、for
等语句,被inline
修饰的函数支持局部返回
还是上面的例子
代码语言:javascript复制while (--i > 0) {
let {
return // success
}
}
while (--i > 0) {
ret {
return // error: return is not allow here
}
}
简单的理解就是,使用inline
修饰的函数,可以直接在循环语句中通过return
跳出循环体。而非inline
函数是不支持的,它支持跳出方法体。
原因也很简单,回头再看之前的反编译的二进制代码,因为使用inline
修饰的方法是平铺式直接按顺序调用,并没有包含在方法体中,所以如果return
的话就相当于直接在循环体中return
while(--i > 0) {
return
...
}
而未使用inline
修饰的方法,是在另外的方法体中进行调用,所以它的return
只能是返回到方法体。
reified
使用inline
修饰的函数还有一个好处是可以使用reified
来修饰函数的泛型,让函数的泛型具体化
inline fun <reified T, R> T.det(block: (T) -> R): R {
val a = 0
if (a is T) { // success
}
return block(this)
}
fun <T, R> T.ret(block: (T) -> R): R {
val a = 0
if (a is T) { // error: Cannot check for instance of erased type: T
}
return block(this)
}
// 或者
inline fun <reified T> membersOf() = T::class.members
传统的泛型是会在程序运行的过程中进行擦除操作,而使用reified
修饰的泛型,通过反编译二进制表现就是将泛型替换成具体的类型,不进行类型擦除。
$i$f$det = false;
int a$iv = 0;
if (Integer.valueOf(a$iv) instanceof MainActivity) {
this.a();
}
MainActivity it = (MainActivity)this;
int var7 = false;
it.b();
noinline
有了inline
自然也有对立的noinline
。
对于多个函数方法参数,可以使用noinline
来指定某个函数方法参数不使用inline
的特性
inline fun <T, R> T.net(block: (T) -> R, noinline noBlock: () -> Unit): R {
noBlock()
return block(this)
}
这样就只有block
会执行inline
的优化。
crossinline
还有一种情况,如果使用了inline
修饰的函数,被使用到了嵌套的内联函数中,直接使用是会报错的,需要为函数参数添加crossinline
修饰符
inline fun <T, R> T.cet(block: (T) -> R, crossinline noBlock: () -> Unit): R {
Runnable {
noBlock()
}
return block(this)
}
今天有关kotlin
的inline
部分就分析到这,希望你有所收获。
推荐阅读
数组:面试中的疑难点
Jetpack:DataStore必知的几个优点
Android Startup最新进展