【Kotlin】レッスン3-06:ジェネリクスの基礎を理解しよう

記事内に商品プロモーションを含む場合があります

この記事で学べる知識:ジェネリクスの基礎

この記事の練習問題を解くために必要な知識:
基礎文法、制御構造(レッスン1~2)関数の定義と呼出しデフォルト引数とキーワード引数関数の戻り値真偽値を返す関数関数のオーバーロードジェネリクスの基礎

<<前のページ Kotlin記事一覧 次のページ>>

Kotlinの「ジェネリクスの基礎」とは

この章ではKotlinにおける「ジェネリクスの基礎」の意味や使い方を学習します。用語の解説が不要な方はここをクリックして練習問題へ飛びましょう。




Kotlinでは異なる型を扱う関数やクラスを一つの形で定義できる仕組みとして「ジェネリクス」が提供されています。

ジェネリクスを利用すると、型を抽象化することで柔軟性と再利用性の高いコードを作成することができます。

この章では、関数におけるジェネリクスの基礎を学び、簡単な例を通してその使い方を理解します。

ジェネリック関数とは?

ジェネリクスとは型を具体的に指定せずにコードを記述するための仕組みです。

通常、関数は特定の型に依存して作られますが、ジェネリクスを使うことで異なる型に対応可能な汎用的な関数を作ることができます。

たとえば次の例を考えてみましょう。

「1つの値を出力する関数」を作る場合、文字列を扱う関数や数値を扱う関数など、扱う型ごとに関数を作らなければなりません。

fun printString(item: String) {
    println(item)
}

fun printInt(item: Int) {
    println(item)
}

この方法では扱う型が増えるたびに関数を新たに作成する必要があり、非常に効率が悪くなります。

しかしジェネリクスを使うと、次のように1つの関数で複数の型に対応できます。

fun <T> printItem(item: T) {
    println(item)
}
  • <T> は「型パラメータ」を意味します。T は任意の型を表し、必要に応じて別の名前(例:EK)に変更できます。
  • item: T は、関数が任意の型の引数を受け取れることを示します。
  • この関数を使えばどんな型でも出力できる柔軟な関数を実現できます。

このようにジェネリクスを使えば、型に関係なく再利用可能な関数を簡単に作成できます。

使用例:ジェネリック関数

ジェネリクスを使うことで、同じロジックを異なる型で再利用できる汎用的な関数を作成できます。

たとえば「2つの値を比較して同じかどうかを確認する関数」を考えてみましょう。

通常、型ごとに関数を作ると次のようになります。

fun compareInt(a: Int, b: Int): Boolean {
    return a == b
}

fun compareString(a: String, b: String): Boolean {
    return a == b
}

これでは扱う型ごとに新しい関数を作成する必要があります。

ジェネリクスを使えば、次のように1つの関数ですべての型に対応できます。

fun <T> compareItems(a: T, b: T): Boolean {
    return a == b
}

fun main() {
    println(compareItems(1, 1))          // true
    println(compareItems("Hello", "Hi")) // false
    println(compareItems(3.14, 3.14))    // true
}

この例ではcompareItems 関数を使って整数、文字列、浮動小数点数を比較しています。

ジェネリクスを使うことで型に依存しない汎用的な関数を作ることが可能になります。

ジェネリクスのメリット

ジェネリクスを使う主な利点は以下の通りです。

  1. コードの再利用性向上:型に依存しないため、汎用的なコードを書けます。
  2. 型安全性の向上:型キャストが不要になり、ランタイムエラーを防ぎます。
  3. 簡潔な設計:同じロジックを複数の型で使うための重複を減らせます。

これにより特に複雑なシステムの設計やメンテナンスが容易になります。

まとめ

【初心者向け】Kotlinの関数の使い方を分かりやすくまとめた概念図。 特に関数の呼び出しや戻り値、関数のオーバーロード、ジェネリクス、デフォルト引数とキーワード引数について視覚的に理解できるようまとめている。

ジェネリクスはKotlinで効率的かつ型安全なプログラムを構築するための重要な概念です。

