iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 5
0
Mobile Development

30天,從0開始用Kotlin寫APP系列 第 5

Day 05 | Kotlin 中的條件式、循環式與跳轉方法

  • 分享至 

  • xImage
  •  

流程控制應該是每種程式語言或多或少都會實作的部份,這也是寫 Code 時必須要了解的核心觀念, Kotlin 流程控制大致

  • 條件式( Conditional Branch )
    • If-Else
    • When
  • 循環式( Conditional Loops )
    • For loops
    • While loops
  • 跳轉語句 ( Jump Statement )
    • Return
    • Break
    • Continue

條件式( Conditional Branch )

If-Else

If-Else 是很基礎的條件表達方法,Kotlin 中的基本使用規則與 Java 相同

Example:

val a: Int = 1
val b: Int = 10

if (a > b) {
    print("$a is bigger")
} else {
    print("$b is bigger")
}

提供另一種 Kotlin 中常見的寫法,可把判斷執行直接交給變數處理

val a: Int = 1
val b: Int = 10
val max: Int = if (a>b) a else b
print(max) // Output: 10

另外,值得一提的是 Kotlin 沒有三元運算子(Ternary operator)的表達方法,即 condition ? then : else ,根據開發團隊說法, 他們認為 If 已經夠用了,所以就不克提供。雖然 Kotlin 沒有支援三元運算子但有提供貓王運算子( Elvis oprator),這之後在 Null Safety 會再探討,有興趣的可以參考我之前寫過的介紹 長的帥,連Code都是香的 - Elvis Operator ?:

When

Kotlin 中的 When 與 C langage 與 Java 中的 Switch-case 相同,基礎用法如下

Example:

val a: Int = 1
when (a) {
    1 -> print("a == 1")
    2 -> print("a == 2")
    else -> print("a is neither 1 nor 2")
}
// Outout: a == 1

而 Kotlin 的 when 還有幾種變化型

  1. 多個判斷條件放在一起
val a: Int = 1
when (a) {
    0, 1 -> print("a == 0 or a == 1")
    2 -> print("a == 2")
    else -> print("a is neither 1 nor 2")
}
// Outout: a == 0 or a == 1
  1. 加入 in 判斷值是否在 range
val a: Int = 30
when (a) {
    in 1..10 -> print("a is in 1 to 10")
    !in 11..20 -> print("a is outside of range")
    else -> print("none of the above")
}
// Output: a is outside of range
  1. 可以處理 Error handler extension
when(val result = client.requestUserProfile(userId)) {
    is UserProfileResult.Success -> print(result.userProfile)
    is UserProfileResult.Error -> print("Get some error: ${result.Error}")
}
  1. 未提供任何參數,則 Branch 條件只是 Boolean 表達式
val a = 10
when {
    a % 2 != 0 -> print("$a is odd")
    a % 2 == 0 -> print("$a is even")
}
// Output: 10 is even

因為 when 的寫法與變化有很多種,因此 when 在 Kotlin 中的應用場景也是非常多,尤其是在處理 Error handler 的時候,更是會頻繁的出現 when 判斷喔

循環式( Conditional Loops )

相信有寫程式的開發者一定對 Loops 不陌生,基本上用法和其他程式語言都非常相似

For-loops

For-loops 提供 Iterator 遍歷所有內容,在 For-loop 中,會隱式方法獲取 Iterator

Example:

val numbers = listOf("one", "two", "three")
for (item in numbers) {
    println(item)
}
// Output: 
// one
// two
// three

For-loop 可以搭配 range expression 達到更多元的操作
Example:

for (i in 1..3) {
    println(i)
}
// Output:
// 1
// 2
// 3

for (i in 10 downTo 0 step 2) {
 println(i)
}
// Output:
// 10
// 8
// 6
// 4
// 2
// 0

神奇的 Iterator 問題

另外補充一篇滿有趣的文章,是我在查 Iterator 時發現的

他提供了一個很有趣的問題,讓我們先來看看問題長怎樣:

