Kotlin Inline Functions
ããã¯Kotlin Advent Calendar 2015ã®ç¬¬8æ¥ç®ã®è¨äºã§ããæ¨æ¥ã¯ n_yunoue ããã«ãããã¨ãããããã°ã¦ã§ã¼ãã«ä¹ãã§ããã
仿¥ã¯ inline functions ã®è©±ãæ¸ãããã¨æãã¾ããæ¬é¡ã«å
¥ãåã« function literal 㨠function expression ã®éãã«ã¤ãã¦èª¬æãã¦ããã®å¾ã« inline
modifier ãããã«ã¾ã¤ãã Kotlin ã®æ©è½ï¼non-local returnãreified
modifier ã crossline
modifierï¼ã«ã¤ãã¦èª¬æãã¾ãã
Difference between Function Literal & Function Expression
Kotlin ãæ¸ãå§ããæåã¯ãfunction ã®æ§ã ãªæ¸ãæ¹ã«æ¸æããªãã£ãã§ããããã以ä¸ã¯ãã¹ã¦æ£ããæ¸ãæ¹ã§ãã
// Return the longest element list.maxBy { it.length } list.maxBy { a -> a.length } list.maxBy { a: String -> a.length } list.maxBy (fun(a) = a.length ) list.maxBy (fun(a): Int = a.length ) list.maxBy (fun(a): Int { return a.length }) list.maxBy (fun(a: String): Int { return a.length })
ããã«è¨ãã°ã颿°ã宣è¨ããæ¹æ³ãããã¤ãããã®ã§ãããããã§ã¯çç¥ãã¦ããã¾ãããã
ãã¦ãæ¨é¡ã® function literal 㨠function expression ã®éãã§ããä¸è¨ã®ã©ãã function literal ã§ãã©ãã function expression ã§ããããï¼ããã§ãããã¬ã¼ã¹ï¼{}
ï¼ã«å²ã¾ãã颿°ï¼æåã®3ã¤ï¼ã¯ãã¹ã¦ function literal ã§ãæ®ãã® fun
ãã¼ã¯ã¼ãã使ããã¦ããä¾ã¯ function expression ã§ããç°¡åã§ããã
ã§ã¯ããã®ä¸¡è ã®éãã¯ä½ã§ããããï¼å®ã¯ã両è ã®éã㯠return æã®è§£éã®ã¿ã«ç¾ããããã§ã*1ã
ä¾ãã°ã以ä¸ã®ããã« function literal ã®ä¸ã§ã¯ç´ ã® return æã¯ç¦æ¢ããã¦ãããã³ã³ãã¤ã«ã¨ã©ã¼ã«ãªãã¾ããããã¯ããç´ ã® return æã¯ç´è¿ã®ååä»ã颿°ï¼named functionï¼ã function expression ãè±åºãããã¨å®ãããã¦ãããlambda ããã®è±åºã«ã¯ä½¿ããªãããã§ããreturn@ordinaryFunction
ã®ãããªã©ãã«ãä»ããã°ãreturn æã®ä½¿ç¨ã¯å¯è½ã§ãã
fun foo() { ordinaryFunction { return // ERROR: can not make `foo` return here } } fun foo() { ordinaryFunction { return@ordinaryFunction // OK: exit the lambda } }
䏿¹ãfunction expression ãååä»ã颿°ã®å ´åã¯ããã®ãããªç´ ã® return æã¯åé¡ãªã使ãã¾ãã
fun foo() { ordinaryFunction (fun() { return // OK: exit the function ) } fun foo() { ordinaryFunction (fun() { return@ordinaryFunction // OK: exit the function ) }
ãã®ç´ ã® return æã®è§£éã®å·®ã«ã¤ãã¦ã¯å¾ã§è§¦ããã®ã§è¦ãã¦ããã¦ãã ããã
Inline Modifier
æ¬é¡ã® inline
modifier ã§ãããã® modifier ã¯ãåç¥ã®éãã弿°ã§æ¸¡ããã颿°ããã®ã¾ã¾ã¤ã³ã©ã¤ã³å±éãããã¨ã示ãã¾ããã³ã¼ãã§è¡¨ãã¨ã以ä¸ã®ããã«ãªãã¾ããinline
modifier ãä»ãã¨ã渡ããã弿° f ã颿°ãªãã¸ã§ã¯ãã¨ãã¦æ±ãã®ã§ã¯ãªãããã®ã¾ã¾ä¸èº«ãå±éãã¾ãã
inline fun inlineFunction(f: () -> Unit) { f() }
å®éã«ãã¤ãã³ã¼ãã§ã©ããªãã確èªãã¦ã¿ã¾ããããIntelliJ ã§ã¯ Kotlin Bytecode
ã¨ããã³ãã³ããå®è¡ããã¨ãKotlin ãã©ããªãã¤ãã³ã¼ãã«ãªãã®ãç°¡åã«ç¢ºèªã§ãã¾ããç¹ã«ãKotlin ã³ã¼ãå´ã§ãã¤ã³ã¿ãç§»åããã¨å¯¾å¿ãããã¤ãã³ã¼ãã®ç®æããã¤ã©ã¤ããããã®ã便å©ã§ãã
ã¾ããinline
modifier ãä»ããªãå ´åããã®ãã㪠Kotlin ã³ã¼ããã
ordinalFunction(fun() { println("calling fun") return })
ãããªãã¾ãã
ALOAD 0 GETSTATIC LambdaTest$testLambda02$1.INSTANCE : LLambdaTest$testLambda02$1; CHECKCAST kotlin/jvm/functions/Function0 INVOKEVIRTUAL LambdaTest.ordinalFunction (Lkotlin/jvm/functions/Function0;)V
ããã§ LambdaTest$testLambda02$1.INSTANCE
ã¨ããã®ãç¡å颿°ãªãã¸ã§ã¯ãã«ãªããä¸ã®æ¹ã§ä»¥ä¸ã®ããã«å®ç¾©ããã¾ãã
// ================LambdaTest$testLambda02$1.class ================= // class version 50.0 (50) // access flags 0x30 // signature Lkotlin/jvm/internal/Lambda;Lkotlin/jvm/functions/Function0<Lkotlin/Unit;>; // declaration: LambdaTest$testLambda02$1 extends kotlin.jvm.internal.Lambda implements kotlin.jvm.functions.Function0<kotlin.Unit> final class LambdaTest$testLambda02$1 extends kotlin/jvm/internal/Lambda implements kotlin/jvm/functions/Function0 { ... // access flags 0x11 public final invoke()V L0 LINENUMBER 36 L0 LDC "calling fun" INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V L1 LINENUMBER 37 L1 RETURN L2 LOCALVARIABLE this LLambdaTest$testLambda02$1; L0 L2 0 MAXSTACK = 1 MAXLOCALS = 1 ... }
䏿¹ãinline
modifier ãä»ãã¨ã以ä¸ã®ãã㪠Kotlin ã³ã¼ããã
inlineFunction(fun() { println("calling fun in inline function") return })
ãããªãã¾ãã
LINENUMBER 45 L2 ALOAD 0 ASTORE 1 NOP L3 LINENUMBER 9 L3 L4 LINENUMBER 46 L4 LDC "calling fun in inline function" INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V L5 LINENUMBER 47 L5 L6 GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit; POP L7 LINENUMBER 10 L7 L8 L9
颿°å¼ã³åºããæ¶ãã¦ãç´æ¥ function expression ã®ä¸èº«ãå®è¡ããã¦ããã®ãåããã¾ããåæ§ã® function literal ãæ¸¡ãã¦ãåããã¤ãã³ã¼ãã«ãªãã®ãåããã§ãããã
non-local return
ããã§ããããããããã»ã©ã® return æã®è©±ã«æ»ãã¾ããinline
modifier ããªãã¨ãã以ä¸ã® Kotlin ã³ã¼ãã¯ã³ã³ãã¤ã«ã¨ã©ã¼ã«ãªãã¾ããã
fun foo() { ordinaryFunction { return // ERROR: can not make `foo` return here } }
䏿¹ãinline
modifier ãä»ãã颿°ã®å
é¨ã§ã¯ããã®ãããªç´ ã® return æã許ããã¾ãããã ããfunction expression å
ã® return æã¨ç°ãªããå¤å´ã® foo()
ã¡ã½ããããè±åºãã¦ãã¾ãã¾ãã
fun foo() { inlineFunction { println("calling lambda in inline function") return // OK: exit foo() } println("never reach here") }
ããã non-local return ã¨å¼ã°ãããã®ã§ããå°ã
ä¹±æ´ã«è¦ããããããã¾ãããããã¨ãã° forEach
ã®ãããªã¡ã½ããå
ã§å©ç¨ããã¨ãé常㮠for æã¨åããããªå¦çãã§ãã¦ä¾¿å©ã§ãã
fun hasZeros(ints: List<Int>): Boolean { ints.forEach { if (it == 0) return true // returns from hasZeros } return false }
ãªãããã® non-local return ããã¤ãã³ã¼ããè¦ã¦ã¿ãã¨ã以ä¸ã®ããã«ãªãã¾ããprintln("calling lambda in inline function")
ã®å¾ã« RETURN
ãå¼ã°ãã¦ããã®ãåããã¾ããfunction expression ã弿°ã«æ¸¡ããã¨ãã¯ããã ã® POP
ã ã£ãã¨ããã§ãã
LINENUMBER 44 L13 ALOAD 0 ASTORE 1 NOP L14 LINENUMBER 9 L14 L15 LINENUMBER 45 L15 LDC "calling lambda in inline function" INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V L16 LINENUMBER 46 L16 RETURN
noinline modifier
ããããã¯ããããç´°ãã inline
modifier ã«é¢é£ããããã¤ãã®ææ³ã®è©±ã§ããããã»ã©ã® inline function ã¯å¼æ°ã® lambda ããã¹ã¦ã¤ã³ã©ã¤ã³å±éãã¾ãããããã¦ã»ãããªãå ´åãããã¾ãããã®ãããªå ´å㯠noinline
modifier ã弿°ã®åã«æå®ãã¦ããã¾ãã
inline fun foo(f: () -> Unit, noinline g: () -> Unit) { f() g() }
å®éã«ã以ä¸ã®ããã«ä¸è¨ã®é¢æ°ãå¼ã³åºãã¨ã
foo(fun() { println("calling 1st fun in inline function") return }, fun() { println("calling 2nd fun in inline function") return })
ãã¤ãã³ã¼ãã以ä¸ã®ããã«ãªãã¾ãã2ã¤ç®ã® lambda ã颿°ãªãã¸ã§ã¯ãã¨ãã¦æ±ããã¦ããã®ãåããã¾ãã
LINENUMBER 40 L2 ALOAD 0 ASTORE 1 GETSTATIC LambdaTest$testLambda$4.INSTANCE : LLambdaTest$testLambda$4; CHECKCAST kotlin/jvm/functions/Function0 ASTORE 2 NOP L3 LINENUMBER 13 L3 L4 LINENUMBER 41 L4 LDC "calling 1st fun in inline function" INVOKESTATIC kotlin/io/ConsoleKt.println (Ljava/lang/Object;)V L5 LINENUMBER 42 L5 L6 GETSTATIC kotlin/Unit.INSTANCE : Lkotlin/Unit; POP L7 LINENUMBER 14 L7 ALOAD 2 INVOKEINTERFACE kotlin/jvm/functions/Function0.invoke ()Ljava/lang/Object; POP
crossline modifier
inline function å ã§ lambda ãæ±ãå ´åããã® lambda ããã®ã¾ã¾ inline function å ã§å®è¡ããã®ã§ã¯ãªããä»ã®æèã§å®è¡ããå ´åãããã¾ãããã®å ´å㯠crossline modifier ãä»ããªãã¨ã³ã³ãã¤ã«ã¨ã©ã¼ã«ãªãã¾ãã
inline fun bar(f: () -> Unit) { var g = object: Runnable { override fun run() { f() // ERROR: Can't inline 'f' here: it may contain non-local returns. Add 'crossinline' modifier to parameter declaration 'f' } } } inline fun bar(crossline f: () -> Unit) { var g = object: Runnable { override fun run() { f() // OK } } }
reified modifier
inline function ã§ã¯ãç·ç§°åã®å ·ä½çãªåã確å®ãããããä»®å弿°ãããããå®éã®ã¯ã©ã¹åã§ãããã®ããã«æ±ãã¾ããããè¨ã£ã¦ãä½ãããåãããªãã¨æãã®ã§å®ä¾ãè¦ã¦ã¿ã¾ãããã
fun <U> isInstance(t: Any): Boolean { return t is U // ERROR: Cannot check for instance of erased type: U } inline fun <reified U> isInstance(t: Any): Boolean { return t is U // OK }
Java ã«æ
£ãã¦ãã人ã«ã¯å¨ç¥ã ã¨æãã¾ãããJava ã®ç·ç§°åã¯ã³ã³ãã¤ã«æã«å弿°ã決å®ãã¦åæ¶å»ãè¡ããããããã©ã³ã¿ã¤ã æã«ã¯åãåããã¾ããããã®ãããããããå
·ä½çãªåãå¥ã®å¼æ°ã§æå®ãããªã©ã®è¦å´ããã¦ãã¾ããï¼List.toArray() ã§é
åãå¥é渡ãã¢ã¬ã§ãï¼ãããããinline function ã¨ããã® reified
modifier ã使ãã¨ããã®ãããªè¦å´ããè§£æ¾ããã¾ããç´ æ´ãããç¾æç¹ã§ã¯ inline function éå®ã§ããããã®ãã¡ä»ã§ã使ããããã«ãªãã®ã§ããããã
ãªããreified
modifier ã®è©³ç´°ã«ã¤ãã¦ã¯ãkotlin/reified-type-parameters.md at master · JetBrains/kotlin ã«æ¸ããã¦ãã¾ãã
ã¾ã¨ã
ãã®è¨äºã§ã¯ãKotlin ã® inline
modifier ã¨ããã«é¢é£ããæåã«ã¤ãã¦è¿°ã¹ã¾ãããç¹ã«ã
- non-local return
crossline
modifierreified
modifier
ã«ã¤ãã¦ã¯ãinline function ç¹æãªãã®ãªã®ã§è¦ãã¦ããã¾ããããããã«ãnon-local return ã«ã¤ãã¦ã¯ function literal 㨠function expression ã®éããéè¦ã§ããã
Kotlin ãå¦ã³å§ãã¦ãã¾ã ä¸ã¶æã®ç§ã®ææ³ã§ãããKotlin çãªæ¸ãæ¹ããããã¨ããã¨ãã©ããã¦ã lambda ãå¤ç¨ãããã¨ã«ãªãã¾ãããã®ãããããã©ã¼ãã³ã¹ãéè¦ãªç®æã§ã¯ inline ã¯å¿ é ã¨è¨ã£ã¦è¯ãã§ãããã
åæã«ãnon-local return ã®æåã¯ãç§ã«ã¯å°ã ããéãã«ãã¤ãã¾ããã¾ããfunction literal 㨠function expression ã®éããããã«ãããªããªãããã®ããã®ææ³ã®ä½¿ãåãã¯ç¾æç¹ã§ã¯éå°ã«æãã¾ãããã¤ãreturn æã lambda ããã®è±åºã«è¦ããã®ã§ã人ã«ãã£ã¦ã¯äºæ³å¤ã®æåãããå¯è½æ§ãããã§ãããããã¡ãããinline function ã§ãªãå ´åã¯ã³ã³ãã¤ã©ã¨ã©ã¼ã«ãããã¨ã§ä¸ç¨æãª return æã¯ç¦æ¢ãã¦ãã¾ãããfunction literal å ã§ã¯ç´ ã® return æã¯è¨±ãããã©ãã«ãä»ãããã¨ãå¿ é ã«ãã¦ãè¯ãã£ãã®ã§ã¯ãªããã¨æãã¾ãã
䏿¹ã§ãreified
modifier 㯠inline éå®ã¨ã¯ãããã¨ã¦ããããããæ©è½ã§ããJava ããã°ã©ãã³ã°ã§ã¯åæ
å ±ãæ¶å»ãããªãããã«æ§ã
ãªãã¯ããã¯ã使ããã¦ãã¾ããããããã£ããã®ã«é ¼ããã«æ¸ãã®ã¯ Java ããã°ã©ãã«ã¨ã£ã¦æå ±ã§ãããã
å¼ãç¶ã Kotlin ã®åå¼·ããã¦ããããã¨æãã¾ãã
ææ¥ã¯@RyotaMurohoshiããã«ãããKotlinÃAndroidã§ã¯ãªãä½ããã§ãã
Reference
- Higher-Order Functions and Lambdas
- Inline Functions
- kotlin/reified-type-parameters.md at master · JetBrains/kotlin
*1:å°ãªãã¨ã Kotlin ã®ã³ã³ãã¤ã©ã grep ããéã㯠https://github.com/JetBrains/kotlin/blob/4b8017e34b0842d717df82bbeb1a7f465e8a46fc/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java#L1843-L1843 ããããã大ããªå½±é¿ã¯ãªãããã§ãããä»ã«ãéãããããã¨ããåç¥ã®æ¹ã¯ãææããã ããã幸ãã§ãã