Kotlin 4 での Lambda の使用。インライン関数 inline

1. インライン関数

        キーワード inline で変更された関数は、インライン関数と呼ばれます。インライン変更は、関数をパラメータとして使用する高階関数です。通常の関数を変更すると、警告が表示されます。インライン化によって予想されるパフォーマンスへの影響はわずかです。インライン化は、関数型のパラメータを持つ関数に最適に機能し、通常の関数のインライン化によるパフォーマンスへの影響は無視できます。

2. インラインを使用して高階関数を変更する利点

        Kotlinの通常の関数宣言と高階関数のインライン関数宣言をJavaコードに変換して比較します。コトリンコード:

class Test {
    //不使用inline 修饰的高阶函数
    fun test(f: () -> Unit) {
        f()
    }
    //使用inline 修饰的高阶函数
    inline fun testInline(f: () -> Unit) {
        f()
    }
    fun call() {
        test { print("test") }
        testInline { print("testInline") }
    }
}

Javaコード:

public final class Test {
   public final void test(@NotNull Function0 f) {
      Intrinsics.checkNotNullParameter(f, "f");
      f.invoke();
   }

   public final void testInline(@NotNull Function0 f) {
      int $i$f$testInline = 0;
      Intrinsics.checkNotNullParameter(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);
   }
}

        呼び出しを見ると、非インライン関数を呼び出すときにこの関数が直接呼び出され、Lambda 関数を呼び出すために匿名クラス Function0 が作成されていることがわかります。インライン関数は、匿名クラスを作成する代わりに関数本体をコピーしますが、Lambda関数実装本体を直接埋め込みます。

        高階関数がInline装飾されていない場合、この関数を呼び出すと、この関数が直接参照され、この関数パラメータの呼び出しを実装するために匿名クラスが作成されます。これには 2 つの部分のオーバーヘッドがあります。この関数を直接呼び出すと、追加のポップ操作 (関数呼び出しはスタック フレームのプッシュとポップのプロセスです)、および匿名クラスの作成もパフォーマンスを消費します。Inline を使用して高次関数を変更すると、パフォーマンスが向上します。

         インライン関数を呼び出す場合、上記に基づいて、インライン化によってLambdaパラメーター関数の本体が直接埋め込まれること、およびインライン化によって生成されるコードが増加する可能性があることはすでにわかっていますが、それを適切に使用すれば (つまり、大きすぎるインライン化を避ける必要があります)関数)、特にループ内の「メガモーフィック」呼び出しでパフォーマンスが向上しました。

3. インライン noinline を無効にする

        インライン関数に渡されるラムダ式パラメータの一部のみをインライン化したい場合は、 noinline 修飾子を使用して、インライン化したくない関数パラメータをマークできます。

inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { //…… }

        インライン化可能なラムダ式は、インライン関数内でのみ非インライン関数のパラメータとして渡される場合、またはフィールドに格納される場合、インライン変更は必要ありません。

inline fun testInline(f1: () -> Unit,  f: () -> Unit) {
    f1.invoke()
    val a = f//会提示错误,内联Lambad 不能参数赋值
    testInlineInner(f) //会提示错误,内联Lambad 不能作为普通函数参数进行传递
}

fun testInlineInner(f: () -> Unit) {
    f()
}
inline fun testInline(f1: () -> Unit,  noinline f: () -> Unit) {
    f1.invoke()
    val a = f//可以
    testInlineInner(f) //可以
}

4. インライン関数のラムダ式で return (非ローカルリターン) を使用する

        Kotlin では、名前付き関数または匿名関数からの通常の修飾されていない戻り値を使用してのみ終了できます。returnラムダ式を終了するには、ラベルを使用する必要があります。ラムダ式は、それを含む関数を返すことができないため、ラムダ式内での bare の使用は禁止されています 。

fun testInlineInner(f: () -> Unit) {
    f()
}
testInlineInner {
        return//'return' is not allowed here
    }

ただし、ラムダ式によって渡された関数がインラインの場合、戻り値もインライン化できるため、許可されます。この種の戻り値 (ラムダ式内に配置されますが、ラムダ式を含む関数を終了するもの) は、非ローカル戻り値と呼ばれます

inline fun testInlineInner(f: () -> Unit) {
    f()
}

fun main(args: Array<String>) {
    testInlineInner {
        return// 可以,退出main函数
    }
}

5.クロスインライン

        インライン化がLambda関数本体で直接呼び出されず、入れ子関数または他の実行環境で呼び出される場合は、次のように宣言する必要があります。crossinline。

inline fun f(crossinline body: () -> Unit) {
   val f = Runnable { body() }
}

六、Reified 型のパラメータ

        reified は、ジェネリックに関する Kotlin のキーワードです。そのため、ジェネリックは消去されません。ジェネリック メソッドでジェネリック型を変更するために reified を使用する必要がある場合は、Inline 関数でジェネリック型を指定できるため、Inline を使用してジェネリック メソッドを変更する必要があります。型は消去されません。インライン関数はコンパイル中に呼び出される場所にバイトコードを埋め込むため、コンパイラはジェネリックに対応する特定の型を認識します。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)
    }
}

7. インライン属性

inlinesetメソッドを変更したりget、両方のアクセサーが次のようにマークされるようにプロパティを直接変更したりすることもできます。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
    }
    //转化为Java代码
    public final void test() { 
      Amount amount = new Amount(10L);
      int $i$f$isEmpty = false;
      boolean isEmpty = amount.getAmount() <= 0L; //代码嵌入
      boolean isEmptyNoInline = amount.isEmptyNoInline(); //非嵌入
   }
}

1. コレクション関数 API でのラムダ式の使用の概要

2. 関数の型とインスタンス化

3. SAM 変換 (単一抽象メソッド変換)

4. インライン関数のインライン化

おすすめ

転載: blog.csdn.net/old_land/article/details/119612708