// 當 list 元素內容是 [1, 3, 5] 時,執行會是正確的
val list = mutableListOf(1, 3, 5)
for (i in list) {
    if (i.equals(3)) list.remove(i)
}
println(list)
// Output: [1, 5]

// 然而,當 list 元素內容是 [1, 2, 3, 4, 5] 時,會報錯誤訊息
val list = mutableListOf(1, 2, 3, 4, 5)
for (i in list) {
    if (i.equals(3)) list.remove(i)
}
println(list)
// Output:
// Exception in thread "main" java.util.ConcurrentModificationException
//  at java.util.ArrayList$Itr.checkForComodification (ArrayList.java:909) 
//  at java.util.ArrayList$Itr.next (ArrayList.java:859) 
//  at FileKt.main (File.kt:3) 

看完上面的例子,是不是滿頭問號,難道是7月鬼門開,乖乖放不夠多所以 Compiler 不乖乖嗎?

這篇文章有深入去探討 Iterator的源碼,並且透過源碼也應證實作 Iterator 時,不要用集合自帶的方法去改變集合結構(例如 remove),而應該用 Itereator 自帶的方法對集合做操作,在 Kotlin 中可以用 filter 達到,這個例子我用到了 Higher-order function 和 Scope function,這些之後都會寫文章探討喔

val list = mutableListOf(1, 2, 3, 4, 5)
list.filter {
    !it.equals(3)
}.apply {
  println(this)
}

而我也是在用 Kotlin 實作這篇文章時,才發現原來 Kotlin Iterator 底層實作是 Java 的 Iterator,因此錯誤訊息等等都指向 Java exception,又學了一課

While-loops

while 和 do..while 用法本質上和 C 或 Java 用法一致,這篇就不多花時間探討

var a = 3
while (a > 0) {
    println(a)
    a--
}
// Output: 
// 3
// 2
// 1

跳轉語句 ( Jump Statement )

Return

在 Java 中 Return 使用非常常見,這種編寫方式在 Kotlin 中也有被保留下來

fun main() {
	println(sum(1, 10))
}

fun sum(a: Int, b: Int): Int {
    return (a + b)
}

但如果 return 是表達式,就可以省略 return

fun main() {
	println(sum(1, 10))
}

fun sum(a: Int, b: Int) = a + b

Continue 與 Break

continue 與 break 都是用來控制循環結構式,能達到中斷或跳過,目的與 Java 中的相同

Example:

for (i in 1..10) {
    if (i % 2 == 0) continue
    println(i)
}
// Output: 
// 1
// 3
// 5
// 7
// 9

Example:

for (i in 1..10) {
    if (i % 2 == 0) break
    println(i)
}
// Output: 
// 1

Label @

但如果在 forEach 方法裡面用 continuebreak ,Compiler 會回傳 'break' and 'continue' are only allowed inside a loop 錯誤
因此我們要在 forEach 裡使用 Label 達到 continuebreak 的效果,而且上面提到跳轉語句的用法,在 Kotlin 中也可以搭配 Label 做使用, Label 的用法就像奇異博士的傳送門,只需要加上標示符 @ 符號,就可以在表達式之間穿梭

Example:

listOf(1, 2, 3, 4, 5).forEach lit@{
    if (it == 3) return@lit
    print(it)
}
print(" done with explicit label")
// Output: 1245 done with explicit label

// 另外一種用法
listOf(1, 2, 3, 4, 5).forEach {
    if (it == 3) return@forEach
    print(it)
}
print(" done with explicit label")
// Output: 1245 done with explicit label

結論

今天介紹了 Kotlin 中該如何做到條件控制,以及探討了一個 Iterator 的問題。
明天會開始介紹 NullSafety ,以及如何將 NullSafety 結合 Scope function 和 Extension function。


Reference


上一篇
Day 04 | 變量、類型推斷以及字串模版
下一篇
Day 06 | Kotlin 中的 Null Safety 與 Scope Function
系列文
30天,從0開始用Kotlin寫APP30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言