KotlinのTupleについて

採用はこちら

Kotlinには、ScalaやPythonのような 汎用Tuple型(Tuple1, Tuple2, Tuple3 …)の体系は存在しません

しかし、複数の値をまとめて扱う用途は、次の仕組みによって実現できます。

  • Pair
  • Triple
  • data class
  • Destructuring declaration(分解代入)

つまり、Kotlinでは「Tuple」という言葉自体はあまり使われませんが、Tuple的な機能は標準で提供されているという状態です。

目次

Tupleとは何か(前提)

Tupleとは

複数の値を1つのまとまりとして扱うデータ構造

のことです。

特徴は次の通りです。

  • 要素の型が異なっていてもよい
  • 要素数が固定
  • 順序がある

例(Python)

t = (10, "apple")

このTupleは

(整数, 文字列)

という2つの値の集合です。

Kotlinではこのような用途を PairやTripleなどで実現します。

Pair(2要素Tuple)

Kotlinで最も基本的なTuple的構造が Pair です。

Pairは「2つの値」をまとめるためのクラスです。

Pairの基本構文

val pair = Pair(1, "apple")

型は次のようになります。

Pair<Int, String>

要素の取得

Pairは2つのプロパティを持っています。

  • first
  • second
val pair = Pair(1, "apple")

println(pair.first)
println(pair.second)

出力

1
apple

to を使ったPair生成

Kotlinでは次の書き方が非常によく使われます。

val pair = 1 to "apple"

これは次の意味です。

Pair(1, "apple")

toinfix関数として定義されています。

概念的には次のような関数です。

infix fun <A, B> A.to(that: B): Pair<A, B>

Map生成での使用

PairMap を作るときに頻繁に使われます。

val map = mapOf(
    "apple" to 100,
    "banana" to 200
)

この "apple" to 100Pairを作っているだけです。

Triple(3要素Tuple)

3つの値をまとめるクラスが Triple です。

基本構文

val triple = Triple(1, "apple", true)

Triple<Int, String, Boolean>

要素の取得

Tripleには3つのプロパティがあります。

  • first
  • second
  • third
println(triple.first)
println(triple.second)
println(triple.third)

Destructuring(分解代入)

KotlinのTuple的機能で最も重要なのが 分解代入(destructuring declaration) です。

これは、オブジェクトの中身を複数の変数に分解する構文です。

Pairの分解代入

val pair = Pair(1, "apple")

val (number, fruit) = pair

println(number)
println(fruit)

このコードは内部的に次の呼び出しに変換されます。

pair.component1()
pair.component2()

componentN() の仕組み

Kotlinの分解代入は、componentN() 関数によって実現されています。

例えば

component1()
component2()
component3()

などです。

Pairの実装は概念的には次のような構造です。

data class Pair<A, B>(
    val first: A,
    val second: B
)

この data class では次の関数が自動生成されます。

component1() -> first
component2() -> second

data classでも分解代入できる

data class でも同じ仕組みが使えます。

data class User(
    val id: Int,
    val name: String
)

使用例

val user = User(1, "Taro")

val (id, name) = user

内部では次の関数が呼ばれます。

component1() -> id
component2() -> name

Mapでの分解代入

KotlinではMapのループでも分解代入が使えます。

val map = mapOf(
    "apple" to 100,
    "banana" to 200
)

for ((key, value) in map) {
    println("$key = $value")
}

これは Map.Entry

component1()
component2()

を利用しています。

Listの分解代入

Listでも分解代入が可能です。

val list = listOf(10, 20)

val (a, b) = list

ただし注意点があります。

  • componentN() が定義されている範囲でのみ利用可能
  • 要素数が不足すると例外が発生する

Pair / Triple の欠点

PairやTripleは便利ですが、実務では乱用すると問題が起こります。

理由は 可読性の低下です。

fun getUser(): Pair<Int, String>

この関数は

Pair<id, name>

なのか

Pair<age, city>

なのかが分かりません。

data classを使うのがベスト

この問題を解決するのが data class です。

data class User(
    val id: Int,
    val name: String
)

使用例

fun getUser(): User {
    return User(1, "Taro")
}

これなら意味が明確です。

Kotlinで複数値を扱うベストプラクティス

実務では次のように使い分けます。

方法用途
Pair一時的な2値
Triple一時的な3値
data class意味を持つ構造
Destructuring値の取り出し

Tuple的構造を自作することも可能

自分で componentN() を実装すれば、独自のTuple風クラスも作れます。

class Point(val x: Int, val y: Int) {

    operator fun component1() = x
    operator fun component2() = y
}

使用

val (x, y) = Point(10, 20)

まとめ

KotlinにはScalaのような汎用Tuple型はありませんが、次の仕組みによって同じことが実現できます。

  • Pair(2値)
  • Triple(3値)
  • data class(構造体)
  • Destructuring declaration(分解代入)

実務では

data class > Pair > Triple

の順で使用されることが多く、意味のあるデータにはdata classを使うのが最も推奨される方法です。

以上、KotlinのTupleについてでした。

最後までお読みいただき、ありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次