版權(quán)歸作者所有捺球,轉(zhuǎn)發(fā)請注明出處:http://www.reibang.com/p/14a837e67dd7
Kotlin中的inline關(guān)鍵字 - 內(nèi)聯(lián)函數(shù)
Kotlin中的inline關(guān)鍵字 - 內(nèi)聯(lián)類
前言
Kotlin
是一種在 Java
虛擬機(jī)上運行的靜態(tài)類型編程語言衫哥,被稱之為 Android
世界的Swift
谤绳,在Google
I/O 2017中,Google
宣布 Kotlin
成為 Android
官方開發(fā)語言
內(nèi)聯(lián)函數(shù)
被inline
關(guān)鍵字所修飾的函數(shù)為內(nèi)聯(lián)函數(shù)
內(nèi)聯(lián)函數(shù)的作用
嘗試去修飾普通函數(shù)
inline fun getAmount() = 0.1
這種寫法編輯器會有警告提示:
Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types
意味著使用inline
只有在修飾一個參數(shù)為functional types
的函數(shù)時候效果最好,也就是我們所說的高階函數(shù)的一種,kotlin
中的forEach
函數(shù)中參數(shù)action
就是一個函數(shù)類型,所以forEach
函數(shù)使用了inline
修飾
@kotlin.internal.HidesMembers
public inline fun <T> Iterable<T>.forEach(action: (T) -> Unit): Unit {
for (element in this) action(element)
}
inline修飾高階函數(shù)的好處
我們對高階函數(shù)分別使用普通函數(shù)聲明钱贯,和使用內(nèi)聯(lián)函數(shù)聲明
class Test {
//不使用inline 修飾的高階函數(shù)
fun test(f: () -> Unit) {
f()
}
//使用inline 修飾的高階函數(shù)
inline fun testInline(f: () -> Unit) {
f()
}
fun call() {
test {
print("test")
}
testInline {
print("testInline")
}
}
}
轉(zhuǎn)化為Java代碼
public final class Test {
public final void test(@NotNull Function0 f) {
Intrinsics.checkParameterIsNotNull(f, "f");
f.invoke();
}
public final void testInline(@NotNull Function0 f) {
int $i$f$testInline = 0;
Intrinsics.checkParameterIsNotNull(f, "f");
f.invoke();
}
public final void call() {
this.test((Function0)null.INSTANCE);
int $i$f$testInline = false;
int var3 = false;
String var4 = "testInline";
boolean var5 = false;
System.out.print(var4);
}
}
在call()
中可以看出,第一行,當(dāng)調(diào)用非內(nèi)聯(lián)函數(shù)時是直接調(diào)用了此函數(shù),并且創(chuàng)建了匿名類Function0用于Lambda函數(shù)的調(diào)用
this.test((Function0)null.INSTANCE);
在call()
中的其他代碼可以看出侦另,內(nèi)聯(lián)函數(shù)則是復(fù)制了函數(shù)體過來秩命,而沒有創(chuàng)建匿名類尉共,而是直接嵌入了Lambda
函數(shù)的實現(xiàn)體
int $i$f$testInline = false;
int var3 = false;
String var4 = "testInline";
boolean var5 = false;
System.out.print(var4);
當(dāng)高階函數(shù)沒有使用Inline
修飾時,調(diào)用此函數(shù)會直接引用此函數(shù)弃锐,并且會創(chuàng)建匿名類以實現(xiàn)此函數(shù)參數(shù)的調(diào)用袄友,這有兩部分開銷,直接調(diào)用此函數(shù)會創(chuàng)建額外的棧幀以及入棧出棧操作(一個函數(shù)的調(diào)用就是一個棧幀入棧和出棧的過程)霹菊,并且匿名類的創(chuàng)建也會消耗性能
使用 Inline 修飾高階函數(shù)是會有性能上的提升
避免內(nèi)聯(lián)大型函數(shù)
在調(diào)用內(nèi)聯(lián)函數(shù)時剧蚣,基于上述我們已經(jīng)知道內(nèi)聯(lián)會將Lambda
參數(shù)函數(shù)體直接進(jìn)行嵌入,避免了函數(shù)的引用以及棧幀和對象創(chuàng)建的開銷旋廷,但是如果Lambda
所內(nèi)聯(lián)的函數(shù)體數(shù)量太大鸠按,嵌入則會造成調(diào)用位置的函數(shù)體增長,所以需要 避免內(nèi)聯(lián)大型函數(shù)
noinline關(guān)鍵字使用
內(nèi)聯(lián)Lambad
只能在內(nèi)聯(lián)函數(shù)內(nèi)部使用饶碘,或者作為內(nèi)聯(lián)參數(shù)進(jìn)行傳遞待诅,如果想 將內(nèi)聯(lián)Lambad
作為普通函數(shù)參數(shù)進(jìn)行傳遞或者存儲在字段中則不能使用內(nèi)聯(lián)函數(shù)聲明,或者在聲明為內(nèi)聯(lián)函數(shù)然后將需要傳遞的Lambda
參數(shù)指定為noinline
inline fun testInline(f: () -> Unit) {
testInlineInner(f) //會提示錯誤熊镣,內(nèi)聯(lián)Lambad 不能作為普通函數(shù)參數(shù)進(jìn)行傳遞
}
fun testInlineInner(f: () -> Unit) {
f()
}
如果一個函數(shù)中有多個函數(shù)參數(shù),并且部分參數(shù)時可以直接使用內(nèi)聯(lián)方式調(diào)用募书,另一部分則需要進(jìn)行傳遞或者賦值绪囱,則可以將函數(shù)聲明為內(nèi)聯(lián)函數(shù),內(nèi)聯(lián)函數(shù)中莹捡,不符合內(nèi)聯(lián)方式的參數(shù)使用noinline
修飾
inline fun testInline(f1: () -> Unit, noinline f: () -> Unit) {
f1()
testInlineInner(f)
}
fun testInlineInner(f: () -> Unit) {
f()
}
內(nèi)聯(lián)Lambda中使用return
- 在調(diào)用普通高階函數(shù)時鬼吵,在Lambda中不能直接使用retrun退出lambda表達(dá)式,需要進(jìn)行聲明然后退出lambda篮赢,inline lambda可以直接return齿椅,但是退出的是當(dāng)前外部函數(shù)而不是lambda表達(dá)式
fun call() {
testOrdinary {
print("testOrdinary")
return //報錯
}
testOrdinary {
print("testOrdinary")
return@testOrdinary //正常 退出 testOrdinary
}
testInline {
print("testInline")
return //正常 退出call()
}
testInline {
print("testInline")
return@testInline //正常 退出 testInline
}
}
crossinline關(guān)鍵字使用
當(dāng)內(nèi)聯(lián)Lambda
不是直接在函數(shù)體中調(diào)用,而是在嵌套函數(shù)或者其他執(zhí)行環(huán)境中調(diào)用則需要聲明為crossinline
inline fun testInline(crossinline callBack: () -> Unit) {
object : Thread(){
override fun run() {
callBack() //執(zhí)行在object中启泣,需要將callBack聲明為 crossinline
}
}
}
Reified 類型的參數(shù)
reified 是Kotlin關(guān)于范型的關(guān)鍵字涣脚,從而達(dá)到泛型不被擦除,如果需要使用reified 去修飾泛型方法中的泛型類型寥茫,則需要使用Inline修飾此泛型方法遣蚀,因為Inline函數(shù)可以指定泛型類型不被擦除,因為內(nèi)聯(lián)函數(shù)編譯期會將字節(jié)碼嵌入到調(diào)用它的地方纱耻,所以編譯器才會知道泛型對應(yīng)的具體類型芭梯,使用reified 和 Inline適用于泛型方法,并且方法體中需要對泛型類型做以判斷的情況
inline fun <reified T> Bundle.plus(key: String, value: T) {
when (value) {
is String -> putString(key, value)
is Long -> putLong(key, value)
is Int -> putInt(key, value)
}
}
Inline properties
inline
也可以修飾set
弄喘,get
方法也會在調(diào)用位置進(jìn)行嵌入代碼以收益性能玖喘,并且可以直接修飾屬性使兩個訪問器都標(biāo)記為inline
class Amount(var amount: Long) {
private val isEmpty: Boolean
inline get() {
return amount <= 0
}
private val isEmptyNoInline: Boolean
get() {
return amount <= 0
}
fun test(){
val amount = Amount(10)
val isEmpty = amount.isEmpty
val isEmptyNoInline = amount.isEmptyNoInline
}
//轉(zhuǎn)化為Java代碼
public final void test() {
Amount amount = new Amount(10L);
int $i$f$isEmpty = false;
boolean isEmpty = amount.getAmount() <= 0L; //代碼嵌入
boolean isEmptyNoInline = amount.isEmptyNoInline(); //非嵌入
}
}
歡迎關(guān)注Mike的簡書
Android 知識整理