この章では関数でのジェネリクスの基本的な使い方を学びました。次章以降ではさらに実用的なシーンでの応用方法について掘り下げていきます。

引き続き、実際にコードを書きながら理解を深めていきましょう!

ジェネリクスの練習問題:型に依存しない関数の便利さを体験しよう

「ジェネリクス」を使って汎用的な関数を作成し、さまざまな型のデータを処理するプログラムを作成してください。

以下の要件に従ってコードを書いてください。

この問題の要件

以下の要件に従ってコードを完成させてください。

  1. 関数 printItem を作成すること。
    • ジェネリクス型 <T> を使用し、引数 item を出力する関数にすること。
    • 出力例:「入力された値: こんにちは」
  2. 関数 areEqual を作成すること。
    • ジェネリクス型 <T> を使用し、2つの引数 ab が等しいかどうかを判定して返す関数にすること。
    • 出力例:「10と10は等しい: true」
  3. メイン関数で以下を実行すること:
    • printItem を使用し、異なる型(例:文字列、整数、小数)の値を出力すること。
    • areEqual を使用し、異なる型の値を比較して結果を出力すること。
  4. 出力を日本語で表現すること。
    • 例:「10と10は等しい: true」「入力された値: 123」

ただし、以下のような実行結果となるコードを書くこと。

*****↓↓正解コードの実行結果の例↓↓*****

=== printItem 関数の例 ===
入力された値: こんにちは
入力された値: 123
入力された値: 3.14

=== areEqual 関数の例 ===
10と10は等しい: true
"Kotlin"と"Java"は等しい: false
3.14と3.14は等しい: true

この問題を解くヒント

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

ヒント1【コードの構成を見る】

正解のコードは上から順に以下のような構成となっています。
(※下記の□はコード内のインデントを表しています)

1:汎用的な値を出力するジェネリック関数printItemの定義
  □ 引数itemを受け取り、printlnで「入力された値: 」を出力
2:汎用的な値を比較するジェネリック関数areEqualの定義
  □ 引数aとbを比較し、結果を返す
3:main関数の定義
  □ printlnで「=== printItem 関数の例 ===」を出力
  □ printItem関数を呼び出し、「こんにちは」を出力
  □ printItem関数を呼び出し、123を出力
  □ printItem関数を呼び出し、3.14を出力
  □ printlnで「=== areEqual 関数の例 ===」を出力
  □ areEqual関数を呼び出し、10と10を比較し、結果をresult1に代入
  □ printlnで「10と10は等しい: 」とresult1の値を出力
  □ areEqual関数を呼び出し、「Kotlin」と「Java」を比較し、結果をresult2に代入
  □ printlnで「”Kotlin”と”Java”は等しい: 」とresult2の値を出力
  □ areEqual関数を呼び出し、3.14と3.14を比較し、結果をresult3に代入
  □ printlnで「3.14と3.14は等しい: 」とresult3の値を出力

ヒント2【穴埋め問題にする】

以下のコードをコピーし、コメントに従ってコードを完成させて下さい。

// 汎用的な値を出力する関数 (ジェネリック関数)
fun <T> printItem(item: T) {
    /*【穴埋め問題1】
    ここにprintItem関数の中身を記述してください。「入力された値: $item」と出力するコードを書いてください。
    */
}

// 汎用的な値を比較する関数 (ジェネリック関数)
fun <T> areEqual(a: T, b: T): Boolean {
    /*【穴埋め問題2】
    ここにareEqual関数の中身を記述してください。引数aとbを比較し、結果を返すコードを書いてください。
    */
}

fun main() {

    // 値を出力するジェネリクス関数の例
    println("=== printItem 関数の例 ===")
    /*【穴埋め問題3】
    ここにprintItem関数を使用して「こんにちは」「123」「3.14」を出力するコードを書いてください。
    */

    // 値を比較するジェネリクス関数の例
    println("\n=== areEqual 関数の例 ===")
    /*【穴埋め問題4】
    ここにareEqual関数を使用して以下の結果を出力するコードを書いてください。
    ・10と10が等しい場合の結果
    ・"Kotlin"と"Java"が等しいかの結果
    ・3.14と3.14が等しいかの結果
    */
}

