Menu
CoddyTech

ジェネリクス入門

CoddyのJavaジャーニー「オブジェクト指向プログラミング」セクションの一部。レッスン 53/87。

値を保存し、後から取り出すクラスを作成する必要があると想像してください。ジェネリクスがなければ、型ごとに別々のクラスを書く必要があります。Integer用、String用、Double用などです。これではコードが重複し、保守が大変になります。

ジェネリクスを使うと、型安全性を維持しながら、あらゆる型で動作するクラスやメソッドを書けるため、この問題を解決できます。プレースホルダー(型パラメーターと呼ばれます)を定義し、そのクラスを使うときに実際の型に置き換えます。

// ジェネリクスなし - Objectを使用し、キャストが必要
List list = new ArrayList();
list.add("Hello");
String s = (String) list.get(0);  // 手動でのキャストが必要

// ジェネリクスあり - 型安全で、キャスト不要
List<String> list = new ArrayList<String>();
list.add("Hello");
String s = list.get(0);  // キャスト不要

山かっこ <T> は型パラメーターを表します。List<String> と書くと、このリストには文字列だけが含まれることをコンパイラーに伝えることになります。整数を追加しようとすると、実行時に失敗するのではなく、コンパイラーがすぐにエラーを検出します。

ジェネリクスには、明示的なキャストが不要になることと、実行時ではなくコンパイル時に型エラーを検出できることという、2つの重要な利点があります。これにより、コードの安全性が高まり、よりすっきりします。これからのレッスンでは、独自のジェネリッククラスやメソッドの作成方法を学びます。

challenge icon

チャレンジ

簡単

Objectをキャストして使う従来の方法と、型安全なジェネリクスを使う新しい方法を比較し、ジェネリクスの力を示すシンプルなストレージシステムを作りましょう。

違いを実際に確かめるため、3つのファイルを作成します。

  • OldBox.java:ジェネリクス以前の方法で値を格納するクラスを作成します。OldBoxには、型がObjectのprivateフィールドcontentを用意します。Objectを受け取って格納するコンストラクターと、Objectを返すgetContent()メソッドを含めます。この方法でも動作しますが、値を取り出すときにキャストが必要です。
  • GenericBox.java:次に、ジェネリクスを使った、現代的で型安全なバージョンを作成します。GenericBoxの宣言では型パラメーターTを使います。型がTのprivateフィールドcontent、T型の値を受け取るコンストラクター、そして型Tを返すgetContent()メソッドが必要です。このクラスを使うとき、キャストは不要です。
  • Main.java:両方の方法を並べて示します。入力は2つ受け取ります。単語(String)と数値(integer)です。

    まず、従来の方法を使います。単語を格納するOldBoxを作成し、Stringにキャストして取り出してから、次のように出力します:OldBox (with cast): [value]

    次に、ジェネリクスを使う方法です。同じ単語を格納するGenericBox<String>を作成し、キャストせずに取り出して、次のように出力します:GenericBox (no cast): [value]

    続いて、ジェネリクスが整数でも使えることを示します。数値を格納するGenericBox<Integer>を作成し、値を取り出して、次のように出力します:GenericBox Integer: [value]

    最後に、空行の後にType safety: Generics catch errors at compile time!を出力します。

入力は、単語(String)、数値(Stringとしてのinteger。これをパースします)の順に2つ受け取ります。

重要な違いに注目してください。OldBoxでは、(String) box.getContent()のようにキャストし、型が正しいことを期待する必要があります。GenericBoxでは、コンパイラーが型を認識するため、キャストは不要です。これがジェネリクスの主な利点です。キャストの手間をかけずに型安全性を確保できます。

自分で試してみよう

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String word = scanner.nextLine();
        int number = Integer.parseInt(scanner.nextLine());
        
        // TODO: wordを格納するOldBoxを作成する
        // Stringにキャストして取り出し、"OldBox (with cast): [value]"を出力する
        
        // TODO: wordを格納するGenericBox<String>を作成する
        // キャストせずに取り出し、"GenericBox (no cast): [value]"を出力する
        
        // TODO: numberを格納するGenericBox<Integer>を作成する
        // 取り出して"GenericBox Integer: [value]"を出力する
        
        // TODO: 空行を出力してから、次を出力する:
        // "Type safety: Generics catch errors at compile time!"
    }
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン

自分で練習してみよう: Javaオンラインコンパイラ