操作符
?操作符
表示这个对象可能为空
1 | //在变量后面加?,代表这个变量可以为空 |
1 | //如果str不能转为Int类型,则返回null |
1 | // 如果b非空,就返回b.length,否则返回null,这个表达式的类型是Int? |
?:操作符
如果操作符左边表达式不为空用左边表达式结果,为空则用右边的值。类似三元运算符1
2
3
4val l: Int = if(b!=null) b.length else -1
// 除了完整的if表达式,这个还可以用Elvis操作符来表示:
val l = b?.length ?: -1
// 如果?: 左侧表达式非空,elvis操作符就返回左侧表达式的值,否则返回右侧的值。
注意:当且仅当左侧为空时,才会对右侧表达式求值。
!!操作符
一定是非空的值
1 | //这会返回一个非空的b值,或者如果b为空,就会抛出空指针异常 |
== 与 ===
这个类似java中的 == 和 .equals()
== 判断值是否相等,=== 判断值及引用是否完全相等。
1 | val num: Int = 128; |
..符号以及 in 和 !in 操作符
..代表从x到y,==包括x和y,这是一个闭区间==运算符,而until则是半闭区间运算符,代表从a到b范围内所有的值,==包括a和不包括b==。
in代表在一个区间中,!in代表不在一个区间中。
1 | if(i in 1..10){ //等价于 1 <= i <= 10 |
downTo()函数
倒叙遍历,区间内循环
1 | for(i in 4 downTo 1){} //print(i) -- 4321 |
step函数
可以进行任意数量的迭代
1 | for (i in 1..4 step 2) print(i) // prints "13" |
::符号
得到类的Class对象
1 | startActivity(Intent(this@KotlinActivity, MainActivity::class.java)) |
@符号
限定this的类型
1 | class User { |
作为标签
跳出双层循环1
2
3
4
5
6
7
8
9
10
11
12loop@ for (itemA in arraysA) {
var i : Int = 0
for (itemB in arraysB) {
i++
if (itemB > 2) {
break@loop
}
println("itemB:$itemB")
}
}
==命名函数自定义标签:==
1 | fun fun_run(){ |
从forEach跳出
1 | fun forEach_label(ints: List<Int>) |
as?操作符
当使用as转型的时候,可能经常出现ClassCastException。所以,现在可以用==as?安全转型==,当转型不成功的时候,它会返回null。
注意:在使用intent传值的时候,会出现空字符串不能用as强制转型,应该使用as?
1 | val m: Int? = a as? Int |
冒号:
用于类的继承,变了的定义
- 类型和超类型之间的冒号前要有一个空格
- 实例和类型之间的冒号前不能用空格
1 | //定义全局变量时 |
类型判断符is
检查某个实例是否是某个类型,如果判断出属于某个类型,那么判断后的分支中可以直接可当该类型使用,==无需显示转换==
1 | fun getStringLength(obj: Any): Int? { |
$操作符
字符串可以包含模板表达式,及一小段代码,会求值并把结果包含到字符串中。模板字符串以美元符号$开头,由一个简单的名字构成:
1 | val value:Int=5; |
标准函数
run 、 apply 、 let 、 also 和 with 五个函数均位于 kotlin 包下的 Standard 文件中,其含义和用法比较相似,现分别介绍如下。
run
- run函数使用的一般结构
1 | object.run{ |
- run函数的inline+lambda结构
1 | @kotlin.internal.InlineOnly |
结构分析
run函数实际上可以说是let和with的结合体,run函数只是接受一个lambda函数作为参数,以闭包形式返回,==返回值为最后一行的值或者指定的return的表达式==例子
1 | //kotlin |
适用场景
适用于let,with函数任何场景,因为run函数是let,with两个函数的结合体,它既弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样省略,直接访问实例的公有属性和方法,另一方面也弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理run函数使用前后对比
不使用run函数1
2
3
4
5
6
7
8
9
10
11
12
13override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
with(item){
holder.tvNewsTitle.text = StringUtils.trimToEmpty(titleEn)
holder.tvNewsSummary.text = StringUtils.trimToEmpty(summary)
holder.tvExtraInf = "难度:$gradeInfo | 单词数:$length | 读后感: $numReviews"
...
}
}
使用run函数
1 | override fun onBindViewHolder(holder: ViewHolder, position: Int){ |
let
- let函数的一般结构
1 | object.let{ |
- let函数底层的inline扩展函数+lambda结构
1 | @kotlin.internal.InlineOnly |
结构分析
从源码let函数来看,它是一个只有一个lambda 函数块block作为参数的函数,调用T类型对象的let函数,则该对象为函数的参数。在函数块内可以通过it指代该对象,返回值为函数块的最后一行或者指定return表达式。let函数的kotlin和java转化
1 | //kotlin |
let函数适用的场景
最常用的场景就是使用let函数处理需要针对一个可以null的对象统一判空处理。let函数的前后对比
没有使用前:
1 | mVideoPlayer?.setVideoView(activity.course_video_view) |
使用后:
1 | //对对象做了统一的判空处理 |
with
- with函数的一般结构
1 | with(object){ |
- with函数底层的inline扩展函数+lambda结构
1 | @kotlin.internal.InlineOnly |
结构分析
with函数和前面几个函数使用方法略有不同,因为它不是以扩展的形式存在的。它是将某个对象作为函数的参数,==在函数块内可以通过this指代该对象==。返回值为函数块最后一行或者指定的return表达式。可以看出with函数接收了两个参数,分别为T类型的对象receiver和一个lambda函数块,所以with函数最原始的样子如下:
1 | val result = with(user, { |
但是由于,with函数最后一个参数是一个函数,可以把函数提到圆括号外部,所以最终with函数的调用形式如下:(可以看下kotlin的lambda表达式)
1 | val result = with(user) { |
- with函数的kotlin和java转化
1 | //kotlin |
- with函数的适用场景
适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBindViewHolder中,数据model的属性映射到UI上
- with函数使用前后对比
没有使用:
1 | @Override |
使用了之后:
1 | override fun onBindViewHolder(holder: ViewHolder, position: Int){ |
apply
- apply函数使用的一般结构
1 | object.apply{ |
- apply函数的inline+lambda结构
1 | @kotlin.internal.InlineOnly |
- apply函数的inline结构分析
从结构上来看,apply函数和run函数很像,唯一不同点就是他们各自返回值不同,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回值则是传入对象的本身。
- apply函数的kotlin和java转化
1 | /kotlin |
- apply函数使用前后的对比
没有使用:
1 | mSheetDialogView = View.inflate(activity, R.layout.biz_exam_plan_layout_sheet_inner, null) |
使用后:
1 | //形成了链式调用 |
多层级判空问题
1 | if (mSectionMetaData == null || mSectionMetaData.questionnaire == null || mSectionMetaData.section == null) { |
-> 使用了apply后可以变为链式调用
1 | mSectionMetaData?.apply{ |
also
- also函数使用的一般结构
1 | object.also{ |
- also函数的inline+lambda结构
1 | @kotlin.internal.InlineOnly |
- also函数的inline结构分析
also函数的结构实际上和let很像,唯一的区别是返回值不一样,let是以闭包的形式返回的,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值,而also函数则返回传入对象本身。这个和apply一样。
- also函数的kotlin和java转化
1 | //kotlin |
标准函数总结
函数名 | 定义inline结构 | 函数体内代指对象 | 返回值 | 是否是扩展函数 | 使用场景 |
---|---|---|---|---|---|
let | fun T.let(block: (T) -> R): R = block(this) | it | 闭包返回 | 是 | 适用于处理不为null的操作场景 |
with | fun with(receiver: T, block: T.() -> R): R = receiver.block() | this | 返回闭包 | 否 | 适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上 |
run | fun T.run(block: T.() -> R): R = block() | this | 闭包返回 | 是 | 适用于let,with函数任何场景。 |
apply | fun T.apply(block: T.() -> Unit): T { block(); return this } | this | 返回this | 是 | |
also | fun T.also(block: (T) -> Unit): T { block(this); return this } | it | 返回this | 是 | 适用于let函数的任何场景,一般可用于多个扩展函数链式调用 |