MENU

【Kotlin】レッスン3-06:ジェネリクスの基礎を理解しよう|型に依存しない関数を作成

tggaa478@yahoo.co.jp

一つ前のページでは関数のオーバーロードについて学習しました。

今回は ジェネリクスの基礎 について見ていきましょう。

Lesson1:基礎文法編
Lesson2:制御構造編
Lesson3:関数編

 ・Lesson3-1:関数の基本を理解しよう
 ・Lesson3-2:デフォルト引数とキーワード引数を理解しよう
 ・Lesson3-3:関数の戻り値を理解しよう
 ・Lesson3-4:真偽値を返す関数を理解しよう
 ・Lesson3-5:関数のオーバーロードを理解しよう
 ・Lesson3-6:ジェネリクスの基礎を理解しよう ◁今回はココ
 ・確認問題3-☆1:ブラックジャックゲームを作ろう
 ・確認問題3-☆2:丁半賭博ゲームを作ろう
 ・確認問題3-☆3:モンスターとのバトルゲームを作ろう
Lesson4:コレクション編
Lesson5:オブジェクト指向編

<<前のページ

学習記事一覧

次のページ>>

ジェネリクスの基本|柔軟で再利用可能なコードを

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で効率的かつ型安全なプログラムを構築するための重要な概念です。

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

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

練習問題|ジェネリクスを使った汎用関数で複数型を出力・比較しよう

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

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

この問題の要件

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

  1. 関数 printItem を作成すること。
    • ジェネリクス型 <T> を使用し、引数 item を出力する関数にすること。
    • 出力例:「入力された値: こんにちは」
  2. 関数 areEqual を作成すること。
    • ジェネリクス型 <T> を使用し、2つの引数 a と b が等しいかどうかを判定して返す関数にすること。
    • 出力例:「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からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

Q
ヒント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の値を出力

Q
ヒント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が等しいかの結果
    */
}

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

練習問題の解答と解説

この問題の正解コードとその解説は以下の通りです。

クリックして開いて確認してください。

Q
正解コード
// 汎用的な値を出力する関数 (ジェネリック関数)
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")
}
Q
正解コードの解説

コードをブロックごとに分割して解説します。

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

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学習サイトにするため、ぜひ 問い合わせフォーム からご意見下さい。

<<前のページ

学習記事一覧

次のページ>>

記事URLをコピーしました