一、常量與變數
1.1 型別推導
在 Kotlin 中,只要是編譯器認識的型別,就可以自動推匯出變數的型別,不需要我們顯示的指定。
val a = "fancyluo" //推導 String
val b = 666 //推導 Int
val c = a + b //推導 String
複製程式碼
1.2 常量
Kotlin 中使用 value
的縮寫 val
來表示一個不可變的值型別,與 Java 中 final
的用法類似。
// Java
public static final String NAME = "fancyluo"
// Kotlin
val NAME = "fancyluo"
複製程式碼
以上的兩行程式碼在使用上來說是一樣的,如果你想重新給「NAME」賦值,是不被允許的,編譯器會報錯。
但是,它們本質上還是有區別的,下面引入一個概念
編譯期常量:值在編譯期就已經確定的常量,並且會把對它的引用全部替換為它的值。
Java 使用 final 定義的是編譯期常量,而 Kotlin 使用 val 定義的是不可變的值型別,也可以稱為執行時常量。如果想要在 Kotlin 中定義編譯期常量,那麼需要使用 const 關鍵字。
const val NAME = "fancyluo"
複製程式碼
1.3 變數
Kotlin 中使用 variable
的縮寫 var
來表示變數,變數可以被重新賦值。
var x = "fancyluo"
x = "HiphopMan"
複製程式碼
二、函式
我們先來看看函式的語法,如下
- fun [函式名]([引數列表]):[返回值型別]{[函式體]}
- fun [函式名]([引數列表]) = [表示式]
複製程式碼
Kotlin 中的函式以 fun
開頭,下面以幾個例子來說明 Kotlin 函式的使用方法。
2.1 有返回值
Kotlin 中函式的返回值寫在引數列表的後面,以冒號加一個返回值的型別表示。
fun count(price: Int, sum: Int): Int {
return price * sum
}
複製程式碼
如果一個函式只是返回一個表示式的值,那可以使用更簡潔的寫法,直接使用等號後面跟表示式即可。
fun count(price: Int, sum: Int): Int = price * sum
複製程式碼
如果可以推匯出表示式的型別,那麼返回值也可以忽略不寫。
fun count(price: Int, sum: Int) = price * sum
複製程式碼
2.2 無返回值
Kotlin 中函式如果沒有返回值,預設返回的是 Unit
,類似於 Java 中的 void
。Unit
本身沒什麼意義,平時開發中並不用顯示的指定,只要知道這麼一回事就好了。
fun printName(name:String):Unit{
println(name)
}
複製程式碼
當函式體為一個表示式的時候可以用簡化的寫法,這時候函式的返回值就是表示式的返回值,都是返回 Unit
。
fun printName(name:String) = println(name)
複製程式碼
2.3 匿名函式
匿名函式無需函式名,但必須賦值給一個變數或常量,否則編譯器會報錯。
var sum = fun(a: Int, b: Int) = a + b
println(sum(6,6))
複製程式碼
2.4 編寫函式的建議
- 遵循單一職責原則,功能要單一。
- 函式起名應該要顧名思義,儘量避免不符合規範的命名。
- 引數的個數不要太多。
三、Lambda 表示式
3.1 語法與示例
我們先來看看 Lambda 表示式的語法。
- {[引數列表] -> [函式體,最後一行是返回值]}
Lambda 表示式其實也就是匿名函式,下面看個例子。
// 匿名函式
var sum = fun(a: Int, b: Int) = a + b
// Lambda 表示式
var sum = { a: Int, b: Int -> a + b }
// 無參無返回值
var printName = { println("fancyluo") }
複製程式碼
可以看到,上面定義的 Lambda 表示式有兩個引數 a 和 b,a + b 則為表示式的返回值,引數和返回值之間使用 -> 來分隔。如果 Lambda 表示式沒有返回值,那麼 -> 可以省略。
大家看到前面的例子會不會認為 Lambda 表示式只能寫一行呢?其實不然,函式體可以有多行,最後一行為 Lambda 表示式的返回值。
var sum = { a: Int, b: Int ->
println("a + b = ${a + b}")
a + b
}
複製程式碼
那麼 Lambda 表示式如何呼叫呢?使用 ()
,相當於執行了 invoke()
。
println(sum(1, 2))
println(sum.invoke(1, 2))
複製程式碼
3.2 Lambda 表示式的型別
在 Kotlin 中,函式也是一種型別,可以被賦值和傳遞
// 無參且返回值為 Unit 的函式型別:() -> Unit
val printName = { print("fancyluo")}
// 接收兩個整型引數且返回一個整型的函式型別:(Int,Int) -> Int
val sum = { a: Int, b: Int -> a + b }
// Array 的擴充套件方法
// 接收一個 T 型別引數並返回 Unit 型別的函式型別
public inline fun <T> Array<out T>.forEach(action: (T) -> Unit): Unit {
for (element in this) action(element)
}
複製程式碼
前面我們說過,呼叫 Lambda 表示式就是呼叫其 invoke() 方法,而 Kotlin 在Functions.kt 檔案裡定義了 Function0 ~ Function22 這 23 個型別,Lambda 表示式的invoke() 方法接收幾個引數,叫表明它是 FuntionN 型別。以上面的例子來說,printName 就是 Function0 型別,sum 就是 Function2 型別。
// Function0 型別:() -> Unit
public interface Function0<out R> : Function<R> {
public operator fun invoke(): R
}
// Function2 型別:(Int,Int) -> Int
public interface Function2<in P1, in P2, out R> : Function<R> {
public operator fun invoke(p1: P1, p2: P2): R
}
複製程式碼
那如果我們定義了接收 23 個引數的 Lambda 表示式呢?那麼執行時就會丟擲找不到Function23
這個類的異常。
3.3 Lambda 表示式的簡化
當使用 Lambda 表示式作為函式的引數時,可以有很多簡化的寫法,下面一一演示。
// 完整寫法
intArrayOf(1, 2, 3, 4).forEach({ element -> println(element) })
// Lambda 表示式引數只有一個的時候,可以忽略不寫,使用 it 代替
intArrayOf(1, 2, 3, 4).forEach({ println(it) })
// 函式的最後一個引數是 Lambda 表示式,大括號可以移到括號外邊
intArrayOf(1, 2, 3, 4).forEach(){ println(it) }
// 函式只有Lambda 表示式一個引數,小括號也可以省略
intArrayOf(1, 2, 3, 4).forEach{ println(it) }
// forEach 引數型別:(T) -> Unit
// println 函式型別:(Any) -> Unit
// 入參、返回值與形參一致的函式可以用函式引用的方式作為實參傳入
intArrayOf(1, 2, 3, 4).forEach(::println)
複製程式碼
最後再提一個問題,看如下程式碼。
fun main(args: Array<String>) {
intArrayOf(1, 2, 3, 4).forEach {
if (it == 3) return
println(it)
}
println("這裡不會被列印")
}
複製程式碼
在 Lambda 表示式裡面使用 return
相當於 return
了 main
函式。如果只想終止迭代的話,需要使用標籤。
fun main(args: Array<String>) {
intArrayOf(1, 2, 3, 4).forEach ForEach@ {
if (it == 3) return@ForEach
println(it)
}
println("這裡不會被列印")
}
複製程式碼