程序设计中的流程控制有三种结构,即顺序、分支和循环结构。Kotlin中的流程控制结构分类如下:
o 分支结构:if和when
o 循环结构:while、do-while和for
o 跳转结构:break、continue和return
分支结构提供了一种控制机制,使得程序具有了“判断能力”,能够像人类的大脑一样分析问题。分支结构又称条件结构,条件结构使部分程序可根据某些表达式的值被有选择地执行。在Kotlin语言中分支结构有if和when,本节先介绍if结构。
9.1.1 if结构当做语句使用
在Kotlin语言中if和when结构都是表达式,表示式是有返回值的,而语句没有。在前面4.4节讨论过这个问题。但是if表达式也可以当成if语句使用,这与传统if语句完全一样,下面先if结构当做语句使用。
if语句有三种结构:if结构、if-else结构和else-if结构三种。
1.f结构
if (条件表达式) {
语句组
}
如果条件表达式为true就执行语句组,否则就执行if结构后面的语句。
2. if-else结构
if (条件表达式) {
语句组1
} else {
语句组2
}
当程序执行到if语句时,先判断条件表达式,如果值为true,则执行语句组1,然后跳过else语句及语句组2,继续执行后面的语句。如果条件表达式的值为false,则忽略语句组1而直接执行语句组2,然后继续执行后面的语句。
3. else-if结构
if (条件表达式1) {
语句组1
} else if (条件表达式2) {
语句组2
} else if (条件表达式3) {
语句组3
…
} else if (条件表达式n) {
语句组n
} else {
语句组n+1
}
可以看出,else-if结构实际上是if-else结构的多层嵌套,它明显的特点就是在多个分支中只执行一个语句组,而其他分支都不执行,所以这种结构可以用于有多种判断结果的分支中。
示例如下:
//代码文件:chapter9/src/com/a51work6/ch9.1.1.kt
package com.a51work6
fun main(args: Array) {
// 1. if结构
val score = 95
if (score >=85) {
println(“您真优秀!”)
}
if (score <60) {
println(“您需要加倍努力!”)
}
if (score >=60 && score < 85) {
println(“您的成绩还可以,仍需继续努力!”)
}
// 2. if-else结构
if (score <60) {
println(“不及格”)
} else {
println(“及格”)
}
// 3. else-if结构
val testScore =76
val grade: Char
if (testScore>= 90) {
grade = 'A'
} else if
(testScore >= 80) {
grade = ‘B’
} else if
(testScore >= 70) {
grade = ‘C’
} else if
(testScore >= 60) {
grade = ‘D’
} else {
grade = ‘F’
}
println("Grade = " + grade)
}
运行结果如下:
您真优秀!
及格
Grade = C
上述代码如果对于Java或C等其他语句有些熟悉的读者,应该很容易读懂,这不再赘述。
9.1.2 if表达式
Kotlin语言主张代码简洁,9.1.1节代码显然不够简洁,Kotlin使用if表达式让代码简洁。if表达式中每个代码块的最后一个表达式就是它的返回值。因为要求有返回值,所以没有if结构,只有if-else和else-if两种结构。具体说明如下:
1.if-else结构
val(或var) bar = if (条件表达式) {
语句组1
表达式
} else {
语句组2
表达式
}
当程序执行到if语句时,先判断条件表达式,如果值为true,则执行语句组1所在代码块执行,完成后计算表达式。然后跳过else语句所在的语句组2代码块执行,完成后计算表达式,最后结束将表达式计算结果赋值给变量bar。
fun main(args: Array) {
val score = 95
// 1. if-else结构
val result1 = if (score < 60) { ①
println("不及格")
} else {
println("及格")
} ②
val result2 = if (score < 60) { ③
println("不及格")
//TODO
"重新考试" ④
} else {
println("及格")
//TODO
"通过考试" ⑤
} ⑥
// 2. else-if结构
val testScore = 76
val grade: Char = if (testScore >=90) ⑦
'A'
else if (testScore >= 80)
'B'
else if (testScore >= 70)
'C'
else if (testScore >= 60)
'D'
else
'F' ⑧
println("Grade = " + grade)
}
上述代码第①行~第②行是使用if-else结构的if表达式,虽然把结果赋值给result1变量,但这个表达式结果事实上没有任何的值,因为它的两个代码块中最后一条不是个表达式,是一个println语句,它是没返回值的。这种场景下使用if表达式就没有实际意义,而是考虑使用if语句结构。
代码第③行~第⑥行也是if-else结构的if表达式,两个代码块最后都有表达式,见代码第④行和第⑤行。结果将"通过考试"字符串赋值给result2变量,返回值是有实际意义的。
代码第⑦行~第⑧行是else-if结构的if表达式,每个代码块都只有一条表达式,因此可以省略大括号。
when提供多分支程序结构,替代Java中C语言风格的switch语句,C、C++、Objective-C和Java等多种语言都采用该种风格。when彻底地颠覆了自C语言风格以来大家对于switch的认知,这个颠覆表现在以下三个方面。
1.C语言风格的switch只能比较离散的单个的整数(或可以自动转换为整数)表达式,而when可以使用整数、浮点数、字符、字符串,以及任何可以比较的类型表达式,而且它比较的数据可以是离散的也可以是连续的范围。
2.when中的每个分支不需要添加break语句,分支执行完成就会跳出when语句。
3.when可以作为表达式使用,并且可以将一个结果赋值给其他变量,或者与其他表达式进行计算。而C语言风格的switch语句不能作为表达式。
9.2.1 when结构当做语句使用
下面先介绍一下when结构当做语句使用,语法结构如下:
when (表达式) {
分支条件表达式1 -> {
语句组1
}
分支条件表达式2 -> {
语句组2
}
…
分支条件表达式n -> {
语句组n
}
else -> {
语句组n+1
}
}
在运行时“表达式”计算结果,会与每个分支中的“分支条件表达式”进行匹配,直到找到一个分支,然后进入该分支的代码块执行,执行完成结束when语句。when结构当做语句时,最后的else分支可以省略。另外,如果语句组所在的代码块只有一条语句,可以省略大括号。
示例代码如下:
//代码文件:chapter9/src/com/a51work6/ch9.2.1.kt
package com.a51work6
fun main(args: Array) {
val testScore = 75 //设定一个数值用来测试
when (testScore / 10) { ①
9 -> { ②
println('优')
}
8 -> println('良')
7, 6 -> println('中') ③
else -> println('差')
}
val level = "优" //设定一个数值用来测试
var desc = "" //接收返回值
when (level) { ④
"优" -> desc ="90分以上"
"良"-> desc = "80分~90分"
"中"-> desc = "70分~80分"
"差"-> desc = "低于60分"
}
println("说明 = " + desc)
}
运行结果如下:
中
说明 = 90分以上
上述代码第①行when语句实现了将100分制转换为:“优”、“良”、“中”、“差”评分制,其中7分和6分都是“中”成绩,见代码第③行把7和6情况放到一个分支中,当成一种情况考虑,它们之间用逗号(,)分隔。代码第②行分支代码块没有省略大括号,其他的分支代码块都省略了。
代码第④行是when语句省略了else分支,而且事实上这个when语句是有返回值的,所以它最好采用when表达式方式。
Kotlin中的when语句很灵活,上述示例中表达式结果比较是否等于分支条件表达式结果。此外,还可以使用in或者!in表达式结果是否在一个范围或集合中;可以用is或者!is表达式结果是否是某一类型的对象。
when语句还可以省略表达式,此时分支条件表达式可以是单纯的布尔值,示例代码如下:
when {//省略表达式
testScore >= 90 -> println(‘优’) //分支条件表达式单纯的布尔值
else -> println(‘良’)
}
9.2.2 when表达式
9.2.1节的when语句示例代码显然还不够简洁,与if表达式类似,when表达式也可以使得代码变得更加简洁。when表达式语法结构如下:
val(或var) bar = when (表达式) {
分支条件表达式1 -> {
语句组1
表达式
}
分支条件表达式2 -> {
语句组2
表达式
}
…
分支条件表达式n -> {
语句组n
表达式
}
else -> {
语句组n+1
表达式
}
}
when表达式每一个分支最好是一条表达式,最后结束将表达式计算结果赋值给变量bar。需要注意的是when表达式不能省略else分支,除非编译器能判断出来,程序已经覆盖了所有的分支条件,这种情况一般会在when与枚举类结合使用时出现,因为枚举类的成员常量是固定几个取值。when与枚举类使用细节将在11.9节详细介绍这里不再赘述。
示例代码:
//代码文件:chapter9/src/com/a51work6/ch9.2.2.kt
package com.a51work6
fun main(args: Array) {
val testScore = 75 //设定一个数值用来测试
val grade = when (testScore / 10) {
9 -> '优'
8 -> '良'
7, 6 -> '中'
else -> '差'
}
println("Grade = " + grade)
val level = "优" //设定一个数值用来测试
val desc = when (level) {
"优"-> "90分以上"
"良"-> "80分~90分"
"中"-> "70分~80分"
"差"-> "低于60分"
else -> "无法判断"
}
println("说明 = " + desc)
}
上述代码中使用了两个when表达式,从代码中可见when表达式都没有省略else分支,读者可以将else分支注释掉,看一看是否能够编译通过。
循环语句能够使程序代码重复执行。Kotlin支持三种循环结构:while、do-while、和for。for和while循环是在执行循环体之前测试循环条件,而do-while是在执行循环体之后测试循环条件。这就意味着for和while循环可能连一次循环体都未执行,而do-while将至少执行一次循环体。
9.3.1 while语句
while语句是一种先判断的循环结构,格式如下:
while (循环条件) {
语句组
}
while循环没有初始化语句,循环次数是不可知的,只要循环条件满足,循环就会一直进行下去。
下面看一个简单的示例,代码如下:
//代码文件:chapter9/src/com/a51work6/ch9.3.1.kt
package com.a51work6
fun main(args: Array) {
var i = 0
while (i * i < 100_000) {//采用下划线分割数值可读性好
i++
}
println("i = " + i) //输出结果是i = 317
println("i * i = " + i * i)
//输出结果是i * i = 100489
}
上述程序代码的目的是找到平方数小于100000的最大整数。使用while循环需要注意几点,while循环条件语句中只能写一个表达式,而且是一个布尔型表达式,那么如果循环体中需要循环变量,就必须在while语句之前对循环变量进行初始化。本例中先给i赋值为0,然后在循环体内部必须改变循环变量的值,否则将会发生死循环。
9.3.2 do-while语句
do-while语句的使用与while语句相似,不过do-while语句是事后判断循环条件结构,语句格式如下:
do {
语句组
} while (循环条件)
do-while循环没有初始化语句,循环次数是不可知的,不管循环条件是否满足,都会先执行一次循环体,然后再判断循环条件。如果条件满足则执行循环体,不满足则停止循环。
下面看一个示例代码:
//代码文件:chapter9/src/com/a51work6/ch9.3.2.kt
package com.a51work6
fun main(args: Array) {
var i = 0
do {
i++
} while (i * i < 100_000)//采用下划线分割数值可读性好
println("i = " + i) //输出结果是i = 317
println("i * i = " + i * i)
//输出结果是i * i = 100489
}
该示例与上一节的示例是一样的,都是找到平方数小于100000的最大整数。输出结果也是一样的。
9.3.3 for语句
Kotlin语言中没有C语言风格的for语句,它的for语句相等于Java中增强for循环语句,只用于对范围、数组或集合进行遍历。
范围示例代码如下:
//代码文件:chapter9/src/com/a51work6/ch9.3.3.kt
…
for (num in 1…9) { //使用范围运算符
println("$num x $num = ${num *
num}")
}
输出结果如下:
1 x 1 = 1
2 x 2 = 4
3 x 3 = 9
4 x 4 = 16
5 x 5 = 25
6 x 6 = 36
7 x 7 = 49
8 x 8 = 64
9 x 9 = 81
上述代码是计算1~9的平方表,for循环中1…9范围,取值是大于等于1小等于9,范围将在9.5节详细介绍,这里不再赘述。num是从范围取出的元素,省略了var或val声明,注意num不是C语言风格for语句中的循环变量,它是范围、数组或集合中取出的元素。
集合遍历示例代码如下:
//代码文件:chapter9/src/com/a51work6/ch9.3.3.kt
…
// 声明并初始化Int数组
val numbers = intArrayOf(43, 32, 53, 54, 75, 7, 10) ①
for (item in numbers) { ②
println(“Count is:KaTeX parse error: Expected 'EOF', got '}' at position 9: item") }̲ 上述代码第①行是使用intA…i] =${numbers[i]}”)
}
运行结果:
numbers[0] = 43
numbers[1] = 32
numbers[2] = 53
numbers[3] = 54
numbers[4] = 75
numbers[5] = 7
numbers[6] = 10
跳转语句能够改变程序的执行顺序,可以实现程序的跳转。Kotlin中主要有3种跳转语句:break、continue和return。本节重点介绍break和continue语句的使用。return可以用于函数或Lambda表达式返回数据,详细内容将10.1节和14.3.4节介绍,本节暂不介绍。
9.4.1 break语句
break语句可用于上一节介绍的while、do-while和for循环结构,它的作用是强行退出循环体,不再执行循环体中剩余的语句。
在循环体中使用break语句有两种方式:带有标签和不带标签。语法格式如下:
break //不带标签
break@label //带标签,label是标签名
不带标签的break语句使程序跳出所在层的循环体,而带标签的break语句使程序跳出标签指示的循环体。
下面看一个示例,代码如下:
val numbers = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
for (i in numbers.indices) {
if (i == 3) {
// 跳出循环
break
}
println(“Count is: " + numbers[i])
}
在上述程序代码中,当条件i==3的时候执行break语句,break语句会终止循环,程序运行的结果如下:
Count is: 1
Count is: 2
Count is: 3
break还可以配合标签使用,示例代码如下:
label1@ for (x in 0…4) { ①
for (y in 5 downTo 1) { ②
if (y == x) {
// 跳转到label1指向的外循环
break@label1 ③
}
println(”(x,y) =( x , x, x,y)")
}
}
println(“Game Over!”)
默认情况下,break只会跳出最近的内循环(代码第②行for循环)。如果要跳出代码第①行的外循环,可以为外循环添加一个标签label1,注意在定义标签的时候后面跟一个@。代码第③行的break语句后面跟有@label1,注意中间没有空格,这样当条件满足执行break语句时,程序就会跳转出label1标签所指定的循环。
程序运行结果如下:
(x,y) = (0,5)
(x,y) = (0,4)
(x,y) = (0,3)
(x,y) = (0,2)
(x,y) = (0,1)
(x,y) = (1,5)
(x,y) = (1,4)
(x,y) = (1,3)
(x,y) = (1,2)
Game Over!
如果break后面没有指定外循环标签,则运行结果如下:
(x,y) = (0,5)
(x,y) = (0,4)
(x,y) = (0,3)
(x,y) = (0,2)
(x,y) = (0,1)
(x,y) = (1,5)
(x,y) = (1,4)
(x,y) = (1,3)
(x,y) = (1,2)
(x,y) = (2,5)
(x,y) = (2,4)
(x,y) = (2,3)
(x,y) = (3,5)
(x,y) = (3,4)
(x,y) = (4,5)
Game Over!
比较两种运行结果,就会发现给break添加标签的意义,添加标签对于多层嵌套循环是很有必要的,适当使用可以提高程序的执行效率。
9.4.2 continue语句
continue语句用来结束本次循环,跳过循环体中尚未执行的语句,接着进行终止条件的判断,以决定是否继续循环。
在循环体中使用continue语句有两种方式可以带有标签,也可以不带标签。语法格式如下:
continue //不带标签
continue@label //带标签,label是标签名
下面看一个示例,代码如下:
val numbers = intArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
for (i in numbers.indices) {
if (i == 3) {
continue
}
println("Count is: " + numbers[i])
}
程序运行结果如下:
Count is: 1
Count is: 2
Count is: 3
Count is: 5
Count is: 6
Count is: 7
Count is: 8
Count is: 9
Count is: 10
在上述程序代码中,当条件i==3的时候执行continue语句,continue语句会终止本次循环,循环体中continue之后的语句将不再执行,接着进行下次循环,所以输出结果中没有3。
带标签的continue语句示例代码如下:
label1@ for (x in 0…4) { ①
for (y in 5 downTo 1) { ②
if (y == x) {
continue@label1 ③
}
println("(x,y) = ( x , x, x,y)")
}
}
println(“Game Over!”)
默认情况下,continue只会跳出最近的内循环(代码第②行for循环),如果要跳出代码第①行的外循环,可以为外循环添加一个标签label1,然后在第③行的continue语句后面跟有@label1,这样当条件满足执行continue语句时,程序就会跳转出外循环。
程序运行结果如下:
(x,y) = (0,5)
(x,y) = (0,4)
(x,y) = (0,3)
(x,y) = (0,2)
(x,y) = (0,1)
(x,y) = (1,5)
(x,y) = (1,4)
(x,y) = (1,3)
(x,y) = (1,2)
(x,y) = (2,5)
(x,y) = (2,4)
(x,y) = (2,3)
(x,y) = (3,5)
(x,y) = (3,4)
(x,y) = (4,5)
Game Over!
由于跳过了x == y,因此下面的内容没有输出。
(x,y) = (1,1)
(x,y) = (2,2)
(x,y) = (3,3)
(x,y) = (4,4)
在前面的学习过程中多次用到了区间(Range)来表示一个范围,这一节介绍一下区间。
9.5.1 表示区间
区间有闭区间、开区间和半开区间之分,在程序设计中闭区间和半闭合区间使用比较多,闭区间包含上下临界值;半开区间包含下临界值,但不包含上临界值。
闭区间含义如下:
下临界值≤ 范围 ≤上临界值
半开区间含义如下:
下临界值≤ 范围 <上临界值
在Kotlin语言中闭区间采用区间运算符(…)表示,而半开区间则需要使用中缀运算符until表示。示例代码如下:
//代码文件:chapter9/src/com/a51work6/ch9.5.1.kt
package com.a51work6
fun main(args: Array) {
for (x in 0..5) { //定义闭区间包含0和5 ①
print("$x,")
}
println()
for (x in 0 until 5) { //定义半开区间包含0,不包含5 ②
print("$x,")
}
println()
for (x in 'A'..'E') { //定义闭区间包含'A'和'E' ③
print("$x,")
}
println()
for (x in 'A' until 'E') { //定义半开区间包含'A',不包含'E' ④
print("$x,")
}
}
运行结果:
0,1,2,3,4,5,
0,1,2,3,4,
A,B,C,D,E,
A,B,C,D,
上述代码中第①行和第④行使用区间运算符(…)定义了一个闭区间。代码第②行和第③行使用until中缀运算符定义了一个闭区间。
9.5.2 使用in和!in关键字
判断一个数值是否在区间中可以使用in关键字。而!in关键字,则是判断一个值不在区间中。此外,这两个关键字(in和!in)还可以判断一个数值是否集合或数组中。
示例代码如下:
//代码文件:chapter9/src/com/a51work6/ch9.5.2.kt
package com.a51work6
fun main(args: Array) {
var testscore = 80 //设置一个分数用于测试
var grade = when (testscore) { ①
in 90..100 -> "优"
in 80 until 90 -> "良"
in 60 until 80 -> "中"
in 0 until 60 -> "差"
else -> "无"
} ②
println("Grade = " + grade)
if (testscore !in 60..100) { //使用!in关键字 ③
println("不及格")
}
val strArray = arrayOf("刘备", "关羽", "张飞")
val name = "赵云"
if (name !in strArray) { ④
println(name + "不在队伍中")
}
}
上述代码第①行~第②行是使用when表达式,在分支条件表达式in关键字是否表达式testscore的值在区间中。代码第③行使用了!in关键字,判断testscore表达式值不在60…100)区间中。
另外,in和!in关键字还可以应用于集合或数组判断,代码第④行是判断name表达式值不在字符串集合strArray中。
通过对本章内容的学习,读者可以了解到Kotlin语言的程序流程控制,其中包括分支结构(if和when)、循环语句(while、do-while和for)和跳转语句(break和continue)等。最后介绍了Kotlin区间。