Destructuring declarationとは何か、構文とKotlinでの使用法

著者: IT Sectr 公開日: 2026-06-23 読了時間: 7 分

Destructuring declaration(分解宣言)は、オブジェクトを1つの式で複数の変数に分解できるKotlinの機能です。val (name, age) = user構文は、2つの変数を同時に宣言し、オブジェクトのコンポーネントから値を割り当てます。Kotlinドキュメント(2026年)によると、data classを扱う際、分解によりコード量が30~50%削減されます。Destructuring declarationはコードをより簡潔で読みやすくします。

主なポイント

  • Destructuring declaration — オブジェクトを変数に分解する構文 val (a, b) = obj
  • ComponentN() — 自動的に呼び出されるcomponent1()、component2()、component3()などの関数
  • Data classはプライマリコンストラクタで宣言されたすべてのプロパティに対して自動的にcomponentN()を生成
  • PairとTriple — 2つおよび3つの値に対応する組み込みの分解を備えた標準クラス
  • Map.Entry — 便利な反復処理のためにkeyとvalueへの分解をサポート

Destructuring declarationとは?

Destructuring declaration(分解宣言)は、クラスインスタンスを1つの式で複数の変数に分解できるKotlinの構文構造です。左側には括弧で囲まれた変数のリスト、右側には分解されるオブジェクトが含まれます。Kotlinは各変数に対して対応するcomponentN()関数(Nは括弧内の変数の順序番号)を自動的に呼び出します。

分解宣言は主にdata classと共に使用されます。Kotlinコンパイラは、プライマリコンストラクタのプロパティ数に基づいて、各data classに対してcomponent1()、component2()などを自動的に生成します。data修飾子のない通常のクラスでは、componentN()は生成されませんが、operatorキーワードを使用して手動で定義できます。

分解宣言は、ペアのコレクション、関数の結果、Mapの反復処理を扱う際にコードを簡素化できることから、人気のあるパターンになっています。person.firstNameやperson.lastNameと書く代わりに、開発者はval (firstName, lastName) = personと記述します。これにより、行数が減り、特にループやラムダでの可読性が向上します。

Kotlinでの分解の仕組み

val (a, b) = obj構文に遭遇すると、Kotlinコンパイラは次のコードを生成します:val a = obj.component1()、val b = obj.component2()。変数がvarで宣言されている場合、var代入が生成されます。各変数の型は、対応するcomponentN()関数の戻り値の型から推論されます。つまり、分解宣言は完全に型安全であり、コンパイル時にチェックされます。

kotlin
data class Person(val firstName: String, val lastName: String, val age: Int)

fun main() {
    val person = Person("Alice", "Smith", 30)

    // 分解: component1()、component2()、component3()
    val (firstName, lastName, age) = person
    println("$firstName $lastName、$age 歳")
}

リストでは、Personは3つのプロパティを持つdata classです。Kotlinはcomponent1() → firstName、component2() → lastName、component3() → ageを生成します。式val (firstName, lastName, age) = personはこれらの関数を呼び出し、結果を割り当てます。重要な点:変数名はプロパティ名と一致する必要はありません — コンパイラはcomponentN()の順序とその型のみを確認します。最初のN個のプロパティのみを分解し、残りは無視できます。

Data classesと標準型の分解

Data classesは分解宣言の主要な候補です。各data classは、プライマリコンストラクタのすべてのプロパティに対して自動的にcomponentN()を取得します。componentN()の順序はプロパティ宣言の順序に対応します。つまり、data classでフィールドの順序を変更すると、分解での変数の順序も変わり、微妙なバグにつながる可能性があります。

kotlin
// Pairの分解
val pair = Pair("key", 42)
val (key, value) = pair
println("$key -> $value")

// Tripleの分解
val triple = Triple("A", "B", "C")
val (first, second, third) = triple

// ループ内のMap.Entry
val map = mapOf("a" to 1, "b" to 2)
for ((k, v) in map) {
    println("$k = $v")
}

PairTripleは、2つと3つのコンポーネントに対して組み込みの分解を備えた標準クラスです。Pairは、個別のdata classを作成せずに関連する2つの値を返す必要がある関数の戻り値の型としてよく使用されます。Map.Entryのforループでは、(k, v) = entry構文がcomponent1()とcomponent2()を介してキーと値を分解します。これはKotlinでMapを反復処理する標準的なパターンであり、keyとvalueの明示的な呼び出しを完全に置き換えます。

未使用変数のアンダースコア

