首页 Kotlin 正文
  • 本文约3726字,阅读需19分钟
  • 90
  • 0

Kotlin内联(inline)功能何时使用?

摘要

什么是内联函数? 为了使函数内联,我们将在fun之前添加inline关键字,以将正常函数转换为内联函数 inline fun calculateTime() { println(Calculate) } fun main() { println(Start) calculateTime() println(End) } 编译结果 public void ma...

什么是内联函数?

为了使函数内联,我们将在fun之前添加inline关键字,以将正常函数转换为内联函数

inline fun calculateTime() {
    println("Calculate")
}

fun main() {
    println("Start")
    calculateTime()
    println("End")
}

编译结果

public void main() {
   System.out.println("Start");
   System.out.println("Calculate");
   System.out.println("End");
}

正如我们所看到的,内联函数的完整主体在编译时被插入函数调用站点。

正如我们所看到的,内联函数的完整主体在解编译时被插入函数调用站点。

内联函数的优势:无函数调用开销——更快的程序执行。

为什么不让每个功能都内联呢?

使每个函数内联最终会增长代码,因为相同的代码将在任何地方重复。

有些人会说,如果功能体很小,则使用内联,否则不会。这在某种程度上是正确的。

但是,当我们用正常参数内联正常函数时,编译器会给我们以下警告

Expected performance impact from inlining is insignificant. Inlining works best for functions with parameters of functional types 因此,这意味着内联对性能的预期影响可以忽略不计,因为如果需要,编译器很可能会这样做。

我们什么时候内联功能?

当函数采用函数类型参数(lambdas)时,我们通常更喜欢内联函数

让我们深入研究一下原因,以及在这种情况下内衬实际上如何帮助我们

在正常情况下:

fun calculateTime(block: ()->Unit): Long {
    val initialTime = System.currentTimeMillis()
    block.invoke()
    return System.currentTimeMillis() - initialTime
}

fun main() {
    val time = calculateTime {
        println("Hello")
    }
    println(time)
}

编译结果

public long calculateTime(Function0 block) {
  long initialTime = System.currentTimeMillis();
  block.invoke();
  return System.currentTimeMillis() - initialTime;
}

public void main() {
  long time = calculateTime(
     new Function() {
        @Override
        public void invoke() {
          System.out.print("Hello");
        }
      }
    );
  System.out.println(time);
}

public interface Function0<out R> : Function<R> {
    public operator fun invoke(): R
}

在Kotlin中,函数类型(lambdas)转换为扩展接口的匿名/常规类(Function0)的对象Function

问题?—如果我们调用此函数(calculateTime)100次,将创建100个Function类对象并收集垃圾。这会影响性能。

解决方案?—使用内联来防止对象创建

inline fun calculateTime(block: ()->Unit): Long {
    val initialTime = System.currentTimeMillis()
    block.invoke()
    return System.currentTimeMillis() - initialTime
}

fun main() {
    val time = calculateTime {
        println("Hello")
    }
    println(time)
}

编译结果

public void main() {
  long initialTime = System.currentTimeMillis();
  System.out.println("Hello");
  long time = System.currentTimeMillis() - initialTime;
  System.out.println(time);
}

因此,将其他函数作为参数的函数在内联时会更快(因为没有创建Function对象)。

当我们有具有小函数体的函数类型参数时,我们应该使用内联函数。

noinline

如果我们在内联函数中有多个函数类型参数,并且我们不想内联所有参数,我们可以使用noinline关键字。

fun main() {
    val time = calculateTime({
        println("Hellow")
    }, {
        println("World")
    })
    println(time)
}

inline fun calculateTime(block1: () -> Unit, noinline block2: () -> Unit): Long {
    val initialTime = System.currentTimeMillis()
    block1.invoke()
    block2.invoke()
    return System.currentTimeMillis() - initialTime
}

编译结果

public void main() {
  long initialTime = System.currentTimeMillis();
  System.out.println("Hello");
  Function block = new Function() {
        @Override
        public void invoke() {
          System.out.print("World");
        }
      }
    );
  block.invoke();
  long time = System.currentTimeMillis() - initialTime;
  System.out.println(time);
}

crossinline

crossinline关键字用于避免非本地返回。 让我们用例子来理解

inline fun calculateTime(block: () -> Unit): Long {
    val initialTime = System.currentTimeMillis()
    block.invoke()
    return System.currentTimeMillis() - initialTime
}

fun main() {
    val time = calculateTime {
        println("Hello")
        return
    }
    println(time)
}

编译结果

public void main() {
   long initialTime = System.currentTimeMillis();
   System.out.println("Hello");
}

在这里,我们可以看到,返回后,没有写其他声明(计算和打印最终时间)。这是非本地回报。为了避免这个问题,我们可以将lambda标记为crossinline

inline fun calculateTime(crossinline block: () -> Unit): Long {
    val initialTime = System.currentTimeMillis()
    block.invoke()
    return System.currentTimeMillis() - initialTime
}

fun main() {
    val time = calculateTime {
        println("Hello")
        return // This will give compile time error
    }
    println(time)
}

当将lambda参数标记为crossinline时,如果我们添加return语句,编译器将给出错误(‘return’ is not allowed here)。

将内联用于泛型类型参数

如果我们想直接在Kotlin泛型中处理类类型呢?

fun <T> doSomething(value: T) {
   println("Value: $value") // OK
   println("Type: ${T::class.simpleName}") // Error
}

fun main() {
   doSomething("something")
}

在上面的示例中,我们得到了错误 Cannot use ‘T’ as reified type parameter. Use a class instead

我们不能直接处理该类型,因为当我们传递给该函数时,类型参数在运行时被擦除。因此,我们不可能确切知道我们正在处理哪种类型。

解决方案?—使用内联函数和reified类型参数。

inline fun <reified T> doSomething(value: T) {
   println("Value: $value") // OK
   println("Type: ${T::class.simpleName}") // OK
}

fun main() {
   doSomething("something")
}

在上述示例中,实际类型参数将代替T。因此,T::class.simpleName变为String::class.simpleName

reified关键字只能与内联函数一起使用。

标签:inline

扫描二维码,在手机上阅读
    评论