跳至主要内容

解構宣告 (Destructuring declarations)

有時,將一個物件解構 (destructure) 成多個變數會很方便,例如:

val (name, age) = person

這種語法稱為解構宣告 (destructuring declaration)。 一個解構宣告會一次建立多個變數。 你已經宣告了兩個新的變數:nameage,並且可以獨立使用它們:

println(name)
println(age)

一個解構宣告會被編譯成以下程式碼:

val name = person.component1()
val age = person.component2()

component1()component2() 函數是 Kotlin 中廣泛使用的慣例原則 (principle of conventions) 的另一個例子 (參見 +* 等運算符,以及 for 迴圈作為範例)。 任何東西都可以放在解構宣告的右側,只要可以對其呼叫所需數量的 component 函數即可。 當然,也可以有 component3()component4() 等等。

備註

componentN() 函數需要用 operator 關鍵字標記,以便允許在解構宣告中使用它們。

解構宣告也適用於 for 迴圈:

for ((a, b) in collection) { ... }

變數 ab 取得對集合元素呼叫 component1()component2() 所返回的值。

範例:從函數返回兩個值

假設你需要從一個函數返回兩個東西 - 例如,一個結果物件和某種狀態。 在 Kotlin 中,一種簡潔的方法是宣告一個 data class(資料類別) 並返回它的實例:

data class Result(val result: Int, val status: Status)
fun function(...): Result {
// computations

return Result(result, status)
}

// 現在,使用這個函數:
val (result, status) = function(...)

由於資料類別會自動宣告 componentN() 函數,因此解構宣告在這裡適用。

備註

你也可以使用標準類別 Pair 並讓 function() 返回 Pair<Int, Status>, 但通常最好讓你的資料被正確命名。

範例:解構宣告和 Map (映射)

遍歷 Map (映射) 最好的方式可能是這樣:

for ((key, value) in map) {
// 對 key 和 value 做一些事情
}

為了使其工作,你應該

  • 透過提供 iterator() 函數將 Map (映射) 表示為值的序列。
  • 透過提供函數 component1()component2() 將每個元素表示為一個 pair。

事實上,標準函式庫提供了這樣的擴充功能:

operator fun <K, V> Map<K, V>.iterator(): Iterator<Map.Entry<K, V>> = entrySet().iterator()
operator fun <K, V> Map.Entry<K, V>.component1() = getKey()
operator fun <K, V> Map.Entry<K, V>.component2() = getValue()

因此,你可以自由地在帶有 Map (映射) 的 for 迴圈中使用解構宣告(以及資料類別實例或類似物件的集合)。

用於未使用的變數的底線

如果你在解構宣告中不需要某個變數,你可以放置一個底線來代替它的名稱:

val (_, status) = getResult()

componentN() 運算符函數不會為以這種方式跳過的 component 呼叫。

在 Lambda (匿名函數) 中解構

你可以對 Lambda (匿名函數) 參數使用解構宣告語法。 如果一個 Lambda (匿名函數) 有一個 Pair 類型(或 Map.Entry,或任何其他具有適當的 componentN 函數的類型)的參數,你可以透過將它們放在括號中來引入幾個新的參數來代替一個參數:

map.mapValues { entry `->` "${entry.value}!" }
map.mapValues { (key, value) `->` "$value!" }

請注意宣告兩個參數和宣告一個解構 pair 而不是一個參數之間的區別:

{ a `->` ... } // 一個參數
{ a, b `->` ... } // 兩個參數
{ (a, b) `->` ... } // 一個解構 pair
{ (a, b), c `->` ... } // 一個解構 pair 和另一個參數

如果一個解構參數的 component 未被使用,你可以用底線替換它以避免發明它的名稱:

map.mapValues { (_, value) `->` "$value!" }

你可以為整個解構參數或為一個特定的 component 單獨指定類型:

map.mapValues { (_, value): Map.Entry<Int, String> `->` "$value!" }

map.mapValues { (_, value: String) `->` "$value!" }