分解中に一部のコンポーネントが不要な場合、Kotlinではそれらをアンダースコア_で置き換えることができます。これにより可読性が向上します — 読者はそのコンポーネントが無視されていることをすぐに理解できます。さらに、_を使用すると、初期化されていない変数への誤ったアクセスを防げます:コンパイラは_に対して変数を作成しないため、未使用変数に関する警告がなくなります。

kotlin
data class Order(
    val id: Long,
    val customerName: String,
    val amount: Double,
    val status: String
)

fun process(orders: List<Order>) {
    for ((id, _, amount) in orders) {
        println("注文 $id: $amount")
    }
}

リストでは、Orderに4つのプロパティがありますが、ループに必要なのはidとamountだけです。customerNameの代わりのアンダースコアは、コンパイラに「component2()をスキップ」と指示します。Kotlinはcomponent2()を呼び出さず、このコンポーネントの変数も作成しません。重要:アンダースコアは分解宣言でのみ使用でき、data classコンストラクタでは使用できません。連続する複数のコンポーネントがスキップされる場合、それぞれに独自のアンダースコアが割り当てられます。

カスタムクラス用のcomponentN()の作成

data修飾子のないクラスの場合、operatorキーワードを追加して手動でcomponentN()を定義できます。これにより、ソースコードを変更できないサードパーティのクラスを含む、任意のクラスで分解宣言を使用できます — operatorを使用した拡張関数を記述するだけです。このアプローチは、自動componentN()を持たないライブラリクラスやJavaクラスに特に役立ちます。

kotlin
class Rect(val width: Int, val height: Int)

operator fun Rect.component1(): Int = width
operator fun Rect.component2(): Int = height

// gettersを持つJavaクラスの場合
operator fun Dimension.component1(): Int = width
operator fun Dimension.component2(): Int = height

fun main() {
    val rect = Rect(1920, 1080)
    val (w, h) = rect
    println("解像度: ${w}x${h}")
}

例では、Rectはdataのない通常のクラスです。operator fun component1() = widthとcomponent2() = heightを持つ拡張関数が、分解サポートを追加します。同様にjava.awt.Dimensionの場合:Dimensionに対するoperator拡張により、widthとheightに分解できます。制限:分解宣言のコンポーネントの最大数は5です。理論的にはcomponent6()以上を定義できますが、Kotlinは括弧内の6つ以上の変数に対応する構文を生成しません。

よくある質問

すべてのdata classが分解をサポートしていますか?

はい、すべてのdata classはプライマリコンストラクタのすべてのプロパティに対して自動的にcomponentN()を取得します。クラス本体で宣言されたプロパティ(val/varなしのコンストラクタパラメータを使用)はcomponentN()を生成しません。

変数の数が一致しない場合はどうなりますか?

val (a, b, c) = objのオブジェクトが2つのcomponent関数しか持たない場合、コンパイラはエラーを出力します。変数が少ない場合(val (a) = pair)、Kotlinはcomponent1()のみを呼び出し、component2()を無視します — エラーは発生しません。

ループなしでMapを分解できますか?

はい、Map.Entryは分解をサポートしています:val (key, value) = map.entries.first()。Map全体を一度に分解することはできません — forループまたはイテレータを使用して要素ごとにのみ分解できます。

ラムダ内で分解は機能しますか?

はい、Kotlinはラムダパラメータでの分解をサポートしています:map.forEach { (k, v) -> ... }。ラムダパラメータ内のdata classには、パラメータを囲む入れ子の括弧を追加する必要があります。

分解の最大サイズは?

Kotlinは分解宣言で最大5つの変数をサポートしています。5つ以上のプロパティを分解する必要がある場合は、それらを入れ子のdata classに結合するか、単一の変数内でPair/Tripleを使用してください。

まとめ

  • Destructuring declaration — 構文 val (a, b) = obj、オブジェクトを1つの式で複数の変数に分解
  • ComponentN() — 分解中にコンパイラによって自動的に呼び出される関数component1()、component2()、...
  • Data classはプライマリコンストラクタのすべてのプロパティに対して自動的にcomponentN()を生成
  • PairとTriple — 2つおよび3つのコンポーネントに対応する組み込みの分解を備えた標準クラス
  • アンダースコア _ 変数を作成せずに未使用のコンポーネントを置き換える
  • Operator componentN() 拡張関数で任意のクラス(Javaを含む)に分解を追加
  • Map.Entryのforループ ((k, v) in map) — 辞書を反復処理するための標準的なKotlinの慣用パターン

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください