构造函数、继承

伴生对象

虽然您可以只创建一个名为“letter”的常量,但当您向应用添加更多 intent extra 时,代码可能会变得比较庞杂。另外,您应该将此常量放入哪个类呢?请记住,该字符串会同时用于 DetailActivity 和 MainActivity。您需要一种方法来定义常量,使其能在多个类中使用,同时保持代码的条理性。
  • Kotlin 中有一种便捷的功能叫伴生对象,可用来分离常量,使它们无需特定类实例即可使用。伴生对象与其他对象类似,例如某个类的实例。但是,在程序使用期间,只会有一个伴生对象实例存在,正因为如此,这有时被称为单例模式

属性委托

在 Kotlin 中,每个可变 (var) 属性都具有自动为其生成的默认 getter 和 setter 函数。当您为该属性赋值或读取其值时,系统会调用 setter 和 getter 函数。
只读属性 (val) 与可变属性略有不同,默认情况下仅为其生成 getter 函数。当您读取只读属性的值时,系统会调用此 getter 函数。
Kotlin 中的属性委托可以帮助您将 getter-setter 的责任移交给另一个类。
此类(称为“委托类”)提供属性的 getter 和 setter 函数并处理其变更。
delegate 属性使用 by 子句和 delegate 类实例进行定义:
  • 在Android使用属性委托
    • 那么,当设备经过配置变更后,应用会丢失 viewModel 引用的状态。例如,如果您旋转设备,activity 就会被销毁并重新创建,而您将重新获得初始状态的新视图模型实例。

幕后字段

在 Kotlin 中,字段仅作为属性的一部分在内存中保存其值时使用。字段不能直接声明。 然而,当一个属性需要一个幕后字段时,Kotlin 会自动提供。这个幕后字段可以使用 field  标识符在访问器中引用

后备属性

使用后备属性,可以从 getter 返回确切对象之外的某些其他内容。
我们已经学过,Kotlin 框架会为每个属性生成 getter 和 setter。
对于 getter 和 setter 方法,您可以替换其中一个方法或同时替换两个方法,并提供您自己的自定义行为。为了实现后备属性,您需要替换 getter 方法以返回只读版本的数据。
举例而言,在您的应用中,您需要应用数据仅对 ViewModel 可见:
在 ViewModel 类之内:
  • _count 属性设为 private 且可变。因此,只能在 ViewModel 类中对其进行访问和修改。惯例是为 private 属性添加下划线前缀。
在 ViewModel 类之外:
  • Kotlin 中的默认可见性修饰符为 public,因此 count 是公共属性,可从界面控制器等其他类对其进行访问。由于只有 get() 方法会被替换,所以此属性不可变且为只读状态。当外部类访问此属性时,它会返回 _count 的值且其值无法修改。这可以防止外部类擅自对 ViewModel 内的应用数据进行不安全的更改,但允许外部调用方安全地访问该应用数据的值。

延迟初始化

通常,在声明变量时,您会预先为其提供一个初始值。不过,如果您还没准备好赋值,也可以稍后再对其进行初始化。如需在 Kotlin 中对属性进行延迟初始化,请使用关键字 lateinit(意思是延迟初始化)。如果您能保证您会在使用前对属性进行初始化,就可以使用 lateinit 声明该属性。在对变量进行初始化之前,不会为其分配内存。如果您尝试在初始化变量之前对其进行访问,应用会崩溃。

接口

接口

函数式(SAM)接口

只有一个抽象方法的接口称为函数式接口 或 单一抽象方法(SAM)接口。函数式接口可以有多个非抽象成员,但只能有一个抽象成员。
  • SAM转换(Single Abstract Method Conversions):对于只有单个非默认抽象方法接口的转换
例如,有这样一个 Kotlin 函数式接口:
如果不使用 SAM 转换,那么你需要像这样编写代码:
通过利用 Kotlin 的 SAM 转换,可以改为以下等效代码:

扩展