以上がこの問題の穴埋めコードです。

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。



練習問題の解答と解説

この問題の一つの正解例とそのコードの解説を以下に示します。

正解コードの例

例えば以下のようなプログラムが考えられます。

// 汎用的な値を出力する関数 (ジェネリック関数)
fun <T> printItem(item: T) {
    println("入力された値: $item")
}

// 汎用的な値を比較する関数 (ジェネリック関数)
fun <T> areEqual(a: T, b: T): Boolean {
    return a == b
}

fun main() {

    // 値を出力するジェネリクス関数の例
    println("=== printItem 関数の例 ===")
    printItem("こんにちは") // String型を渡す
    printItem(123)         // Int型を渡す
    printItem(3.14)        // Double型を渡す

    // 値を比較するジェネリクス関数の例
    println("\n=== areEqual 関数の例 ===")
    val result1 = areEqual(10, 10) // Int型の比較
    println("10と10は等しい: $result1")

    val result2 = areEqual("Kotlin", "Java") // String型の比較
    println("\"Kotlin\"と\"Java\"は等しい: $result2")

    val result3 = areEqual(3.14, 3.14) // Double型の比較
    println("3.14と3.14は等しい: $result3")
}

正解コードの解説

今回のコードはKotlinの「ジェネリクスの基礎」を学ぶためのものです。

ジェネリクスを使用することで異なる型を柔軟に扱える汎用的な関数を作成できます。

以下に、コードをブロックごとに分けて解説します。

汎用的な値を出力する関数

fun <T> printItem(item: T) {
    println("入力された値: $item")
}
  • ジェネリック関数: この関数はどの型にも対応する汎用的な関数です。
  • 型パラメータ <T>: T は型を表すパラメータで、関数がどの型でも受け取れることを示しています。
  • 機能: 引数 item を受け取り、それを出力します。

汎用的な値を比較する関数

fun <T> areEqual(a: T, b: T): Boolean {
    return a == b
}
  • ジェネリック関数: この関数もジェネリクスを使用し、どの型にも対応します。
  • 戻り値の型: この関数は Boolean を返します。比較結果が等しければ true、そうでなければ false を返します。
  • 用途: 2つの値が等しいかを比較する機能を提供します。

メイン関数

fun main() {
    // ジェネリック関数の呼び出し例
    println("=== printItem 関数の例 ===")
    printItem("こんにちは") // 文字列型
    printItem(123)         // 整数型
    printItem(3.14)        // 小数型
}

printItemの呼び出し: ジェネリック関数 printItem を使用して、異なる型の値を出力しています。

    // areEqual関数の呼び出し例
    println("\n=== areEqual 関数の例 ===")
    val result1 = areEqual(10, 10) // 整数型の比較
    println("10と10は等しい: $result1")

    val result2 = areEqual("Kotlin", "Java") // 文字列型の比較
    println("\"Kotlin\"と\"Java\"は等しい: $result2")

    val result3 = areEqual(3.14, 3.14) // 小数型の比較
    println("3.14と3.14は等しい: $result3")
}

areEqualの呼び出し: 異なる型でジェネリック関数 areEqual を呼び出しています。

まとめ

このコードを通じてKotlinのジェネリクスを使って型に依存しない汎用的な関数を作成する方法を学びました。

ジェネリクスを使用することでコードの再利用性が高まり、さまざまな型に柔軟に対応できるようになります。

今回の例をベースに、実際のプログラムでもジェネリクスを活用してみてください!

<<前のページ Kotlin記事一覧 次のページ>>

この記事への質問・コメント

この記事を作成するにあたりAIを活用しています。

問題ないことは確認していますが、もし間違いや表現の違和感などありましたら、ご指摘頂けると大変助かります。






    Kotlinのテキスト&問題集トップへ戻る
    トップページへ戻る