原文鏈接 Kotlin Controls and Expressions
有結(jié)果返回的是表達(dá)式,沒有返回的稱之為語句馆类,語句最大的問題是它沒有返回值混聊,那么想要保存結(jié)果就必然會產(chǎn)生副作用,比如改變變量乾巧。很多時候這是不夠方便的句喜,并且在多線程條件下,這甚至是不安全的沟于。Kotlin中咳胃,為了加強線程安全性和方便并發(fā)和異步,因此絕大多數(shù)語句都是表達(dá)式旷太。
[圖片上傳失敗...(image-b0f37e-1699879436422)]
分支表達(dá)式
Kotlin中沒有三元條件符(a > b ? a : b)展懈,但它的條件分支都是表達(dá)式,可以直接放在賦值符的右邊供璧,或者用在return語句中存崖。
if表達(dá)式
它是一個兩個分支的表達(dá)式,是有返回值的:
val maxV = if (a > b) a else b
當(dāng)然了睡毒,把它當(dāng)作常規(guī)的語句來使用也是沒有問題的:
var max: Int
if (a > b) {
max = a
} else {
max = b
}
when表達(dá)式
當(dāng)超過2個分支時来惧,if就不能用了,這時可以用when表達(dá)式演顾,它支持多個分支供搀,類似于其他語言中的switch:
when (x) {
1 -> println("it is 1")
2 -> println("it is 2")
else -> {
println("it is neight 1 nor 2")
}
}
需要注意的是隅居,每一行是一個條件,并不是單單指參數(shù)與其相等葛虐,比如:
when (x) {
in 1..5 -> println("Less than 5 bigger than 1")
x.isEven() -> println("it is even")
else -> println("It is neither even or less than 5")
}
當(dāng)然军浆,最重要的是when是一個表達(dá)式,可以直接用在賦值符的右邊挡闰,或者當(dāng)參數(shù)傳乒融,或者用在return中:
fun Request.getBody() =
when (val response = executeRequest()) {
is Success -> response.body
is HttpError -> throw HttpException(response.status)
}
這里的when就是函數(shù)的返回值,可以看到when是一個表達(dá)式摄悯,它會返回一個值赞季,這個值直接作為函數(shù)的返回值。
從這幾個示例可以看出when表達(dá)式相當(dāng)強大比其他語言的switch要強大許多奢驯,并且可以直接當(dāng)作返回值申钩,當(dāng)需要超過2個條件分支時就可以使用when表達(dá)式。
循環(huán)語句
循環(huán)是語句瘪阁,與其他語言也差不多撒遣。
while loop
while (x < 10) {
println(x)
x++
}
屁股向后式do-while loop
do {
x = poll()
} while (x < 10)
強大的for loop
這個是最強大,也是最常用的循環(huán)語句遍歷數(shù)組管跺,集合和固定步長時的首選义黎。
for (item in collection) print(item)
這里的collection可以是數(shù)組和集合(列表和Set)。嚴(yán)格來說只要collection類型實現(xiàn)了iterator()和next()豁跑,就可以在for loop中使用廉涕。
for加上range,可以非常強大:
for (i in 1..10) // = for (int i = 1; i <= 10; i++)
for (i in 0 until 10) // = for (int i = 0; i < 10; i++)
for (i in 9 downTo 0) // = for (int i = 9; i >= 0; i--)
for (i in 0 until 10 step 2) // = for (int i = 0; i < 10; i += 2)
如果是數(shù)組或者列表艇拍,但又必須要用索引狐蜕,也可以直接來:
for (i in array.indices) {
println(array[i]) // 'i' is the index
}
其實有更好的方式:
for ((index, value) in array.withIndex()) {
println("the element at $index is $value")
}
其實吧,Kotlin是多范式編程語言卸夕,天生支持函數(shù)式編程层释,多數(shù)情況下不建議直接上for loop,而是用函數(shù)式方式的forEach快集,數(shù)組和集合都支持forEach的:
array.forEach { println(it) }
終止語句
當(dāng)想提前退出函數(shù)的執(zhí)行贡羔,或者循環(huán)時,就需要用到終止語句碍讨,有三種return, break和continue治力。
return終止函數(shù)執(zhí)行
這個都比較熟悉,常規(guī)的用法都是一樣的勃黍,可以提前退出函數(shù):
fun plot(x: Int) {
if (x < 1) {
return -1
}
...
return y
}
但當(dāng)有嵌套的lambda時宵统,如不特別指定,return會退出外層的函數(shù),而不是像想當(dāng)然的退出lambda马澈,比如:
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return // non-local return directly to the caller of foo()
print(it)
}
println("this point is unreachable")
}
這個不是終止lambda的執(zhí)行瓢省,而是直接退出函數(shù)foo的執(zhí)行。如果想解決呢痊班,即也退出遍歷的lambda有三種方案:
- 使用標(biāo)簽
fun foo() {
listOf(1, 2, 3, 4, 5).forEach lit@{
if (it == 3) return@lit // local return to the caller of the lambda - the forEach loop
print(it)
}
print(" done with explicit label")
}
- 使用隱式標(biāo)簽勤婚,也即遍歷的方法當(dāng)作標(biāo)簽
fun foo() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach // local return to the caller of the lambda - the forEach loop
print(it)
}
print(" done with implicit label")
}
- 使用匿名函數(shù)而不是lambda,匿名函數(shù)與常規(guī)函數(shù)體效力一樣涤伐,所以return只在函數(shù)體內(nèi)生效
fun foo() {
listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
if (value == 3) return // local return to the caller of the anonymous function - the forEach loop
print(value)
})
print(" done with anonymous function")
}
這三種方式馒胆,如果非要使用,建議使用方式二凝果,用自帶的隱式label祝迂,因為比較方便,可讀性也不差器净。
但型雳,非常不建議如此使用return語句,這本是應(yīng)該避免的問題山害,lambda多半是用在函數(shù)式遍歷和處理纠俭,在lambda里面提加return本就是非常奇怪的事情。因為如果某些條件不滿足浪慌,想不執(zhí)行此lambda冤荆,應(yīng)該用filter啊,而不是笨拙的非要在lambda中去終止:
fun foo() {
listOf(1, 2, 3, 4, 5)
.filter(i -> i != 3)
.forEach { println(it) }
print("You can do whatever you like here.")
}
循環(huán)的終止
break終止當(dāng)前循環(huán)眷射,continue則是跳過當(dāng)前循環(huán)的當(dāng)前步驟匙赞,直接跳到下一次迭代。這兩個的常規(guī)使用與其他語言是一樣的妖碉。
但對于break,一般來說有一個痛點芥被,就是當(dāng)有循環(huán)嵌套時欧宜,break只能終止一層,如果想終止所有循環(huán)時拴魄,只能再手動的加條件去判斷冗茸,然后再一層一層的break,比如:
for (i in 0 until 10) {
var found = false
for (j in i until 10) {
if (array[i] + array[j] == target) {
found = true
break // only break inner for loop
}
}
if (found) {
break // this break outer for loop
}
}
這多少有點笨拙和丑陋匹中,Kotlin有更優(yōu)雅的解決方式夏漱,就是引入了標(biāo)簽label,可以給循環(huán)加上標(biāo)簽顶捷,在break時可以指定標(biāo)簽挂绰,同樣是上面的情況,可以這樣做:
loop@ for (i in 0 until 10) {
for (j in i until 10) {
if (array[i] + array[j] == target) {
break @loop // break all loops easily
}
}
}
其實吧服赎,這玩意兒跟當(dāng)年的goto是一樣的葵蒂,雖然可行交播,但不建議多使用,標(biāo)簽多了以后會讓程序的執(zhí)行更加的混亂践付,試想假如在層層循環(huán)中break錯了某個標(biāo)簽秦士,調(diào)試的難度是相當(dāng)大的。更多的時候需要仔細(xì)想想有沒有更好的遍歷方式永高,而不是靠標(biāo)簽來救命隧土。