本文汇总kotlin里常用API、基础知识,会持续更新,便于查阅。
equals()和hashCode()方法kotlin中文站
- Kotlin 中的== 等同于调用 equals() 函数,比较两个对象引用是否相等要用 === 操作符。
- 用 ==操作符时不需要担心空安全,a == b 等同于 a?.equals(b) ?: b == null。
- 和Java一样,复写equals()方法同样遵循复写equals()方法遵守的约定,需要同时复写hashCode()方法。
public open class Any public constructor() {
public open operator fun equals(other: kotlin.Any?): kotlin.Boolean { }
public open fun hashCode(): kotlin.Int { }
public open fun toString(): kotlin.String { }
}
lateinit、lazy、getter()
private lateinit var mViewMoudule: ViewModuleImpl;
private val mLiveData by lazy { MutableLiveData() }
val textView: TextView
get() = findViewById(R.id.tv_sth)
-
lateinit 关键字
变量在定义时不需要初始化,所以在使用变量时,不需要加上 ? 和 !! 操作符。
但是在使用第一次变量之前,一定要保证为它赋值 , 不然会出现空指针异常。 -
by lazy
在第一次使用该变量时会进行赋值;
十分适合加载可能访问或不可访问的变量。 -
get() 函数
每次调用该变量时,即相当于调用get()函数,适合Bean属性的获取,不适合复杂赋值等变量
-
扩展函数
- Kotlin 能够扩展一个类的新功能而无需继承该类或者使用像装饰者这样的设计模式。 这通过叫做 扩展 的特殊声明完成。 这种机制称为 扩展函数 。此外,也有 扩展属性 ,
- 扩展不能真正的修改他们所扩展的类。通过定义一个扩展,你并没有在一个类中插入新成员, 仅仅是可以通过该类型的变量用点表达式去调用这个新函数。
- 扩展函数是静态分发的,即他们不是接收者类型的虚方法。 这意味着调用的扩展函数是由函数调用所在的表达式的类型来决定的, 而不是由表达式运行时求值结果决定的。即静态绑定的,非动态运行时类型推断的,无虚表替换。
- 如果一个类定义有一个成员函数与一个扩展函数,而这两个函数又有相同的接收者类型、 相同的名字,并且都适用给定的参数,这种情况总是取成员函数。
-
全局函数
- 全局函数的作用域是以包为作用域的;
- 引入的时候可以直接使用import xxx.全局函数,这个跟java的不同;
- 也就是同一个包作用域下,不可以有“同名”的全局函数;
- 如果不同的包作用域下,有“同名”的全局函数,则需要引入全局函数的全局路径即可;
-
内联函数
- 添加inline关键字使函数成为内联函数,inline 修饰符影响函数本身和传给它的 lambda 表达式,所有这些都将内联到调用处;
- 内联可能导致生成的代码增加,好好使用这个功能;
- 可以添加noinline修饰符标记不希望内联的函数参数;
- inline在kotlin这里是真的内联的,但是在cpp里是建议编译器进行优化,注意一下这块。
例如如下例子:
// 对MutableList作用域函数:let、apply、also、run、with进行扩展,添加swap函数。 fun MutableList .wap(index1: Int, index2: Int) { val tmp = this[index1] // “this”对应该列表 this[index1] = this[index2] this[index2] = tmp } // 封装lock inline函数 inline fun lock(lock: Lock, block: () -> T): T { lock.lock() try { block() } finally { lock.unlock() } }
在使用这些内联函数,要特别注意返回值,避免使用错误。有些很相近,而内部的代码块中接收者是 this 或 it。
可能有点类似语义上的区别了,之后在使用的过程中再熟悉总结吧,最常用还是let和apply了。
| 函数 | 对象引用 | 返回值 | 是否是扩展函数 |
|---|---|---|---|
| let | it | Lambda 表达式结果 | 是 |
| run | this | Lambda 表达式结果 | 是 |
| with | this | Lambda 表达式结果 | 不是:把上下文对象当做参数 |
| apply | this | 上下文对象 | 是 |
| also | it | 上下文对象 | 是 |
- let
let常用的场景就是使用let函数判空处理,在let闭包下不用写繁琐的问号了。
var list: List? = null list?.let { // 不空位的情况 } ?: let { // 为空的情况 } var list: List ? = mutableListOf() val let = list?.let { it.size // 最后一行返回值 } public inline fun T.let(block: (T) -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return block(this) // 返回闭包 }
- apply
apply一般用于对象实例初始化的时候,使用apply初始化对象的各种属性,或者直接调用对象方法进行初始化。
var map = HashMap().apply { put("key", "value") } var view = TextView(this).apply { id = R.id.textView } public inline fun T.apply(block: T.() -> Unit): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } block() return this // 返回对象本身 }
- run、with、also
- with可以用于UI数据绑定实体,返回闭包最后一行数据,但是user为null时实用性不如let;
- run返回闭包结果,传入的是this,主要目的是强调需要执行的函数
- also返回调用对象本身,also 就像 apply :它接受接收者、做⼀些动作、并返回该接收者。 ⼆者区别是在apply 内部的代码块中接收者是 this,⽽在 also 内部的代码块中是 it。
var user: User? = null;
var result = with(user, {
tv_name.text = this?.name
"${this?.name}, ${this?.age}" // 返回闭包最后一行
})
// 从intent取key_extra的值,不为非空且内容不为空,赋值给url。否则弹出提示并关闭页面。
var url = intent.getStringExtra("key_extra")?.takeIf { it.isNotEmpty() } ?: run {
Log.e("mLogU", "null url")
finish()
}



