Kotlin 基础 (持续更新)

104 阅读4分钟

函数

高阶函数

高阶函数是以函数为参数或返回函数的函数

函数类型

所有函数类型都有一个带括号的参数类型列表和一个返回类型:(A, B) -> C表示一个类型,参数类型列表可能为空,如() -> A。返回Unit类型不能省略。函数类型也为空(()->A)?

::

双冒号操作符表示把一个方法当做一个参数,传递到另一个方法中进行使用 如 foo::toString

Lambda表达式和匿名函数

Lambda 表达式和匿名函数都是函数文字。函数字面量是未声明但立即作为表达式传递的函数

max(strings, { a, b -> a.length < b.length })

第二个参数相当于以下命名函数

fun compare(a: String, b: String): Boolean = a.length < b.length
val sum = {x: Int, y: Int -> x + y}
  • lambda 表达式始终用花括号括起来
  • 完整语法形式的参数声明位于花括号内,并具有可选的类型注释 身体追赶着 -> lambda主体内的最后一个表达式将被视为返回值

传递尾随lambda

根据kotlin约定,如果函数的最后一个参数是函数,则可以将作为相应参数传递的lambda表达式放在括号之外

val priduct = items.fold(1) {acc, e -> acc * e}

如果lambda 是该调用中的唯一参数,则可以完全省略括号 it 单个参数的隐式名称

匿名函数

fun (x: Int, y: Int): Int = x + y fun sum(x: Int, y: Int): Int = x + y

带接收器的函数类型

A.(B) -> C

val sum: Int.(Int) -> Int = {...}

关键字

展开运算符 *

fun foo(vararg strings: String) { /*...*/ }
foo(strings = *arrayOf("a", "b", "c"))

val a = arrayOf(1, 2, 3)
val list = listOf(-1, 0, *a, 4)

中缀方法 infix

kotlin 允许在不使用句点和括号的情况下调用某些函数。这些成为中缀方法 map(1 to "one") 其实 to 是一个利用中缀表示法并返回Pair<A,B>的to()方法

inline fun

使用高阶函数会带来一定的损失,因为每个作为参数的函数都是一个对象,并且捕获一个闭包,闭包是可以在函数体内访问的变量范围。内存分配(函数对象和类)和虚拟调用会引入运行时开销。但在许多情况下,我们需要把该函数“直接内敛到函数内部”,而不是为参数创建函数对象并调用

public final class MainActivity extends AppCompatActivity {
   protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      this.setContentView(2131427356);
      int $i$f$test1 = false;
      int var4 = false;
      String var5 = "act end";
      System.out.println(var5);
      String var6 = "end";
      System.out.println(var6);
   }

   public final void test1(@NotNull Function0 act) {
      int $i$f$test1 = 0;
      Intrinsics.checkNotNullParameter(act, "act");
      act.invoke();
      String var3 = "end";
      System.out.println(var3);
   }
}

可以看到,使用内联函数生成的字节码中,test1函数中的代码被copy到了onCreate函数中,并没有生成额外的类和对象。 这时如果我们调用test1 {return} 是不用@具体的标签名的,可以直接return最外层的函数。

noinline

如果我们不希望传递给内敛函数的所有lambda都被内敛,可以使用noinline 但是使用noline的函数的lambda表达式中,必须使用局部返回(带@标签的return)

crossinline

一些内联函数可能会将自身拥有的函数类型参数实例的调用放在来自另一个上下文作用域的Lambda表达式或者一个匿名类中,这称之为间接调用,间接调用是不支持裸return的,给有间接调用的函数加上crossinline才不会编译器报错,这告诉编译器我们一定不会在该函数中裸return,而是使用局部返回。

reified

image.png 内联函数支持具体化类型参数

Flow

Flow是一种冷流 StateFlow和SharedFlow却是一种热流 区别在于冷流只有在收集者开始收集数据的时候,它才会去发射数据

MutableStateFlow

MutableStateFlow会创建一个StateFlowImpl对象,内部维护着_state的atomic对象,此对象的初始值为我们传入的value,每次发送新数据它也会随着改变成最新的值保存,所以收集者会收集到MutableStateFlow的默认值,并且在屏幕旋转之后还是会收集到最新的数据,所以StateFlow适用于存储和界面相关的数据,这样在屏幕旋转或者activity意外重建的情况下也能保证界面之前的信息不会丢失。

SharedFlow

SharedFlow适用于弹Toast、Dialog等场景,它不需要保存状态,就是执行一次性动作。

lifecycleScope.launch {
    viewModel.stateFlow.collect {
        Log.d("collect", "$it")
    }
}
lifecycleScope.launch {
    for (i in 2..10) {
        viewModel.stateFlow.emit(i)
        delay(100)
    }
}
/// if MutableStateFlow {0,2,3,4......}
/// if MutableSharedFlow {2,3,4......}