1. 概要

このチュートリアルでは、Kotlin関数から複数の値を返す方法を学習します。 最初の2つのセクションでは、Kotlinでのタプルと破壊宣言について簡単に説明します。 次に、これらの宣言を使用して、関数から複数の値を返します。

2. Kotlinにタプルはありません

C#やScalaなどの一部のプログラミング言語は、タプルをファーストクラスでサポートしています。 したがって、タプルを変数に割り当てたり、関数に渡したり、関数から返すことも簡単にできます。

一方、Kotlinは任意のタプルの作成をサポートしていないため、次のような複数の値を返すことはできません。

// won't work
fun returnMultiple(): (Int, Int) {
    return 1, 2
}

上記の例のように複数の値を返すことはできませんが、呼び出しサイトで限定されたタプルのような構文を模倣できます。 具体的には、次のように書くことができます。

val (id, name) = returnIdAndName()

このタプルのような構文は、割り当てにのみ適用できます。 したがって、関数に引数を渡すときにこのトリックを使用することはできません。

考えられる解決策についての一般的な考え方がわかったので、解決策がどのように機能するかを見てみましょう。

3. 破壊宣言

Kotlinでは、オブジェクトを一連の変数に分解することができます。 たとえば、次のデータクラスがあるとします。

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

タイプUserの変数を、割り当ての2つのプロパティに分解できます。

val (id, name) = User(1, "Ali")

上に示したように、オブジェクトインスタンスを1つの変数に割り当てる代わりに、その一部を別の変数に割り当てています。 それが宣言の破壊の本質です。宣言は一度に複数の変数を作成します

これらのタイプの宣言は、演算子関数をcomponentN命名規則で定義しているタイプにのみ使用できます。 つまり、 component1 関数は最初の非構造化変数であり、component2関数は2番目の変数です。

Kotlinでは、宣言を破棄するためにいくつかのタイプがすでに使用可能です。

  • data クラスは、すべてのプロパティのコンポーネント関数を宣言しているため
  • ペアトリプルなどの一部の組み込みタイプ
  • コレクションの種類

したがって、基本的に、上記のすべての型を使用して、関数から複数の値を返すことができます。 さらに、 componentN 関数を使用してカスタム型を定義し、それらを戻り型として使用することもできます。

4. 複数の変数を返す

4.1. ペア

破壊パターンを使用して複数の値を返すことができることがわかったので、その例をいくつか見てみましょう。 手始めに、 任意のタイプの2つの値を返す場合は、ペアを返すことができます関数から、呼び出しサイトでそれを分解します

fun twoPair(): Pair<String, Int> = "Ali" to 33 // equivalent to Pair("Ali", 33)

fun main() {
    val (name, age) = twoPair()
    println("$name is $age years old")
}

上に示したように、関数本体のタプルのように複数の変数を返さない場合でも、呼び出しサイトではそれらをタプルとして扱います。 それが宣言を破壊する魔法です。

任意のタイプの2つの値を返す場合、ペア組み込みタイプが最適なオプションのようです。

4.2. トリプル

まったく同じように、 Tripleを使用すると、任意のタイプの3つの値を返すことができます。 タイプ

fun threeValues(): Triple<String, Int, String> = Triple("Ali", 33, "Neka")

fun main() {
    val (name, age, bornOn) = threeValues()
}

Triple は、あらゆる種類の3つの値を返すための最良のオプションでもあります。

4.3. 配列とコレクション

さらに、配列とコレクションを使用して、同じタイプの最大5つの値を返すことができます。

fun fiveValues() = arrayOf("Berlin", "Munich", "Amsterdam", "Madrid", "Vienna")

fun main() {
    val (v1, v2, v3, v4, v5) = fiveValues()
}

Kotlinの制限は5です。これは、配列とコレクションに5つのcomponentN拡張関数が定義されているためです。

4.4. データクラス

場合によっては、意味のある名前でカスタムタイプを定義する方がよい場合があります。 このように、破壊的な構文糖衣を使用することに加えて、関数宣言で特定の意味を伝えることができます。

data class Pod(val name: String, val ip: InetAddress, val assignedNode: String)
fun getUniquePod() = Pod("postgres", InetAddress.getLocalHost(), "Node 1")

fun main() {
    val (podName, ip, assignedNode) = getUniquePod()
}

ここでは、データクラスを使用して宣言を破棄し、複数の値を返すことができるという事実を利用しています。 この特定の例では、データクラスの名前は、Kubernetesポッドを処理していることを明確に示しています。これは以前のアプローチと比較して読みやすくなっています。

4.5. カスタムcomponentN関数

最後に、 componentN 関数を拡張関数として宣言して、非構造化宣言を利用できる場合もあります。

operator fun KeyPair.component1(): ByteArray = public.encoded
operator fun KeyPair.component2(): ByteArray = private.encoded

fun getRsaKeyPair(): KeyPair = KeyPairGenerator.getInstance("RSA").genKeyPair()

fun main() {
    val (publicKey, privateKey) = getRsaKeyPair()
}

上記の例では、java.security.KeyPairタイプで2つのcomponentN関数を定義して、より慣用的な方法で公開鍵と秘密鍵を抽出しています。 これは、すでに定義されているタイプと関数を使用する場合に特に役立ちます。

5. 結論

この記事では、Kotlinがタプルをサポートしていないため、タプルを使用して関数から複数の値を返すことができないという事実について説明しました。

この悪いニュースにもかかわらず、関数から複数の値を返すためのタプルの動作を模倣するために、破壊宣言を使用することができます。 具体的には、PairTriple、コレクションタイプと配列、最後に data class esなどの組み込み型を使用して、複数の値を返すことができます。 状況と値の数とそのタイプに応じて、さまざまなアプローチを使用できます。

いつものように、すべての例はGitHubから入手できます。