Kotlin拾遗

return关键字

从封闭函数中返回

1
2
3
4
5
fun lookForAlice(people: List<Person>) {
people.forEach {
if(it.name == "Alice") return
}
}

用标签返回

1
2
3
people.forEach @label {
if(it.name == "Alice") return@label
}

函数名作为标签返回

1
2
3
people.forEach {
if(it.name == "Alice") return@forEach
}

匿名函数局部返回

1
2
3
people.forEach(fun(person) {
if(it.name == "Alice") return
})

面向对象

所有类默认为final需要显式声明open以便继承

主/次构造函数

类可以有一个主构造函数和多个次构造函数

1
2
3
4
5
6
7
8
9
10
11
class User(name: String) { // 此处实际上省略了constructor关键字
// 若为class User(var name: String)则自动生成同名属性
// 主构造函数
init { // init块可以有多个按它们在类体中的顺序执行
println(name)
}
// 次构造函数
constructor(name: String, age: Int): this(name) { // 主构造函数规定了入口
println("$name $age")
}
}

扩展函数

扩展函数不可以重写

1
fun Int.isEven() = this % 2 == 0

伴生对象

可以为伴生对象命名

1
companion object Loader { ... }

为匿名伴生对象定义扩展函数时需要使用默认名Companion

1
fun ClsName.Companion.getSomething(): Type { ... }

枚举类

软关键字

如此处的enumclass前才起作用其他处可以作为名称使用

1
2
3
enum class Color(rgb: Int) {
RED(0xFF0000), GREEN(0x00FF00), BLUE(0x0000FF)
}

下例获取枚举类常量的个数

1
val n = enumValues<EnumClsName>().size

enumValues<T>()返回一个Array<T>使可以通过泛型数组的形式访问枚举类的常量

数据类

自动创建属性它使用圆括号且一般没有方法

1
data class User(var id: Int, var name: String)

自定义运算符

等式校验

KotlinJAVA不同a==b相当于a?.equals(b)?:(b == null)而引用比较使用a===b

1
2
3
4
5
override fun equals(obj: Any?): Boolean {
if(obj === this) return true // 优化
if(obj !is Point) return false // 类型检验
return obj.x == x && obj.y == y // 确定了Point类型因此可以访问成员
}

equals()Any中就有定义意味着所有对象都支持等式校验

!=也会转换为equals()的调用

排序校验

要实现Comparable接口相关的运算符有><>=<=

1
2
3
4
5
6
class Person(...): Comparable<Person> {
override fun compareTo(other: Person): Int {
return compareValuesBy(this, other,
Person::lastName, Person::firstName)
}
}

其他

1
operator fun plus(other: Cls): Cls { ... }

函数

高阶函数

函数的参数或返回值类型也为函数

1
2
3
4
5
6
7
8
fun String.filter(predicate: (Char) -> Boolean): String {
val sb = StringBuilder()
for(i in 0 until length) {
val element = get(i)
if(predicate(element)) sb.append(element)
}
return sb.toString()
}

闭包

当函数最末参数为lambda表达式可写于括号外

例如Thread的完全写法如下

1
2
3
Thread(object : Runnable {
override fun run() { ... }
})

SAM(Single Abstract Method)

单抽象方法

满足SAM可简化为如下

1
Thread({ ... })

使用闭包还可简化为如下

1
Thread { ... }

形如Thread { ... }的结构中{}就是一个闭包

函数引用

1
val fRef = ::f

可以fRef(params)其本质是fRef.invoke(params)

返回值可空的函数类型

1
var canReturnNull: (Int, Int) -> Int? = { null }

函数类型的可空变量

1
var funOrNull: ((Int, Int) -> Int)? = null

空安全

?.空安全调用运算符

foo?.bar()

  • foo!=null得到foo.bar()
  • foo==null得到null

?:空值合并运算符

又称Elvis运算符顺时针旋转90°看起来像猫王Elvis一样

foo?:bar()

  • foo!=null得到foo
  • foo==null得到bar

as?安全转换

foo as? Type

  • foo is Type得到foo as Type
  • foo !is Type得到null

?:as?可结合使用b as? A ?: Type

!!非空断言

一旦使用!!空安全优势便不得体现

foo!!

  • foo!=null得到foo
  • foo==null得到NullPointerException

泛型

擦除

ERROR:

Cannot check for instance of erased type: T.

1
fun<T> isA(value: Any) = value is T // ERROR!

实化

1
inline fun<reified T> isA(value: Any) = value is T

通配

1
if(value is List<*>) { ... }

上界约束

1
2
3
fun<T: Number> oneHalf(value: T): Double {
return value.toDouble() / 2.0
}

多重约束

1
2
fun<T> ensureTrailingPeriod(seq: T)
where T: CharSequence, T: Appendable { ... }

非空约束

1
class Processor<T: Any> { ... }

默认的<T>使T的类型可空

协变

1
interface List<out T> { fun getSomething(index: Int): T }

逆变

1
interface Compare<in T> { fun compare(first: T, second: T): Int }

多线程

协程(Coroutine)

一套线程API即更方便的线程框架

下例的调度器(Dispatchers)可将协程限制于特性线程执行或将它分配至一个线程池或让它不受限制地运行

1
2
3
4
launch(Dispatchers.Main) { // 在UI线程开始
val info = getInfo() // 换至IO线程
view.setInfo(info) // 结束后切回UI线程
}

非阻塞式挂起

非阻塞

协程的写法看似阻塞但实际不然故加以强调

挂起

稍后会自动切回的线程切换

自定义挂起函数而未调用挂起函数(如下例的withContext())将提示suspend为冗余

关键字suspend仅是标识提醒的作用与挂起的实现无关

1
2
3
4
suspend fun getInfo() = withContext(Dispatchers.IO) {
... // 网络请求等耗时操作
return@withContext
}