不知道大家還記不記得我在題目上有寫到「以 Kotlin 為主要語言」這幾個字呢?接下來就讓我來好好介紹一下在這 30 天即將使用到的程式語言-Kotlin,以及我們即將會使用什麼樣的 IDE(開發整合環境)來進行開發吧!
Kotlin 程式語言為 JetBrains 公司所開發的程式語言,其語言所撰寫的程式碼可編譯讓 JVM 執行,也可以編譯後利用 LLVM 轉成原生程式碼(Kotlin/Native)去執行,甚至也可以編譯成 JavaScript 語言讓瀏覽器去執行(Kotlin/JS),是個非常萬用的程式語言。由於可以編譯成讓 JVM 去執行的程式碼的特性,以及透過此特性,可以與 Java 語言進行橋接的關係,這使得使用 Kotlin 去開發 Android 程式相當容易,甚至在 2017 年的 Google I/O 中,Google 宣布在 Android 上會為 Kotlin 提供最佳的支援。不過近期 Kotlin 的野心越來越大,推出了 Kotlin Multiplatform,讓你可以在大多數的作業系統平台上共用程式碼,使得在開發多平台的程式上更加的方便。
Kotlin 程式語言與近代的語言設計相似,除了提供了 Object-Oriented Programming 的語法可以使用外,也導入了很多 Functional Programming 的設計在程式語言裡面。底下根據基本的程式語言功能整理了這些功能在 Kotlin 裡面大概長什麼樣子,如果有學過其他程式語言的話,可以先透過底下的整理去快速了解 Kotlin 的語法,不過如果要熟悉這個語言的話還是建議去找個教學來看。
Long
, Int
, Short
, Byte
Double
, Float
Char
Boolean
val
關鍵字來宣告唯獨之變數,值可為變數。var
關鍵字來宣告可變動之變數。const val
關鍵字來宣告常數,值只能為 compile-time 就可知道的數值。此宣告方式不能放在任何一個程式區塊(例如:函式定義區塊或是類別定義區塊)裡面去定義,也就是不能成為區域變數(local variable),只能放在全域(global)中去定義。or
下面的形式。val a: Int = 2
var b: Double = 2.0
const val c: Boolean = true
// or
val a = 2
var b = 2.0
const val c = true
String
。val a: String = "abc"
// String Interpolation
val b = 174
var c: String = "$b ${b + 33} XD" // "174 207 XD"
if
和 when
兩種語法。when
有點類似於 switch-case
,但功能更多元。if
和 when
兩種都可以當作 expression 來寫,每個區段的最後一行的值,會被當作若執行到該區段會讓整個語句回傳的值。(a ? b : c)
,使用上語法會更一致,且用法上會更彈性。// if
val count = 10
if (count < 5) {
println("太少")
} else if (count > 5) {
println("太多")
} else {
println("剛剛好")
}
// if-expression
val result = if (count < 5) {
"太少"
} else if (count > 5) {
"太多"
} else {
"剛剛好"
}
// when
when (count) {
in 0..4 -> println("太少")
5 -> println("剛剛好")
else -> println("太多")
}
// when-expression
val result = when (count) {
in 0..4 -> "太少"
5 -> "剛剛好"
else -> "太多"
}
IntArray
與 Array<Int>
的話, IntArray
就是對應到 int[]
,而 Array<Int>
就是對應到 Integer[]
。List
、 Map
與 Set
的建構方式,這些集合型態又分成了唯獨版本與可變版本,可變版本即是在該型態名稱前加上 Mutable
即是可變版本的型態名稱。// 各型態不同的陣列
val integers = intArrayOf(1, 2, 3) // IntArray
val doubles = doubleArrayOf(1.0, 2.0, 3.0) // DoubleArray
// 通用陣列
val generalIntegers = arrayOf(1, 2, 3) // Array<Int>
val generalDoubles = arrayOf(1.0, 2.0, 3.0) // Array<Double>
// 唯讀集合 (節錄自 Kotlin 官方教學程式碼)
// List<String>
val strings = listOf("Anne", "Karen", "Peter")
// Map<String, Int>
val map = mapOf("a" to 1, "b" to 2, "c" to 3)
// Set<String>
val set = setOf("a", "b", "c")
// 可變集合 (節錄自 Kotlin 官方教學程式碼)
// MutableList<String>
val strings = mutableListOf("Anne", "Karen", "Peter")
// MutableMap<String, Int>
val map = mutableMapOf("a" to 1, "b" to 2, "c" to 3)
// MutableSet<String, Int>
val set = mutableSetOf("a", "b", "c")
for...in...
與 while
, for...in...
的部分可結合 Kotlin 中的 Range
型態去運用,底下有相關的範例。// for (節錄自 Kotlin 官方教學程式碼)
val names = listOf("Anne", "Peter", "Jeff")
// 會接續印出 Anne 、 Peter 、 Jeff
for (name in names) {
println(name)
}
// 會接續印出 0 到 10
for (x in 0..10) println(x)
// 會接續印出 0 到 9
for (x in 0 until 10) println(x)
// 會接續印出 0, 2, 4, 6, 8
for (x in 0 until 10 step 2) println(x)
// 會接續印出 10, 8, 6, 4, 2, 0
for (x in 10 downTo 0 step 2) println(x)
// 會接續印出 0: Anne, 1: Peter, 2: Jeff
for ((index, value) in names.withIndex()) {
println("$index: $value")
}
// while (節錄自 Kotlin 官方教學程式碼)
// 會接續印出 0 到 9
var x = 0
while (x < 10) {
println(x)
x++
}
// 標籤型的 continue 和 break 用於跳出外層迴圈 (節錄自 Kotlin 官方教學程式碼)
outer@ for (n in 2..100) {
for (d in 2 until n) {
if (n % d == 0) continue@outer
}
println("$n is prime")
}
fun [函式名稱]([參數 1 名稱]: [參數 1 型態], [...其他參數]): [回傳型態] { [內容] }
。return
的話,可直接使用 =
來取代大括弧,並不用寫 return
。用此方式時由於回傳型態容易判斷,故: [回傳型態]
可加可不加。Unit
,並且若回傳參數為 Unit
時,則函式可以不用填寫回傳型態。// 節錄自 Kotlin 官方教學程式碼
fun happyBirthday(name: String, age: Int): String {
return "Happy ${age}th birthday, $name!"
}
// 如果函式只有一行 return 的話,可簡寫成等式
fun happyBirthday2(name: String, age: Int) =
"Happy ${age}th birthday, $name!"
// 無回傳參數的型態為 Unit 或不寫亦可
fun happyBirthday3(name: String, age: Int): Unit =
println("Happy ${age}th birthday, $name!")
val greeting = happyBirthday("Anne", 32)
throw
去丟例外,並使用 try...catch...
去接收例外。// 節錄自 Kotlin 官方教學程式碼
// throw
throw IllegalArgumentException("Value must be positive")
// try-catch
fun divideOrZero(numerator: Int, denominator: Int): Int {
try {
return numerator / denominator
} catch (e: ArithmeticException) {
return 0
}
}
init
區塊可以去做初始化的動作,詳情可見 Kotlin 的文件或是教學。Data Class
這種特殊的類別定義方式去定義它。若使用這種方式定義你的類別的話,則該類別定義完後,程式會自動幫你實作 toString()
、 equals()
與 hashCode()
這三個以純資料物件來說,最容易讓程式自己去定義得比較合乎使用的函式。class Person (val name: String, val age: Int) {
fun greeting() = "嗨!年紀 $age 歲的 $name!"
}
val sonic = Person("Sonic", 30)
val amy = Person("Amy", 22)
println(sonic.greeting) // 印出 "嗨!年紀 30 歲的 Sonic!"
println(amy.greeting) // 印出 "嗨!年紀 22 歲的 Amy!"
// Data Class
data class PersonData (val name: String, val age: Int)
val sonicData = PersonData("Sonic", 30)
println(sonicData.toString()) // 印出 PersonData(name=Sonic, age=30)
null
,唯有型態後面有接問號的才能存放 null
,這種有問號接在後面的型態稱作 Nullable
型態。Nullable
型態的變數都需要在確保不會有 null
的情況下才能對它進行操作。null
的運算子,可以方便對這些 Nullable
型態變數進行操作,像是 Safe call operator ?.
和 Elvis operator ?:
。Nullable
型態轉回不能放置 null
的一般型態的話,可以使用 Not-null assertion operator !!
。將該運算子接在變數後面後,即可轉型該變數的值成為原本不具有 null
型態的值。但如果剛好該變數內容是 null
的話,就會在轉型的過程中丟出 NullPointerException
,所以要特別小心使用。// 節錄自 Kotlin 官方教學程式碼
fun test(a: String, b: String?) {
if (b == null) {
// b 無法使用,若使用會有編譯錯誤
} else {
println(b.length)
}
}
// Safe call operator
class Person (val name: String, val age: Int) {
fun greeting() = println("嗨!年紀 $age 歲的 $name!")
}
val sonic: Person? = null
sonic?.greeting() // 由於 sonic 為 null,故不會執行
// Elvis operator
val x: Int? = null
val y: Int = 3
val z = x ?: y // 若 x 為 null,則給 y
val z = x ?: return y // 若 x 為 null,則函式提早 return 回 y 這個值。
// Not-null assertion operator
val w: Int = x!! // throw NullPointerException
([參數型態列表]) -> [回傳型態]
。{ [參數列表] -> [函式內容] }
,函式內容最後一行會是該函式回傳的值。HTML DSL
,看看 Kotlin 是怎麼被應用來去定義出一份 HTML 文件。// Function Type
fun operateTwoNumbers(a: Int, b: Int, f: (Int, Int) -> Int) =
f(a, b)
// 代入 Lambda Function
val add = operateTwoNumbers(3, 4, { a: Int, b: Int ->
a + b
})
// Lambda Function 的內容也可以是 if-expression
val divide = operateTwoNumbers(5, 0, {a: Int, b: Int ->
if (b == 0) 0 else a / b
})
// 函式最後一個若是 Function Type,可不放入括弧內
val minus = operateTwoNumbers(8, 3) {a: Int, b: Int ->
a - b
}
// Lambda 函式若傳入值型態確定,可省略
val multiple = operateTwoNumbers(5, 15) { a, b ->
a * b
}
// 若 Lambda 僅放入單值,可省略傳入參數,
// 參數用 it (它) 這個字代替
fun operateOneNumber(a: Int, f: (Int) -> Int) =
f(a)
// 代入一個「自己乘以自己」的函式
val square = operateOneNumber(3) { a -> a * a }
// 用 it 簡寫
val square = operateOneNumber(3) { it * it }
// Receiver Type
class Person (val name: String, val age: Int) {
fun greeting() = println("嗨!年紀 $age 歲的 $name!")
}
fun operateWithSonic(f: Person.() -> Unit) =
f(Person("Sonic", 30))
operateWithSonic { // 參數只有 Function Type 的話,連小括弧都可不寫。
greeting()
println("$name 真的很棒。")
}
看完上方的 Kotlin 語法整理後,接著應該就要來嘗試使用這個程式語言了,那通常我們會用什麼軟體來使用 Kotlin 語言開發程式呢?製作 Kotlin 的公司 JetBrains 自己也有研發一套非常完整的 Kotlin IDE,名稱叫做 IntelliJ IDEA。這套 IDE 有兩個版本可以使用,一個是免費使用的 Community 版本,另一個是付費訂閱使用的 Ultimate 版本。那如果你是剛開始接觸這個語言的話,可以先從 Community 版本使用看看,假設後來你真的愛上了這個語言,那到那個時候再來考慮要不要訂閱 Ultimate 的版本也不遲。下載網址可至官方網站去進行下載。
下載安裝完後,我們就來嘗試一下這套軟體吧!首先先開啟 IntelliJ IDEA,可以看見下方畫面。
點擊 New Project
後,進入開新專案介面。
在這個介面上選擇 Kotlin
,然後選擇 JVM
,接著會有專案的屬性設定,這個就隨你決定後,點擊 Start
即會進到可以編輯專案的介面。
在此介面左邊為專案的資料管理,將專案檔案點擊開來後,裡面會有個 src
的資料夾,這個資料夾是存放你的程式碼主要的地方。
接著對該資料夾點右鍵,增加一個 Kotlin 的檔案,可以叫做 Main.kt
。
增加檔案後,右方就會出現可以編輯該檔案的檔案編輯區。我們可以試著來執行看看 Kotlin 語法整理中類別部分的範例程式碼,只不過 Kotlin 會需要一個程式執行的進入點 main()
函式,故整理以後會變成下面的形式:
class Person (val name: String, val age: Int) {
fun greeting() = println("嗨!年紀 $age 歲的 $name!")
}
fun operateWithSonic(f: Person.() -> Unit) =
f(Person("Sonic", 30))
fun main() {
operateWithSonic { // 參數只有 Function Type 的話,連括弧都可不寫。
greeting()
println("$name 真的很棒。")
}
}
填寫完後,會在 main()
函式的旁邊看到綠色的箭頭,點下去後會看到 Run 'xxxx'
的選項,再點擊下去就會開始編譯執行。
最後就可以得到下圖中紅框的結果了!
到這裡我們就成功執行了一個簡單的 Kotlin 程式了,明天我們就會開始來實作資料管理伺服器的部分,就請各位敬請期待囉!
讚!真的很清楚
照著範例練習都可實作出來
Lambda Function比較難讀懂,範例Code很清楚,不過上方的敘述就比較繁雜一些。
期待此系列!!
======
反饋一個遇到的問題:
感謝支持!QwQ!!
啊!這點因為照著官方教學去說明就忘記提到了, const val
不能放在任何一個程式區塊(例如:函式定義區塊或是類別定義區塊)裡面去定義,只能放在全域中去定義。意思就是不能放在 fun main()
裡面,而要放在外面。這個我把它補上去好了XD
Lambda Function 的部分我覺得簡寫的描述還是拿掉好了,直接看 Code 其實比較清楚,我來改一下,感謝!QwQ
感謝回應!
原來const val是用在全域中