Kotlin 能够对一个类扩展新功能而无需继承该类或者使用像 装饰者 这样的设计模式。 这通过做扩展 的特殊声明完成。
说白了就是不继承或者修改原类,给类添加扩展函数或扩展属性
  • 扩展函数
    • 声明一个扩展函数需用一个接收者类型也就是被扩展的类型来作为他的前缀。 下面代码为 MutableList<Int> 添加一个swap 函数:
    • 扩展是静态解析的
      • 扩展函数是静态分发的,即他们不是根据接收者类型的虚方法。 调用的扩展函数是由函数调用所在的表达式的类型来决定的, 而不是由表达式运行时求值结果决定的
    • 成员函数与扩展函数有相同的接收者类型、名字,且都适用给定的参数,这种情况下总是取成员函数
      • 扩展函数可以重载成员函数
    • 可空接收者
  • 扩展属性
    • 由于扩展没有实际的将成员插入类中,因此对扩展属性来说幕后字段是无效的。这就是为什么扩展属性不能有初始化器。他们的行为只能由显式提供的 getter/setter 定义。
  • 伴生对象的扩展
  • 扩展作用域
    • 大多数情况都在顶层定义扩展 — 直接在包里
    • 如需使用在包之外的一个扩展,只需在调用方导入它
  • 扩展声明为成员
    • 可以在一个类内部为另一个类声明扩展。在这样的扩展内部,有多个隐式接收者—— 其中的对象成员可以无需通过限定符访问。扩展声明所在的类的实例称为分发接收者,扩展方法调用所在的接收者类型的实例称为扩展接收者

数据类

密封类

嵌套类

枚举类

内联类

对象表达式

类型别名

线程

协程

协程能够处理多任务,但比直接使用线程更为抽象。协程的一项重要功能是能够存储状态,以便协程可以暂停和恢复。协程可以执行,也可以不执行。
  • launch()
eg:
  • runBlocking()
    • 启动新协程并在新协程完成之前阻塞当前线程。它主要用于在主要函数和测试中的阻塞代码和非阻塞代码之间架起桥梁。该函数在典型的 Android 代码中并不常用。
      感觉有点类似把异步搞成同步?
      eg:
  • async()
    • Kotlin 的 async 函数与 launch 类似。
  • suspend

Else

可见性修饰符

类、对象、接口、构造函数、方法与属性及其 setter 都可以有可见性修饰符private、 protected、 internal和 public。 默认可见性是 public
    • 如果你不使用任何可见性修饰符,默认为 public,这意味着你的声明将随处可见。
    • 如果你声明为 private,它只会在声明它的文件内可见。
    • 如果你声明为 internal,它会在相同模块内随处可见。
    • protected 修饰符不适用于顶层声明。
    • private 意味着只该成员在这个类内部(包含其所有成员)可见;
    • protected 意味着该成员具有与 private 一样的可见性,但也在子类中可见。
    • internal 意味着能见到类声明的本模块内的任何客户端都可见其 internal 成员。
    • public 移位置能见到类声明的任何客户端都可见其 public 成员。
  • 构造函数
  • 局部声明
    • 局部变量、函数和类不能有可见性修饰符。
  • 模块
    • 可见性修饰符 internal意味着该成员只在相同模块内可见

Elvis 运算符 (?:)

表示如果左侧的表达式不为 null,则使用该表达式。但如果左侧的表达式为 null,请使用 Elvis 运算符右侧的表达式。

Lambda表达式

code episode 01是02的简写形式,基于两点特性:
  1. 如果 lambda 是一个函数的唯一参数,那么调用这个函数时可以省略圆括号
  1. 如果 lambda 所表示的匿名函数只有一个参数,那么可以省略它的声明以及->符号(默认会用it来给省略的参数名命名)
  • 尾随lambda语法
    • 当传入的最后一个参数是函数时,您可以将 lambda 表达式放在圆括号外。

高阶函数

Else

    1. 安全操作值的函数
    1. apply: apply 是 Kotlin 标准库中的作用域函数。它在对象的上下文中执行代码块。它会形成一个临时作用域,在该作用域中,您可以访问对象而不需要其名称。apply 的常见用例是配置对象。此类调用可以解读为“对对象应用以下赋值”。
    1. repeat()
    1. 范围
    badge