Kotlinには、ScalaやPythonのような 汎用Tuple型(Tuple1, Tuple2, Tuple3 …)の体系は存在しません。
しかし、複数の値をまとめて扱う用途は、次の仕組みによって実現できます。
PairTripledata classDestructuring 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つのプロパティを持っています。
firstsecond
val pair = Pair(1, "apple")
println(pair.first)
println(pair.second)
出力
1
apple
to を使ったPair生成
Kotlinでは次の書き方が非常によく使われます。
val pair = 1 to "apple"
これは次の意味です。
Pair(1, "apple")
to は infix関数として定義されています。
概念的には次のような関数です。
infix fun <A, B> A.to(that: B): Pair<A, B>
Map生成での使用
Pair は Map を作るときに頻繁に使われます。
val map = mapOf(
"apple" to 100,
"banana" to 200
)
この "apple" to 100 は Pairを作っているだけです。
Triple(3要素Tuple)
3つの値をまとめるクラスが Triple です。
基本構文
val triple = Triple(1, "apple", true)
型
Triple<Int, String, Boolean>
要素の取得
Tripleには3つのプロパティがあります。
firstsecondthird
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についてでした。
最後までお読みいただき、ありがとうございました。










