操作符
?操作符
表示这个对象可能为空
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函数的任何场景,一般可用于多个扩展函数链式调用 |