第四章
密封类 sealed
密封类用来表示受限的类继承结构:当一个值为有限集中的类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。
第五章 类型系统
as
不安全的类型转换,转换失败会抛类型转换失败异常。使用 as?
转换失败返回 null。
Int 等同于 int
Int? 等同于 Integer
协变与不变
java 中数组是协变的,意思就是任意 A 是 B 的父类,则 A[] 也是 B[] 的父类。
List 是不变的,只知道自己是一个 Lit,无法获取泛型参数的类型。Kotlin 中数值支持泛型,当然也不再协变。Any 是所有类的父类,但是你不能将任意一个对象数组赋值给 Array
Kotlin 中 List 是协变的。因为 Kotlin 中 List 的定义是 publick interface List<out E> : Collection<E> {}
,out 关键字,说明泛型类和泛型方法是协变的。为了保证类型安全,kotlin 中的 List 是无法添加元素的。
获取泛型类型
通过匿名内部类
泛型虽然是类型擦除的,但是类型信息是放在对应 class 的常量池中的。匿名内部类在初始化的时候会绑定父类或父接口的相应信息。
1
2val list = object: ArrayLisr<String>()
print(list.javaClass.genericSuperclass)
使用内联函数
kotlin 内联函数在编译的时候编译期会将相应的字节码插入调用的地方,也就是说,参数类型也会被插入字节码中。
1
2
3
4//在kotlin中一个内联函数(inline)可以被具体化(reified),这意味着我们可以得到使用泛型类型的Class
inline fun <reified T> getType() {
return T::class.java
}
需要注意的是,Java 并不支持指定一个函数是否内联,所以 reified 来实例化的内联函数不能在 Java 中调用,因为它永远是需要内联的。
协变和逆变
第 6 章 Lambda 和集合
序列 sequence
序列是惰性求值(延时求值)的,普通集合进行链式操作是(例如 filter、map),每一步都会先产生新的集合,而序列则是将所有操作都应用在一个元素上,当第一个元素执行完 filter、map 后,第二个元素再继续执行 filter、map。
与 Java8 的流(Stream)不同,流是一次性的,只能遍历一次。
内联函数
inline 修饰的函数,会被内联进调用它的地方。直接在字节码中生成相应的函数体实现
实现非局部返回
1 | fun localReturn() { |
因为函数体中的 return 只会在该函数的局部生效
1 | fun foo(returning: () -> Unit) { |
这时可以使用内联函数,
1 | inline fun foo(returning: () -> Unit) { |
还可以使用标签实现 Lambda 非局部返回
1 | fun main() { |
第七章 多态和扩展
运算符重载
operator 关键字叫上 Kotlin 规定的函数名:plus(加法),minus(减法),times(乘法),div(除法),mod(取余)等
1 | data class Area(val valus: Double) |
扩展函数实现机制
可以将扩展函数理解为静态方法。
使用扩展函数添加属性:
1 | val Mutablelist<Int>.sumIsEven: Boolean |
静态与动态调度
调用重载方法时,调用变为静态并且取决于编译时类型
1 | void foo(Base base) {} |
第九章 设计模式
装饰者模式
- 使用类委托减少样板代码
使用 by 关键字,将装饰类的所有方法委托给被装饰对象,然后只需要复写需要装饰的方法即可。
- 使用扩展函数代替装饰者
第十章 函数式编程
惰性求值
也称正则序,当用到时才求值
第十一章 异步和并发
同步与异步
同步指的是一个行为,当执行的时候,在代码层面需要我们主动去等待结果,直到结果返回。
多线程执行时看上去是同步执行,但是线程执行是通过 CPU 调度,CPU 在每个线程间快速切换,同一个时间片内只能执行一个线程。
协程
协程是一个无优先级的子程序调度组件,允许子程序。线程包含于进程,协程包含于线程。只要内存只够, 一个线程中可以有任意多个协程,但某一个时刻只能有一个协程在运行,多个协程共享该线程分配到的计算机资源。
launch 和 runBlocking
runBloking 是最高级的协程,也就是主协程。launch 创建的线程能够在 runBlocking 中运行,反过来不行。runBlocking 会阻塞当前执行的线程。
join
1 | val job = launch { |
join 后程序会一直等待,直到启动的协程结束。这里的等待是非阻塞式的等待,不会将当前线程挂起。
async
会创建一个子协程,会和其他子协程一样并行工作。async 会返回一个 Deferred 对象。Deferred 值是一个非阻塞可取消的 future,是一个带有结果的 job。
future 的意思是,将来会返回一个结果,利用 await 方法可以等待这个值的结果。