Kotlin内联函数,参数化

时间:2020-02-23 14:37:30  来源:igfitidea点击:

在本教程中,我们将研究Kotlin内联函数。
接下来,我们将使用Reified Type Parameters。

Kotlin内联函数

我们之前已经讨论过Kotlin高阶函数和Lambda表达式。
在将函数作为参数传递时,它们非常有用。
但是这些函数是具有自己的回调以及随后的内存分配的对象。
让我们通过示例了解这些函数作为参数传递给内部的方式。

fun main(args: Array<String>) {

  println("Hey how are you doing")
  sampleFunction("theitroad.local", ::println)
}

fun sampleFunction(str: String, expression: (String) -> Unit) {
  println("This is Kotlin Inline Functions Tutorial")
  expression(str)
}

运行时,sampleFunction传递println作为参数。
现在Kotlin是一种基于JVM的语言,因此所有内容都将转换为字节码。
让我们转到"工具" |"查看" |"上面的代码"。
科特林|显示字节码。

主要部分是" expression.invoke()"。
调用lambda表达式(println)将创建一个另外的调用,从而创建一个内存。
Java中的invoke方法如下所示:

expression(new Function() {
      @Override
      public void invoke() {
       //println statement is called here.

      }
  });

现在,如果我们将多个函数作为参数调用,则每个函数的总和将增加方法数量,并对内存和性能产生巨大影响。

内联函数进行救援!

内联函数通过在运行时向调用函数提供函数主体和所有内容,从而使函数调用变得平坦。
我们需要添加inline修饰符来做到这一点。
因此,当调用sampleFunction时,上面的sampleFunction看起来像下面的代码。

inline fun sampleFunction(str: String, expression: (String) -> Unit) {
  print("This is Kotlin Inline Functions Tutorial")
  expression(str)
}

" inline"关键字将功能复制到呼叫站点。
这样可以节省另外的对象创建以调用参数函数,从而为您节省了内存。

现在通过反编译来查看字节码。

注意:printlnlambda表达式在main函数本身中以System.out.println的形式扩展。
不再需要其他电话。

为什么不使每个函数都内联?

  • 内联函数将代码复制到一个位置,从而增加了生成的代码。
    当通过参数调用的函数已经具有大代码时,应避免使用。

  • 此外,内联函数无法访问封闭类的"私有"成员。
    您必须将它们设置为"内部"

下面是使用内联函数的示例:

fun main(args: Array<String>) {
  normalFunction()
}

fun normalFunction() {
  println("This is normal function.")
  inlineFunctionExample({ println("Inlined Functions")},{ println("Instead of object creation it copies the code.")} )
}

inline fun inlineFunctionExample(myFunction: () -> Unit, another: () -> Unit  ) {
  myFunction()
  another()
  print("Finally it's working fine!")
}

在上面的代码中,我们传递了许多lambda表达式。
所有这些都将在运行时复制。

以下是在IntelliJ中生成的字节码:

所有的printlnlambda调用都在normalFunction本身中被展平。
输出占用更少的内存。

内联允许非本地控制流

使用内联函数,您可以从lambda表达式本身返回,并退出调用内联函数的函数。
下面的代码片段演示了相同的内容。

fun main(args: Array<String>) {
  normalFunction()
}

fun normalFunction() {
  println("This is normal function.")
  inlineFunctionExample({ println("Inlined Functions")
      return},{ println("Instead of object creation it copies the code.")} )

  println("This is normal function closing")
}

inline fun inlineFunctionExample(myFunction: () -> Unit, another: () -> Unit  ) {
  myFunction()
  another()
  print("Finally it's working fine!")
}

如您所见,内联函数及其封闭函数都已退出。
我们不能从作为普通函数(非内联函数)一部分的lambda表达式返回。

为了防止这种情况,我们可以将lambda表达式标记为" crossinline"。
如果在该lambda表达式中看到return语句,则会引发编译器错误。

fun normalFunction() {
  println("This is normal function.")
  inlineFunctionExample(
  { println("Inlined Functions")
      return //compiler error here
  },
  { println("Instead of object creation it copies the code.")}
  )

  println("This is normal function closing")
}

inline fun inlineFunctionExample(crossinline myFunction: () -> Unit, another: () -> Unit  ) {
  myFunction()
  another()
  print("Finally it's working fine!")
}

Noinline

" noinline"修饰符用于设置表达式在调用中不被"内联"。

fun main(args: Array<String>) {
  normalFunction()
}

fun normalFunction() {
  println("This is normal function.")
  inlineFunctionExample({ println("Inlined Functions")},
          { println("Instead of object creation it copies the code.")} )

  println("This is normal function closing")
}

inline fun inlineFunctionExample(myFunction: () -> Unit, noinline another: () -> Unit  ) {
  myFunction()
  another()
  print("Finally it's working fine!")
}

内联属性

属性还允许使用Kotlin内联关键字。
就像内联函数一样,它将内联属性访问器方法复制到调用站点。
内联属性不能有后备字段。

fun main(args: Array<String>) {
  print(x)
}

var i = 10
inline var x: Boolean
  get() = i == 11
  set(x) { x}

我们也可以在get和set方法上分别设置内联。

修饰类型参数

参数的类型无法在代码中检索,因为它会在运行时删除。
对于内联函数,虽然可以使用Reified。
为了检索参数的类型,我们可以对其设置一个" reified"修饰符。
为什么这样做?内联函数会在运行时复制完整的函数,因此只要我们对其进行了具体化设置,参数的类型也将可用。

fun main(args: Array<String>) {
  getT<Double>()
}

inline fun <reified T> getT() {
  print(T::class)
}