Kotlin重要知识点

文章目录

  • Kotlin编译器原理
  • 一,语法基础篇
    • 1.1 interface
    • 1.2 类的嵌套
    • 1.3 密封类
    • 1.4 object关键字
    • 1.5 扩展函数或扩展属性
    • 1.6 高阶函数
    • 1.7 inline
  • 二,协程
    • 2.1 简单使用协程
    • 2.2 suspend关键字
    • 2.3 Flow(数据流)
    • 2.4 Channels(通道)
  • 三, 面试题

Kotlin编译器原理

Kotlin和Java本质上都是使用的Java字节码,如下图所示:
Kotlin重要知识点_第1张图片
以下内容节选本文

编译主要分为四个过程:

  1. 词法分析
  2. 语法分析
  3. 语义分析及中间代码生成
  4. 目标代码生成

词法分析

其实词法分析就是将源程序读入的字符序列,按照一定的规则转换成词法单元(Token)序列的过程。词法单元是语言中具有独立意义的最小单元,包括关键字、标识符、常数、运算符、界符等等。

语法分析

语法分析的过程是建立在词法分析的基础上,将单词(Token)序列组合成各类语法短语,如“程序”,“语句”,“表达式”等等,语法分析器将判断源程序在结构上是否正确。

KotlinParser 语法分析器调用 KotlinParsing 进行语法分析,并生成AST抽象语法树。关于如何生成一个简单表达式的AST树,可以参考下图:

Kotlin重要知识点_第2张图片

一,语法基础篇

1.1 interface

Kotlin 的接口,跟 Java 最大的差异就在于,接口的方法可以有默认实现,同时,它也可以有属性。

1.2 类的嵌套

Kotlin 当中的普通嵌套类,它的本质是静态的。相应地,如果想在 Kotlin 当中定义一个普通的内部类,我们需要在嵌套类的前面加上 inner 关键字。

1.3 密封类

定义密封类需要使用 sealed 关键字,它的中文含义也代表着“密封”。在 Android 开发当中,我们会经常使用密封类对数据进行封装。比如我们可以来看一个代码例子:


sealed class Result<out R> {
    data class Success<out T>(val data: T, val message: String = "") : Result<T>()

    data class Error(val exception: Exception) : Result<Nothing>()

    data class Loading(val time: Long = System.currentTimeMillis()) : Result<Nothing>()
}

1.4 object关键字

首先 object 定义的匿名内部类。object 关键字定义单例类的时候,Kotlin 编译器会将其转换成静态代码块的单例模式。
companion object半生对象:是 Java 中的 static 变量,只能定义在对应的类中。
模板单例:

abstract class BaseSingleton<in P,out T> {
    @Volatile
    private var instance: T? = null
    protected abstract fun creator(param: P): T
    fun getInstance(param: P): T = instance ?: synchronized(this) {
        instance ?: creator(param).also { instance = it }
    }
}

1.5 扩展函数或扩展属性

Kotlin 的扩展,由于它本质上并没有修改接收类型的源代码,所以它的行为是无法与“类成员”完全一致的。那么它对比普通的类成员,就会有以下几个限制。

  • 无法被它的子类重写
  • 扩展属性无法存储状态,背后的根本原因,还是因为它们都是静态方法。
  • 扩展的访问作用域仅限于两个地方。第一,定义处的成员;第二,接收者类型的公开成员。

1.6 高阶函数

Lambda表达式
SAM 代表着只有一个抽象方法的接口

1.7 inline

内联函数的工作原理很简单,就是 Kotlin 编译器会将内联函数中的代码在编译的时候自动替换到调用它的地方,这样也就不存在运行时的开销了。

二,协程

大神的讲解

比较全面的讲解

系列讲解

Kotlin重要知识点_第3张图片

  • 协程,可以理解为更加轻量的线程,成千上万个协程可以同时运行在一个线程当中;
  • 协程,其实是运行在线程当中的轻量的 Task;
  • 协程,不会与特定的线程绑定,它可以在不同的线程之间灵活切换。

协程之所以是非阻塞,是因为它支持“挂起和恢复”;而挂起和恢复的能力,主要是源自于“挂起函数”;而挂起函数是由 CPS 实现的,其中的 Continuation,本质上就是 Callback。
Kotlin重要知识点_第4张图片

2.1 简单使用协程

fun main() {
    GlobalScope.launch(context = Dispatchers.IO) {
        //延时一秒
        delay(1000)
        log("launch")
    }
    //主动休眠两秒,防止 JVM 过快退出
    Thread.sleep(2000)
    log("end")
}

private fun log(msg: Any?) = println("[${Thread.currentThread().name}] $msg")
[DefaultDispatcher-worker-1] launch
[main] end

上边代码,通过 GlobalScope(全局作用域)启动了一个协程,从输出结果可以看出来,启动的协程是运行在协程内部的线程池中。

@DelicateCoroutinesApi
public object GlobalScope : CoroutineScope {
    /**
     * Returns [EmptyCoroutineContext].
     */
    override val coroutineContext: CoroutineContext
        get() = EmptyCoroutineContext
}

CoroutineScope
协程作用域,GlobalScope 是 CoroutineScope 的一个实现类,用于指定协程的作用范围,可用于管理多个协程的生命周期,所有协程都需要通过 CoroutineScope 来启动。

CoroutineContext
协程上下文,包含多种类型的配置参数。Dispatchers.IO 就是 CoroutineContext 这个抽象概念的一种实现,用于指定协程的运行载体,即用于指定协程要运行在哪类线程上

2.2 suspend关键字

挂起协程之所以是非阻塞,是因为它支持“挂起和恢复”;而挂起和恢复的能力,主要是源自于“挂起函数”;挂起函数,只能在协程作用域当中被调用,或者是被其他挂起函数调用。

挂起,只是将程序执行流程转移到了其他线程,主线程并未被阻塞。

挂起函数是由 CPS 实现的,其中的 Continuation,本质上就是 Callback。

2.3 Flow(数据流)

https://juejin.cn/post/6844904101801639949
https://juejin.cn/post/7040001489904861191

背压
https://juejin.cn/post/7088338829316210702

2.4 Channels(通道)

https://juejin.cn/post/6844904102040698893

三, 面试题

https://blog.csdn.net/gongjdde/article/details/124001671

你可能感兴趣的:(kotlin系列,kotlin,android,协程)