Kotlin 高阶技巧

30 阅读2分钟

一、高阶函数

高阶函数定义:函数作为参数或者返回值的函数,被称为高阶函数,如下:

1. 函数作为参数

fun main(block: () -> Unit) {
    println(block.invoke())
}

2. 函数作为返回值

fun main(block: () -> Unit): (String) -> Unit {
    println(block.invoke())
    return {} // 返回一个带 String参数的函数
}

3. 高阶函数的作用

  1. 避免创建过多的接口
  2. 使用不当,可能会降低可读性
  3. 在传入函数参数的时候,实际上是创建了匿名内部类,可使用 inline 进行优化

二、Sealed 类

被 Sealed 修饰的类称为密封类,其好处是在作为枚举的时候,能够在编译期穷举所有的状态,避免出现遗漏。

sealed class Status {
    companion object LOADING
    class SUCCESS(val content: String) : Status()
    class FAIL(val msg: String) : Status()
}

三、扩展函数

扩展函数定义:在不修改原类源码的情况下,给某个类型“增加”函数,示例如下:

fun String.copy(content: String): String {
    return this + content
}

扩展函数实际上并没有修改原来的代码,上述代码转成 Java 代码如下:

public final class AKt {
   @NotNull
   public static final String copy(@NotNull String $this$copy, @NotNull String content) {
      Intrinsics.checkNotNullParameter($this$copy, "<this>");
      Intrinsics.checkNotNullParameter(content, "content");
      return $this$copy + content;
   }
}

Kotlin 的扩展函数实际上:

  1. 基于当前文件名生成:filename+Kt,该类是 final 类型
  2. 生成的函数类型是 static、final
  3. 把自身作为第一个参数传入函数

四、inline, noinline, crossinline

1. inline

  1. 消除 lambda 调用创建匿名类对象
  2. 降低调用栈过深的性能开销
fun show() {
    showFace { }
}

inline fun showFace(block: (String) -> Unit) {
    println("show face")
}

上述使用 inline 修饰了 showFace() 方法,实际上,show() 会被编译成如下的代码:

public void show() {
    String var1 = "show face";
    System.out.println(var1);
}

showFace() 的方法直接被 show() 调用。注意 inline 主要作用于高阶函数的作为参数场景,对普通的函数的性能提升影响较小。

2. noline

同样作用于高阶函数的场景,避免某个高阶函数的 lambda 参数被内联。

fun show() {
    showFace {}
}

// 第 9 行会在编译期出现报错,给 block 加上 noline 之后,才会消除报错
inline fun showFace(block: (String) -> Unit) {
    object : ItemClickListener{
        override fun click() {
            block.invoke("") // 报错!!!!!!!
        }
    }
}

interface ItemClickListener {
    fun click()
}

当出现如下场景,lambda 参数需要使用 noline 进行修饰:

  1. lambda 作为参数需要继续传递到下一个函数
  2. 在匿名内部类中被调用(如上实例)

3. crossinline

它的作用如下:禁止 lambda 使用 return 跳出调用者示例如下:

fun show() {
    foo {
        return      // ❌ 报错!!!!不允许非局部 return
        return@foo  // ✅ 正确!!!!允许局部返回
    }
}

inline fun foo(crossinline block: () -> Unit) {
    Runnable {
        block() 
    }.run()
}

还有几个概念:

  1. crossinline 和 inline 都是可以内联的
  2. lambda 需要作为参数传递的话,只能使用 noline 修饰,如果只是在匿名内部类调用,也可以使用 crossinline
  3. noline 和 crossinline 都可以禁止非局部返回