前言: 有些路看起来很近,可是走下去却很远,缺少耐心的人永远走不到尽头。人生,一半是现实,一般是梦想。
一、概述
前面几篇文章中讲解了 Kotlin 的常量、变量、数据类型和作用域函数等,与 Java 相比还是有一定的区别。这里给大家介绍 Kotlin 的相关逻辑控制语句,如:if
,for
,when
,while
等。Kotlin 中没有 Java 中的三元运算符,但是用if
语句可以实现类似效果;when
语句替代了 Java 中的switch
语句;for
循环语句废除了 Java 中的(初始值;条件;增减步长)规则,新增了其他规则。下面我们来详细分析:
二、if语句
Kotlin 中的if
语句与 Java 中的语句有一定区别的,它在 Kotlin 中更灵活,除了能实现 Java 中的写法外,还可以实现表达式以及一个块的运用。一个if
语句包含一个布尔表达式(类似三目运算符)和一条或多条语句。
(1)传统用法
Kotlin 中的if
语句的传统用法与 Java 中的用法一样,没什么好说的,看代码:
val numA = 0val numB = 5var result = 10if (numA == 5) result = numAif (numA > numB) {result = numA} else {result = numB}
(2)表达式(三元运算符)
Kotlin 中其实不存在 Java 中的三元运算符条件表达式 ? 表达式1 : 表达式2
,因为 Kotlin 中的if
语句的表达式会返回一个值,所以不需要三元运算符。
//Java 的三目运算符 Kotlin中这样写会报错//int result = numA > numB ? 10 : 20;//kotlin 中直接使用 if…else…替代var result = if (numA > numB) {10} else {20}Log.e(TAG, "if语句:result == $result")//当numA>numB时,result=10,否则result=20//简写为var result = if (numA > numB) 10 else 20
打印数据如下:
if语句:result == 20
可以看到在 Kotlin 中if
语句可以作为一个表达式并且返回一个值。
三、for语句
Kotlinfor
循环可以对任何提供迭代器(iterator)的对象进行遍历。循环数组会被编译为一个基于索引index
的循环,不会创建迭代器对象。for
循环语句废除了 Java 中的(初始值;条件;增减步长)规则,新增了其他规则。其语法如下:
for (item in collection){print(item){
in: 运算符,表示在…之内的意思,使用in
操作符来遍历。
3.1 递增(..
与until
)
..
与until
都是表示递增的区间,只是区间的取值范围不同。
..
: 创建从此值到指定值的范围。表示一个区间,该区间是闭区间,包括开始值和结束值,[n,m]。until: 创建从此值但不包括指定的值的范围。表示一个区间,该区间是半闭区间,包括开始值,不包括结束值,[n,m)。
//java 的for循环递增// for (int i = 0; i < 5; i++) {// System.out.println(i);// }//循环5次,步长为1的递增for (i in 1..5) {//1 2 34 5print("$i \t")}//循环4次,步长为1的递增for (i in 1 until 5) {//1234print("$i \t")}
打印数据如下:
for语句:..递增 == 1 2 3 4 5for语句:until递增 == 1 2 3 4
注意:区间是从小到大的,如果开始的数值比最后的还要大则没有意义。在until
中,如果[to]值,即最后的数值,这里为5,小于或等于当前值i
,则返回的数值为空。
3.2 递减downTo
Kotlin 使用downTo
表示递减。
downTo: 通过步长-1返回从该值向下到指定的值的序列。表示一个区间,该区间是闭区间,包括开始值和结束值,[n,m]。
//循环5次,步长为-1的递减for (i in 5 downTo 1) {print("$i \t")}
上面的区间是5 downTo 1
,当前值i
必须大于或者等于[to]值,即1,如果当前值i
小于[to]值,则返回的数值为空。打印数据如下:
for语句:downTo递减 == 5 4 3 2 1
3.3 步长step
上面的例子中的步长都是为1,步长我们可以根据自己需求来设置:
step: 返回与给定步骤执行相同范围的进程。也就是说取值的间隔是多少。
//step:步长为2,循环两次的递减for (i in 6 downTo 1 step 2) {print("$i \t")}
打印数据如下:
for语句:step步长为2 == 4 2
3.3 遍历字符串
for
循环也能遍历字符串,会把每个字符逐个打印出来。
//遍历字符串for (i in "HelloWord") {print("$i \t")}
打印数据如下:
for语句:字符串 == H e l l o W o r d
3.4 遍历集合
for
循环遍历集合:
//遍历集合val userArray = arrayListOf(User("姓名1"), User("姓名2"), User("姓名3"))for (item in userArray) {print("${item.name} \t")}
打印数据如下:
for语句:遍历集合 == 姓名1姓名2姓名3
3.5 通过索引遍历
如果你想通过索引遍历遍历集合或数组,可以使用indices
关键字。
indices: 返回此集合的有效索引的[int范围]
。
//indices比遍历val intArray = arrayListOf(10, 20, 30, 40)for (i in intArray.indices) {Log.e(TAG, "for语句:indices遍历 == intArray[$i]:${intArray[i]}")}
打印数据如下:
for语句:indices遍历 == intArray[0]:10for语句:indices遍历 == intArray[1]:20for语句:indices遍历 == intArray[2]:30for语句:indices遍历 == intArray[3]:40
注意:这种“在区间上遍历”会编译成优化的实现而不会创建额外的对象。
3.6 库函数 withIndex
for
循环中的withIndex
返回一个惰性的[Iterable]
,它将原始集合的每个元素包装成一个IndexedValue
对象,其中包含该元素和元素本身的索引。IndexedValue
表示一个在集合或者序列中的值及其在该集合或序列中的索引的数据类型。源码如下:
public data class IndexedValue<out T>(public val index: Int, public val value: T)
来简单使用下:
//withIndex:包含该元素和元素索引val intArray = arrayListOf(10, 20, 30, 40)for ((index, value) in intArray.withIndex()) {Log.e(TAG, "for语句:withIndex == index: $index, value: $value")}
打印数据如下:
for语句:withIndex == index: 0, value: 10for语句:withIndex == index: 1, value: 20for语句:withIndex == index: 2, value: 30for语句:withIndex == index: 3, value: 40
四、when 语句
when
将它的参数和所有分支条件比较,直到某个分支满足条件,跳出语句。when
既可以当做表达式使用也可以当做语句使用,如果被当做表达式,那么符合条件的分支的值就是整个表达式的值;如果当语句使用则忽略分支的值。when
操作符类似 Java 中的switch
操作符,甚至可以替换if
语句。
4.1 普通用法
when
操作符用->
表示要执行的操作,类似switch
的:
,同时每个分支不需要像switch
语句那样添加break
跳出分支,如果分支下面只有一个语句,则可以省略块符号{}
,否则需要加上块符号{}
将要执行的操作包起来。
//Java的 switch 语法int num = 0;switch (num) {case 2:System.out.print("switch语句:num == 2");break;case 4:System.out.print("switch语句:num == 4");break;default:System.out.print("switch语句:num != 2 && num != 4");}//Kotlinvar num = 0when (num) {2 -> print("when语句:num == 2")4 -> {print("when语句:num == 4")}else -> {//注意块, else 等同于java中的 defaultprint("when语句:num != 2 && num != 4")}}
在when
中,else
的含义等同于switch
中的default
,如果其他分支不满足条件都会走else
分支。打印数据如下:
when语句:num != 2 && num != 4
4.2 分支处理方式相同
如果很多分支用相同的方式处理,则可以把多个分支条件放在一起,用,
分隔开来,相当于switch
中不用break
跳转的语句:
var num = 2when (num) {//2,4分支相同的方式处理2, 4 -> print("when语句:num == 2 or num == 4")else -> print("when语句:num != 2 && num != 4")}
打印数据如下:
when语句:num == 2 or num == 4
4.3 条件使用任意表达式
when
语句中的条件可以使用任意表达式,并不是只局限于常量,相当于if
表达式的用法:
when (num > 0) {true -> print("when语句:num > 0")false -> print("when语句:num < 0")}
打印数据如下:
when语句:num > 0
4.4 检查值是否在集合或者数组中
那么也可以检查一个条件是否在in
或者不在!in
一个区间或者集合中,in
表示在…范围内,!in
表示不在…范围内。
var num = 5val intArray = arrayOf(6, 7, 8, 9, 10)when (num) {in 1..5 -> print("when语句:num == 属于 1 ~ 5 中")!in intArray -> print("when语句:num == 不在intArray集合中")else -> print("when语句:num == 都不属于")}
打印数据如下:
when语句:num == 属于 1 ~ 5 中
4.5 检查值是否为指定类型的值
检测一个值是is
或者不是!is
一个特定的类型。注意:kotlin 的智能转换,可以访问该类型的属性和方法而不需要任何的检测。
fun isWhen(x: Any) = when (x) {is Int -> print("when语句:x 是Int类型")!is String -> print("when语句:x 不是String类型")else -> print("when语句:x 是String类型")}//调用isWhen("山水有相逢")
打印数据如下:
when语句:x 是String类型
4.6 不提供参数
when
也可以用来取代if … else
链,如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支条件为true时,则执行该分支,跳出语句。
var str = "kotlin"val strArray = arrayOf("android", "kotlin", "java")when {str is String -> print("when语句:str 是String类型")"java" in strArray -> print("when语句:java == 在集合中")}
打印数据如下:
when语句:str 是String类型
可以看到in
运算符还可以判断集合内是否包含某实例。
五、while与do…while语句
Kotlin 中的while
和do…while
语句与 Java 中的用法相同。while
语句如果不满足条件是不会进入循环的,不同的是do…while
是先执行循环再判断条件,循环至少会执行一次。
while
的结构如下:
while( 布尔表达式 ) {//TODO 循环内容}
while
的结构如下:
do {//TODO 循环内容}while(布尔表达式);
do...while
语句的使用:
var numA = 1while (numA < 6) {Log.e(TAG, "while语句:循环了$numA 次")numA++}
打印数据如下:
while语句:循环了1 次while语句:循环了2 次while语句:循环了3 次while语句:循环了4 次while语句:循环了5 次
do...while
语句的使用:
var numB = 0do {Log.e(TAG, "do...while语句:循环了$numB 次")numB--} while (numB > 0)
打印数据如下:
do...while语句:循环了1 次
六、返回和跳转语句(return、break、continue)
Kotlin 有三种结构化跳转:return、break、continue,和 Java 的用法和意义一致。
return: 默认从直接包围它的函数或者匿名函数返回;break: 终止最直接包围它的循环;continue: 继续下一次最直接包围它的循环。即跳出本次循环,即系下一次循环。
另外,在 Kotlin 中任何表达式都可以使用标签来标记(label),标签的形式是标签名后接@
标识符,如flag@
等,标签符由自己决定:
flag@ for (i in 1 .. 10) {{
我们就可以使用一个标签限定一个 brea k或一个 continue,使用标签限定的 break 会跳到刚好位于该标签指定的循环后面的执行点,continue 继续标签指定的循环的下一个迭代。
6.1 break、continue和标签
在 Kotlin 中支持传统的 break 和 continue 操作符:
//基本用法for (i in 1..10) {if (i == 4) continue//i == 4时,跳出当前循环,进入下一次循环Log.e(TAG, "break和continue:i == $i")if (i > 6) break//i > 6时跳出循环}
打印数据如下:
break和continue:i == 1break和continue:i == 2break和continue:i == 3break和continue:i == 5break和continue:i == 6break和continue:i == 7
现在,我们可以使用一个标签来定义一个 break 或 continue :
//使用一个标签来定义一个break或continue:Log.e(TAG, "=== break和标签一起使用 ===")loop1@ for (i in 1..10) {for (j in 1..10) {if (true) {Log.e(TAG, "break: i + j == $i + $j")break@loop1//跳出@loop1循环}}}Log.e(TAG, "=== continue和标签一起使用 ===")loop2@ for (i in 1..10) {for (j in 1..10) {if (true) {Log.e(TAG, "continue: i + j == $i + $j")continue@loop2//跳出本次循环,进入下一个@loop2循环}}}
打印数据如下:
=== break和标签一起使用 ===break: i + j == 1 + 1=== continue和标签一起使用 ===continue: i + j == 1 + 1continue: i + j == 3 + 1continue: i + j == 4 + 1continue: i + j == 5 + 1continue: i + j == 6 + 1continue: i + j == 7 + 1continue: i + j == 8 + 1continue: i + j == 9 + 1continue: i + j == 10 + 1
可以看到使用一个标签限定一个 break 或一个 continue 后,使用标签限定的 break 会跳到刚好位于该标签指定的循环后面的执行点loop1@
,continue 继续标签loop2@
指定的循环的下一个迭代。
6.2 return和标签
Kotlin 有函数字面量,局部函数和对象表达式。所以 Kotlin 函数可以被嵌套,标签限制的 return 允许我们从外层函数返回,最重要的一个用途就是从 lambda 表达式中返回。普通使用:
private fun returnGrammar() {var intArrays = arrayListOf(1, 2, 3, 4)for (item in intArrays) {if (item == 2) returnLog.e(TAG, "return:item == $item")}Log.e(TAG, "return:外层函数")}
打印数据如下:
return:item == 1
这个 return 表达式从最直接包围它的函数returnGrammar()
中返回,注意,这种非局部的返回只支持传给内联函数的 lambda 表达式,如果我们需要从 lambda 表达式中返回,我们必须加标签并用以限制return。
(1)标签和return一起使用
//1.标签和return一起使用private fun returnGrammar() {var intArrays = arrayListOf(1, 2, 3, 4)//数组的迭代器理由有一个Iterable.forEach()方法,传入了一个it参数,为数组中的元素intArrays.forEach flag@{if (it == 2) return@flagLog.e(TAG, "return:item == $it")}Log.e(TAG, "return:外层函数")}
可以看到,return@flag
只会从 lambda 表达式中返回,并未从外层函数中返回,继续执行输出了3,4
和外层函数
。打印数据如下:
return:item == 1return:item == 3return:item == 4return:外层函数
(2)使用隐式标签,该标签与接受该 lambda 的函数同名
//2.使用隐式标签,该标签与接受该 lambda 的函数同名private fun returnGrammar() {var intArrays = arrayListOf(1, 2, 3, 4)intArrays.forEach {if (it == 2) return@forEachLog.e(TAG, "return:item == $it")}Log.e(TAG, "return:外层函数")}
通常使用隐式标签更方便,该标签与接受该 lambda 的函数forEach
同名。打印数据如下:
return:item == 1return:item == 3return:item == 4return:外层函数
(3)使用匿名函数代替 lambda 表达式,返回是从匿名函数中返回而不是外层函数
//3.使用匿名函数代替 lambda 表达式,返回是从匿名函数中返回而不是外层函数private fun returnGrammar() {var intArrays = arrayListOf(1, 2, 3, 4)intArrays.forEach(fun(value: Int) {if (value == 2) returnLog.e(TAG, "return:value == $value")})Log.e(TAG, "return:外层函数")}
使用一个匿名函数替代 lambda 表达式,匿名函数内部的 return 语句将从该匿名函数自身返回。打印数据如下:
return:value == 1return:value == 3return:value == 4return:外层函数
(4)注意前三个示例中使用的本地return类似于在常规循环中使用的 continue。对于 break 没有直接的等价,但是可以同过添加另一个嵌套 lambda 和非本地 return 来模拟:
private fun returnGrammar() {var intArrays = arrayListOf(1, 2, 3, 4)run loop@{intArrays.forEach {if (it == 2) return@loopLog.e(TAG, "return:@loop == $it")}}Log.e(TAG, "return:外层函数")}
打印数据如下:
return:@loop == 1return:外层函数
当要返回一个值的时候,解析器优先选择标签限制的 return:
return@flag 10
意思是从标签@flag
处返回10,而不是返回一个标签标注的表达式@flag 10
。
七、总结
源码地址:/FollowExcellence/KotlinDemo-master
点关注,不迷路
好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。
我是suming,感谢各位的支持和认可,您的点赞、评论、收藏【一键三连】就是我创作的最大动力,我们下篇文章见!
如果本篇博客有任何错误,请批评指教,不胜感激 !
要想成为一个优秀的安卓开发者,这里有必须要掌握的知识架构,一步一步朝着自己的梦想前进!Keep Moving!