Kotlin 基礎-程式結構(上)

想念你的Android發表於2019-03-14

一、常量與變數

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 中的 voidUnit 本身沒什麼意義,平時開發中並不用顯示的指定,只要知道這麼一回事就好了。

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 相當於 returnmain 函式。如果只想終止迭代的話,需要使用標籤。

fun main(args: Array<String>) {
    intArrayOf(1, 2, 3, 4).forEach ForEach@ {
        if (it == 3) return@ForEach
        println(it)
    }
    println("這裡不會被列印")
}
複製程式碼


相關文章