kotlin inline、noline、crossinline、reified

使用高阶函数时,每个函数都是一个对象,函数调用时还有入栈出栈的开销。

以lock函数为例

fun <T> lock(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        return body()
    } finally {
        lock.unlock()
    }
}

调用处

  val l = ReentrantLock()
  lock(l) {

  }

反编译成JAVA

  ReentrantLock l = new ReentrantLock();
  KtxKt.lock((Lock)l, (Function0)null.INSTANCE);

lambda编译成了函数对象Function0,这样每次调用都会生成函数对象,频繁创建很不友好。

inline

加入inline关键字

inline fun <T> lock(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        return body()
    } finally {
        lock.unlock()
    }
}

inline 修饰符影响函数本⾝和传给它的 lambda 表达式:所有这些都将内联到调⽤处。

上述是官方解释,翻译成人话:inline修饰的函数,其本身和参数lambda表达式的内容会直接替换到调用处。省去了函数调用的开销,和生成函数对象。

调用处

  val l = ReentrantLock()
  lock(l) {

  }

反编译成JAVA

  ReentrantLock l = new ReentrantLock();
  int $i$f$lock = false;
  ((Lock)l).lock();

  try {
      boolean var3 = false;
      Unit var6 = Unit.INSTANCE;
  } finally {
      ((Lock)l).unlock();
  }

加入inline关键字会产生一个新的问题:内联的 lambda 表达式只能在内联函数内部调⽤或者作为可内联的参数传递。

inline fun <T> lock(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        return get(body)
    } finally {
        lock.unlock()
    }
}

fun <T> get(body: () -> T): T {
    return body()
}

编译器会报错,想要将inline的lambda传递到非inline函数,需加上noinline关键字禁用内联。

noinline
inline fun <T> lock(lock: Lock, noinline body: () -> T): T {
    lock.lock()
    try {
        return get(body)
    } finally {
        lock.unlock()
    }
}

fun <T> get(body: () -> T): T {
    return body()
}
非局部返回

调用inline函数

  val l = ReentrantLock()
  lock(l) {
      return
  }

return可使包含该lambda的函数正常退出

调用非inline函数

  val l = ReentrantLock()
  lock(l) {
      return@lock
  }

需要显示指定标签返回

crossinline

⼀些内联函数可能调⽤传给它们的不是直接来⾃函数体、⽽是来⾃另⼀个执⾏上下⽂的 lambda 表达式参数,例如来⾃局部对象或嵌套函数。在这种情况下,该 lambda 表达式中也不允许⾮局部控制流。为了标识这种情况,该lambda 表达式参数需要⽤ crossinline 修饰符标记。

上述为官方解释,说白了就是不允许直接return返回包含lambda的函数,需显示指定标签返回。

var lastTime: Long = 0L

inline fun View.setSingleClick(crossinline onclick: (v: View?) -> Unit) {
    this.setOnClickListener {
        val currentTime = System.currentTimeMillis()
        if (currentTime - lastTime > 500) {
            onclick.invoke(it)
        }
        lastTime = currentTime
    }
}

调用处

  btContent.setSingleClick {
      return@setSingleClick
  }
具体化的类型参数

内联函数⽀持具体化的类型参数

inline fun <reified T> ktxClass() = T::class.java

通过reified关键字修饰泛型,可以直接获取泛型的类型。