基本类型:
数字
- Double Float Long Int Short Byte
- 没有隐式拓宽转换,但算数运算可转换
- 数字面值可以用下划线划分
- 可空化、泛型会把数字装箱
- 装箱后相等(==)但不同一(===)
- 位运算采用中缀方式
字符
- Char
- 不能直接当作数字
- 字符字面值用单引号括起来,特殊字符可以用反斜杠转义
- 没有隐式拓宽转换
- 可空引用时会被装箱,不会保留同一性
布尔值
- Boolean
- 需要可空引用时会被装箱
数组
- Array
- 有get、set、size方法
- arrayOf 、arrayofNulls 、Array创建数组
- 类型不可变
- ByteArray 、ShortArray 、IntArray 等原生类型数组无装箱开销
字符串
- String
- 可用[]访问字符串的元素、for循环迭代字符串
- 转义字符串,如Java字符串
- 原生字符串用 """ 分界符括起来
- 字符串模板,字符串中直接输出 {a+b}
控制流:
if
- if是一个表达式,即它会返回一个值
- if else 代替 三元运算 ? :
- if的分支可以是代码块,最后的表达式作为该块的值
when
- 代替switch, case -> ()
- 可以当作语句、表达式使用
- 多个分支条件可用逗号分隔作同样处理
- 可以用任意表达式(而不只是常量)作为分支条件,in、!in、is、!is等
- 如果 when 作为表达式使用,如果没有验证所有条件,则必须有 else 分支
for
- for 循环可以对任何提供迭代器(iterator)的对象进行遍历
- 遍历时, 数组或集合名.indices ,则通过索引遍历
- 遍历时,调用库函数 withIndex ,则是遍历 index+value组合
- 对数组的 for 循环会被编译为并不创建迭代器的基于索引的循环
while
- while 和 do..while 照常使用
返回和跳转
- 任何表达式都可以用标签(label)来标记;比如 loop@ for(… ,可用break标签:break@loop
- 可指定标签,返回指定处
Break
- 终止最直接包围它的循环
Continue
- 继续下一次最直接包围它的循环
return
- 默认从最直接包围它的函数或者匿名函数返回
- 从 lambda 表达式中返回可以用隐式标签
- 可返回值 如: return @a 1
类和继承:
构造函数
- 一个类可以有一个主构造函数和一个或多个次构造函数
- 主构造函数紧跟类名
- 若不声明,默认会有public无参的主构造函数
- 主构造函数不能包含任何的代码。初始化代码可以放到以 init 关键字作为前缀的初始化块中
- 主构造函数中声明的属性可以是可变的(var)或只读的(val),且属性可在初始化快或在类体内声明的属性初始化器中使用
- 若构造函数没有任何注解或者可见性修饰符,可以省略 constructor 关键字
- 次构造函数需要委托给主构造函数。 : this
继承
- 所有类都有一个共同的超类 Any
- 类默认final不可继承,加open才可继承
- 继承使用冒号
- 内部类中访问外部类的超类:super@Outer
- 子类访问多个超类时,用 super< Base> 区分超类型
覆盖方法
- 基类必须声明open且子类必须声明override
- override方法本身为open,可以加final禁止覆盖
- 接口及接口中方法、抽象类及抽象方法默认为open
覆盖属性
- 基类必须声明open且子类必须声明override
- var属性可以覆盖val属性,反之不行
- 伴生对象 companion object
- 没有静态方法,伴生对象代替
属性和字段:
- var表示可变的,val表示不可变的
- var可设置get、set方法,val可设置get方法
- kotlin1.1后get方法可推导出类型,不用声明
- 属性的get、set方法可用private等改变可见性
- 属性的get、set方法中可用field访问幕后字段
- 若幕后字段field不使用,可用幕后属性
- 用 const 修饰属性则成为编译期常量,只能形容val,且不能有set、get方法
- lateinit可延迟初始化属性
接口:
- 可有抽象方法和实现方法
- 属性必须为抽象属性或提供访问器,且没有幕后字段field
可见性修饰符:
- private 类内部可见
- protected 类内部及子类可见
- internal 类声明的本模块内任何客户端可见
- public 类声明的任何客户端可见
- 不修饰时默认为public
- 外部类不能访问内部类 private成员
- protected成员被覆盖后默认还为protected
- 修饰主构造函数可见性时不能省略constructor关键字
- 局部变量、局部函数和局部类不能有可见性修饰符
扩展:
- 扩展类功能无需继承或装饰
- 伴生对象也可扩展函数和属性
- 一般在顶层定义扩展
- 扩展函数和属性可以声明为open,子类中可覆盖
扩展函数:
- 扩展函数内部this指被扩展者即扩展接受者
- 若同名,成员函数会覆盖扩展函数
- 可以为可空接受者定义扩展
- 没有实际函数插入类中
扩展属性:
- 没有实际函数插入类中,无幕后字段
- 扩展属性不能有初始化器
- 扩展接受者和分发接收者:
- A类中扩展B类,A叫分发接受者,B叫扩展接受者
- 扩展函数中可直接调用A、B类中方法
- 若重名则优先调用B即扩展接受者
- 扩展函数中this指扩展接受者
- 扩展函数中调用分发接受者使用this@A 限定符
数据类:
- data修饰,主构造函数至少一个参数
- 若要有无参构造函数,则主构造函数使用默认值
- 主构造函数参数必须用var或val修饰
- 数据类不能是抽象、开放、密封或者内部的
- copy函数
密封类:
- sealed修饰,存放有限集中的类型(类似于枚举)
- 子类必须在同文件中,子类的子类可以不放在同文件中
- 密封类自身是抽象的,可有抽象成员,不能直接实例化,构造函数默认private
- 结合when表达式方便覆盖所有情况
泛型:
- 只能读取对象的叫生产者,只能写入的对象叫消费者
- T : U 表示T上界为U类型,默认上界为Any?
声明处型变
- out T表示只能生产返回,in T表示只能消费写入
类型投影
- out Any相当于 ? extends Object
- in String相当于 ? super String
星投影
- 对于 < in T,out U >,< , >表示< in nothing,out Any? >
嵌套类和内部类:
- 嵌套类声明在其他类中,不可访问外部类成员
- 内部类声明在其他类中,且用inner修饰,可访问外部类成员
- 内部类持外部类引用,嵌套类不持有外部类引用
枚举类:
- 用于实现类型安全的枚举
- 每个枚举常量都是一个对象,逗号隔开
- 枚举常量可以实现自己的匿名类
- 如果枚举类定义任何成员,要使用分号将成员定义中的枚举常量定义分隔开
- 可用valueOf和values访问枚举常量
- Kotlin 1.1 起,可用enumValues
和enumValueOf 以泛型的方式访问枚举常量
对象表达式和对象声明:
对象表达式:
- 对象表达式 object : A 代替匿名内部类的实现
- 如果超类型有构造函数,则必须传递适当的构造函数参数给它
- 多个超类型可以由跟在冒号后面的逗号分隔的列表指定
- 对象表达式中可以访问外部非final的变量
- 匿名对象可以⽤作只在本地和私有作⽤域中声明的类型; object {...}
对象声明(单例模式):
- object A{...} 表示A为单例类,可以继承子其他类
- 对象声明不是一个表达式,不能用在赋值语句的右边
- 对象声明不能在局部作用域(即直接嵌套在函数内部);但可以嵌套到其他对象声明或非内部类中
- 类内部用 companion object A 对象声明为伴生对象
- 伴生对象成员可通过类名.函数名调用,看起来像其他语言的静态成员
- 伴生对象名A可省略,引用时使用名称Companion
对象表达式和对象声明:
- 对象表达式在使用时立即初始化,对象声明第一次访问时延迟初始化
- 伴生对象在加载类时初始化
类委托:
- 很好的替代继承的方案
- class A(b: B) : B by b ,A调用B的方法会转发给b
委托属性:
- 格式: var/val 属性名: 类型 by 表达式
- get、set方法被委托给委托的getValue、setValue方法
- 属性的委托需要提供getValue、setValue方法
- val属性的委托只需提供getValue
- 用provideDelegate扩展委托逻辑
标准委托
- 延迟属性委托 by lazy
- lazy()是接受一个lambda并返回一个Lazy
实例的函数,返回的实例可以作为实现延迟属性的委托 - 默认情况下,对于 lazy 属性的求值是同步锁的(synchronized):该值只在一个线程中计算,并且所有线程会看到相同的值。可使用LazyThreadSafetyMode作为参数传递给Lazy()作为参数配置线程。
- 可观察属性委托 by Delegates.observable()
- 接受两个参数:初始值和修改时处理程序(handler)。每当给属性赋值时会调用该处理程序(在赋值后执行)。它有三个参数:被赋值的属性、旧值和新值。
- 截获赋值委托 by Delegates.vetoable
- 在属性被赋新值生效之前会调用传递给 vetoable 的处理程序
- 从map中取值委托 by map
- 在一个映射(map)里存储属性的值
属性委托要求
- val声明的只读属性,委托必须提供一个名为 getValue 的函数,函数接受thisRef(必须与属性所有者类型相同或者是它的超类型)和property(必须是类型 KProperty<*> 或其超类型)
- var声明的可变属性,委托还必须额外提供一个名为 setValue 的函数,函数接受thisRef和property和value(必须和属性同类型或者是它的超类型)
- getValue() 和 setValue() 函数可以通过委托类的成员函数提供或者由扩展函数提供;两函数都需要用 operator 关键字来进行标记
- 委托类可以实现包含所需 operator 方法的 ReadOnlyProperty 或 ReadWriteProperty 接口之一,Kotlin 标准库中声明的
kotlin Standard.kt里面的函数:
apply
- 格式: 对象名.apply { this }
- 在函数块内可以通过this指代该对象,返回值为该对象自己
with
- 格式: with(对象名) { this }
- 将某对象作为函数的参数,在函数块内可以通过this指代该对象。返回值为函数块的最后一行或指定return表达式。
let
- 格式: 对象名.let { it }
- 将对象作为函数的参数,在函数块内可以通过it指代该对象。返回值为函数块的最后一行或指定return表达式。
run
- 格式: 对象名.run { this }
- 可无参数输入,也可将对象本身this给函数调用,返回值为函数块最后一行,或者指定return表达式。
参数
默认参数
- 函数参数可以有默认值,当省略相应的参数时使用默认值;默认值通过类型后面的 = 及给出的值来定义。
- 当覆盖一个带有默认参数值的方法时,必须从签名中省略默认参数值。
命名参数
- 可以在调用函数时使用命名的函数参数
- 当一个函数调用混用位置参数与命名参数时,所有位置参数都要放在第一个命名参数之前。
- 可以通过使用星号操作符将可变数量参数(vararg)以命名形式传入;对于单个值不需要星号。
返回 Unit 的函数
- 函数不返回任何有用的值时,它的返回类型是 Unit;不需要显式返回; Unit 返回类型声明也是可选的。
单表达式函数
- 当函数返回单个表达式时,可省略花括号并且在 = 符号之后指定代码体。
- 当返回值类型可由编译器推断时,显式声明返回类型是可选的。
- 具有块代码体的函数必须始终显式指定返回类型,除非他们旨在返回 Unit。
可变数量的参数(Varargs)
- 只有一个参数可以标注为 vararg 。如果 vararg 参数不是列表中的最后一个参数,可以使用命名参数语法传递其后的参数的值。
- 调用 vararg 函数时,若已有一个数组并希望将其内容传给该函数,我们使用伸展(spread)操作符(在数组前面加 * )。
中缀表示法
- 当函数是成员函数或扩展函数,只有一个参数时,可用 infix 关键字标注函数,采用中缀表示法调用函数。
- 格式: 函数名 参数
局部作用域
局部函数
- 一个函数在另一个函数内部
- 局部函数可以访问外部函数(即闭包)的局部变量
成员函数
- 在类或对象内部定义的函数
- 以点表示法调用
尾递归函数
- 允许一些通常用循环写的算法改用递归函数来写,函数用 tailrec 修饰符标记,编译器会优化该递归,留下一个快速而高效的基于循环的版本
- 函数必须将其自身调用作为它执行的最后一个操作
- 在递归调用后有更多代码时,不能使用尾递归,并且不能用在 try/catch/finally 块中
- 目前尾部递归只在 JVM 后端中支持
内联函数
- 使用高阶函数会带来一些运行时的效率损失:每个函数都是一个对象,并且会捕获一个闭包。使用内联函数可避免。
- 接收lambda表达式作为参数的函数,可用 inline 标记函数内联,将影响函数本身和传给它的 lambda 表达式,优化操作。
- 禁用内联:再用 noinline 修饰符标记不想被内联的函数参数
- 当内联函数接收的是来自另一个执行上下文的 lambda 表达式参数,该 lambda 表达式中不允许非局部控制流,要用 crossinline 修饰该表达式。
高阶函数和 lambda 表达式
高阶函数
- 是将函数用作参数或返回值的函数
Lambda 表达式
- lambda 表达式总是被大括号括着;
- 其参数(如果有的话)在 -> 之前声明(参数类型可以省略);
- 函数体(如果存在的话)在 -> 后面。
- 可使用限定的返回语法(打标签)从 lambda 显式返回一个值;否则,将隐式返回lambda主体中最后一个表达式的值。
- 如果函数字面值只有一个参数,那么它的声明可以省略(连同 -> ),其名称是 it 。
- 如果 lambda 表达式的参数未使用,可用下划线取代其名称。(自1.1起)
- 如果一个函数接受另一个函数作为最后一个参数,lambda 表达式参数可以在圆括号参数列表之外传递。
匿名函数
- 函数名称省略,其函数体可以是表达式或代码块。
- 匿名函数参数总是在括号内传递
- lambda 表达式中的 return 将从包含它的函数返回,⽽匿名函数中的 return 将从匿名函数⾃⾝返回。
- Lambda 表达式或者匿名函数可访问并修改在外部作⽤域中声明的变量(闭包)
- 非局部返回:内联函数中的 Lambda 表达式中可使用 return ,退出一个 Lambda 表达式。
- 内联函数支持具体化的类型参数,用 reified 修饰符来限定类型参数,使可在函数内部调用,无需反射。
协程
挂起函数
- 避免阻塞线程并用更廉价、更可控的操作替代线程阻塞的方法:协程挂起
- 挂起函数用 suspend 修饰符标记,则会发生挂起
- 挂起函数只能从协程和其他挂起函数中调用
- 要启动协程,必须至少有一个挂起函数,它通常是匿名的(即它是一个挂起 lambda 表达式)。
- 目前挂起函数类型不能用作超类型,并且不支持匿名挂起函数。
- 挂起函数可以是虚拟的,当覆盖它们时,必须指定 suspend 修饰符
- 用 @RestrictsSuspension 标注类或接口,可保证所有挂起扩展都需要委托给被标注的类或接口的成员或其它委托给它的扩展。
集合
可变集合
MutableList, MutableSet, MutableMap, MutableCollection, MutableIterable
不可变集合
List, Set, Map, Collection, Iterable
创建集合
listOf(), mutableListOf(), setOf(), mutableSetOf(), mutableMapOf(), hashMapOf()......
集合操作符
- 总数操作符
- any -- 判断集合中是否有满足条件的元素,有则返回true
- all -- 判断集合中的元素是否全都满足条件,全满足则返回true
- none -- 判断集合是否都不满足条件,是则返回true
- count -- 查询集合中满足条件的元素个数
- reduce -- 从第一项到最后一项进行累计
- reduceRight -- 从第一项到最后一项进行累计
- fold -- 与reduce类似,不过有初始值,而不是从0开始累计
- foldRight -- 和reduceRight类似,有初始值,不是从0开始累计
- Each -- 循环遍历元素
- forEachIndexed -- 循环遍历元素,同时得到元素index(下标)
- max -- 查询最大的元素,如果没有则返回null
- maxBy -- 获取方法处理后返回结果最大值对应的那个元素的初始值,如果没有则返回null
- min -- 查询最小的元素,如果没有则返回null
- minBy -- 获取方法处理后返回结果最小值对应那个元素的初始值,如果没有则返回null
- sum -- 返回集合元素的总和
- sumBy -- 获取方法处理后返回结果值的总和
- dropWhile -- 返回从第一项起,去掉满足条件的元素,直到不满足条件的一项为止
- 过滤操作符(过滤后会返回一个处理后的列表结果,但不会改变原列表)
- filter -- 过滤出所有满足条件的元素
- filterNot -- 过滤所有不满足条件的元素
- filterNotNull -- 过滤NULL
- take -- 返回从第一个开始的n个元素
- takeLast -- 返回从最后一个开始的n个元素
- takeWhile -- 返回不满足条件的下标前面的所有元素的集合
- drop -- 返回去掉前N个元素后的列表
- dropLastWhile -- 返回从最后一项起,去掉满足条件的元素,直到不满足条件的一项为止
- slice -- 过滤非指定下标的元素,即保留下标对应的元素过滤list中
- 映射操作符
- map -- 遍历集合中的元素并通过某个方法转换后的结果存到一个集合中
- mapIndexed -- 除了得到转换后的结果,遍历时还可以拿到Index(下标)
- mapNotNull -- 执行方法转换前过滤掉为NULL的元素
- flatMap -- 转换集合,通过某个转换得到另外个集合
- groupBy -- 将集合中的元素按照某个条件分组,返回Map
- 顺序操作符
- reversed -- 相反顺序
- sorted -- 自然排序(升序)
- sortedBy -- 根据方法处理结果对对应的元素进行自然(升序)排序
- sortedDescending -- 降序排序
- sortedByDescending -- 根据方法处理结果对对应的元素进行降序排序
- 生产操作符
- zip -- 两个集合按照下标组合成一个个的Pair(Kotlin提供的用来处理双元对数据的,相当于Map中一个键值对)塞到集合中返回
- partition -- 根据判断条件是否成立,拆分成两个集合,通过.first或.second分别获取两个集合
- plus -- 合并两个List,可以用“+”替代
- unzip -- 将包含多个Pair的List转换成含List的Pair
- to -- 合并两个对象生成Pair; 例: A to B 得到 Pair(A, B)
- 元素操作符
- contains -- 判断集合中是否有指定元素,有返回true
- containsAll -- 判断集合是否包含另外个集合,有返回true
- elementAt -- 查找下标对应的元素,如果下标越界会抛IndexOutOfBoundsException
- elementAtOrElse -- 查找下标对应元素,如果越界会根据方法返回默认值(最大下标经方法后的值)
- elementAtOrNull -- 查找下标对应元素,越界会返回Null
- first -- 返回符合条件的第一个元素,没有抛NoSuchElementException
- firstOrNull -- 返回符合条件的第一个元素,没有 回null
- indexOf -- 返回指定下标的元素,没有返回-1
- indexOfFirst -- 返回第一个符合条件的元素下标,没有返回-1
- indexOfLast -- 返回最后一个符合条件的元素下标,没有返回-1
- last -- 返回符合条件的最后一个元素,没有抛NoSuchElementException
- lastIndexOf -- 返回符合条件的最后一个元素,没有返回-1
- lastOrNull -- 返回符合条件的最后一个元素,没有返回null
- single -- 返回符合条件的单个元素,如没有符合或超过一个,抛异常
- singleOrNull -- 返回符合条件的单个元素,如没有符合或超过一个,返回null
- 集合之间的运算
- intersect -- 交集
- subtract -- 差集
- union -- 并集
- minus -- 补集
- add -- 在集合末尾添加元素
- addAll -- 添加一个集合
- remove -- 移除元素
- removeAll -- 移除一个集合
区间
- 区间表达式由具有操作符形式 .. (正序)的 rangeTo 函数辅以 in 和 !in 形成
- 倒序 downTo 函数
- 步长 step 函数
- 不包括其结束元素的区间, until 函数
类型的检查与转换 “is” 与 “as”
is 与 !is 操作符
- 检查对象是否符合给定类型(相当于java的 instanceOf)
不安全的转换操作符as
- 若转换时不可能的,则抛出一个异常
- null 不能转换为 String ,因该类型不是可空的
安全的(可空)转换操作符as?
- 可在转换失败时返回 null
相等性
引用相等
- 引用相等由 ===(以及其否定形式 !== )操作判断
结构相等
- 结构相等由 ==(以及其否定形式 != )操作判断。
操作符重载
- 重载操作符的函数需要用 operator 修饰符标记
空安全
可空类型和非空类型
- 允许为空,在声明后加 ? ,仅val类型
在条件中检查 null?
安全的调用
- 安全调用操作符,写作 ?
Elvis 操作符 ?:
- 如果 ?: 左侧表达式非空, elvis 操作符就返回其左侧表达式,否则返回右侧表达式;
- 当且仅当左侧为空时,才会对右侧表达式求值。
!! 操作符
- b!! 返回一个非空值,若为空,则抛NPE异常
反射
类引用
- 获取 Kotlin 类引用: 类名::class
- 获取 Java 类引用: 类名::class.java
函数引用
- 格式: ::函数名
- 使用限定的类的成员函数或扩展函数: 类名::函数名
属性引用
- 格式: ::属性名
构造函数引用
- 应用类的无参构造函数: ::类名
类型别名
- 格式: typealias 别名 = 原类型名
Java互操作
在Kotlin中调用Java代码
- 遵循 Java 约定的 getter 和 setter 的方法在 Kotlin 中表示为属性
- 如果一个 Java 方法返回 void,那么从 Kotlin 调用时中返回 Unit
- Java 库使用了 Kotlin 关键字(in、object、is)作为方法,在Kotlin中可通过反引号(`)字符转义它来调用该方法。
- Java 中的任何引用都可能是 null,Java 声明的类型在 Kotlin 中会被特别对待并称为平台类型,对这种类型的空检查会放宽。
- Kotlin 只允许 is-检测星投影的泛型类型
- Kotlin 不允许我们把一个 Array
赋值给一个 Array - Kotlin 禁⽌把一个子类的数组当做超类的数组传递给 Kotlin 的方法
- 在 Kotlin 中,所有异常都是非受检的,编译器不会强迫去捕获异常
- Java中 getClass(),在Kotlin中可用 类名::class.java,Kotlin1.1后也可用 类名.javaClass
- Java 类的静态成员在Kotlin中会形成该类的“伴生对象”。无法将这样的“伴生对象”作为值来传递, 但可以显式访问其成员。
- 在Kotlin中使用JNI,要声明一个在本地(C 或 C++)代码中实现的函数,需要使用 external 修饰符来标记它。
Java中调用Kotlin
- Kotlin 属性在Java中会编译成一个getter方法,一个setter方法,一个私有字段
- kt文件中声明的所有函数和属性,在Java中都将编译成一个Java类的静态方法;Java类名默认为kt文件名,也可在文件的开头位于包名之前的地方使用 @JvmName 注解来修改生成的 Java 类的类名。
- 如果多个文件中生成了相同的 Java 类名,另外还要在所有相关文本件中使用 @JvmMultifileClass 注解。
- 如果需要在 Java 中将 Kotlin 属性(包括在命名对象或伴生对象中声明的属性)作为字段暴露成为可见的静态字段,那就需要使用 @JvmField 注解对其标注,或者使用 lateinit 或 const 修饰。Kotlin属性要求有幕后字段、非私有、没有 open / override 或者 const 修饰符并且不是被委托的属性。
- 将Kotlin命名对象或伴生对象中定义的函数用 @JvmStatic 标注,在Java中也将编译成静态的函数
- @JvmStatic 注解也可以应用于对象或伴生对象的属性,使其 getter 和 setter 方法在该对象的类中是静态成员
- Kotlin中 internal 声明会成为 Java 中的 public
- 在Kotlin中用 @JvmOverloads 注解一个有默认参数值的函数(不能是抽象的),在Java中会暴露多个重载(不同参数组合的函数)
- 如果Kotlin中一个类的所有构造函数参数都有默认值,那么在Java中会为其生成一个公有的无参构造函数。
- Kotlin 函数有受检异常,需要用 @Throws(异常类名::class) 注解该函数,在Java中调用此函数才能捕获对应异常。