Menu
Coddy logo textTech

ジェネリッククラス

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

ジェネリクスがなぜ便利なのかを理解したところで、独自のジェネリッククラスを作成してみましょう。ジェネリッククラスは、クラス名の後の山括弧内に1つ以上の型パラメータを宣言し、それをクラス全体で使用することができます。

class Box<T> {
    private T content;
    
    public void set(T content) {
        this.content = content;
    }
    
    public T get() {
        return content;
    }
}

T は型パラメータです。これは、インスタンスを作成する際に実際の型に置き換えられるプレースホルダーです。任意の名前を使用できますが、慣習的には1文字の大文字が使われます。Type(型)の T、Element(要素)の E、Key(キー)の K、Value(値)の V などです。

ジェネリッククラスをインスタンス化するとき、山括弧の中に実際の型を指定します:

Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get();  // キャストは不要です

Box<Integer> intBox = new Box<>();
intBox.set(42);
Integer num = intBox.get();

右側の <> (ダイヤモンド演算子) に注目してください。Javaは宣言から型を推論します。また、複数の型を扱う必要があるクラスに対して、複数の型パラメータを使用することもできます:

class Pair<K, V> {
    private K key;
    private V value;
    
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    
    public K getKey() { return key; }
    public V getValue() { return value; }
}

Pair<String, Integer> pair = new Pair<>("age", 25);
challenge icon

チャレンジ

簡単

ジェネリッククラスの威力を示すギフト登録システムを構築しましょう。あらゆる種類のギフトアイテムを保持できる柔軟なストレージシステムと、複数の型パラメータを使用してギフト名とその詳細をペアにするレジストリを作成します。

コードは3つのファイルに分けて構成します:

  • GiftBox.java: あらゆる種類のギフトを包むことができるジェネリッククラスを作成します。GiftBox<T> は、型 T のプライベートフィールド item と、初期値が false のプライベートフィールド wrapped (boolean) を持つ必要があります。アイテムを受け取るコンストラクタ、アイテムを返す getItem() メソッド、wrapped を true に設定する wrap() メソッド、およびラップされた状態を返す isWrapped() メソッドを含めてください。ラップされた状態に応じて、item + " (wrapped)" または item + " (unwrapped)" のいずれかを返す getStatus() メソッドを追加します。
  • Registry.java: 受取人とそのギフトをペアにする、2つの型パラメータを持つジェネリッククラスを作成します。Registry<K, V> は、型 K のプライベートフィールド recipient と、型 V のプライベートフィールド gift を持つ必要があります。両方の値を受け取るコンストラクタ、ゲッターメソッド getRecipient()getGift()、および recipient + " -> " + gift を返す getEntry() メソッドを含めてください。
  • Main.java: ギフトレジストリを完成させましょう!受取人の名前 (String)、ギフトの説明 (String)、およびギフトの価値 (ドルを表す整数) の3つの入力を受け取ります。

    まず、ギフトの説明を含む GiftBox<String> を作成します。wrap() メソッドを使用してそれを包み、そのステータスを出力します。

    次に、ギフトの価値(ギフトカードの金額を表す)を含む GiftBox<Integer> を作成します。これは包まずに、ステータスを出力してアンラップ状態であることを示します。

    最後に、受取人の名前とギフトの説明をペアにする Registry<String, String> を作成し、レジストリエントリを出力します。

入力は、受取人の名前、ギフトの説明、ギフトの価値(整数)の順に3つ受け取ります。

同じ GiftBox クラスが String 型と Integer 型の両方でシームレスに機能することに注目してください。これがジェネリクスの素晴らしさです!また、Registry クラスは、複数の型パラメータを使用することで、任意の2つの型の間で柔軟なペアリングを作成できることを示しています。

チートシート

ジェネリッククラスは、クラス名の後の山括弧内に1つ以上の型パラメータを宣言し、それをクラス全体で使用できます。

class Box<T> {
    private T content;
    
    public void set(T content) {
        this.content = content;
    }
    
    public T get() {
        return content;
    }
}

T は型パラメータであり、インスタンスを作成する際に実際の型に置き換えられるプレースホルダーです。一般的な慣習では、単一の大文字が使用されます。T は Type(型)、E は Element(要素)、K は Key(キー)、V は Value(値)を表します。

ジェネリッククラスをインスタンス化する際は、山括弧内に実際の型を指定します。

Box<String> stringBox = new Box<>();
stringBox.set("Hello");
String value = stringBox.get();  // No casting needed

Box<Integer> intBox = new Box<>();
intBox.set(42);
Integer num = intBox.get();

<>(ダイヤモンド演算子)を使用すると、Java は宣言から型を推論できます。

複数の型を扱うクラスの場合は、複数の型パラメータを使用します。

class Pair<K, V> {
    private K key;
    private V value;
    
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    
    public K getKey() { return key; }
    public V getValue() { return value; }
}

Pair<String, Integer> pair = new Pair<>("age", 25);

自分で試してみよう

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // 入力を読み込む
        String recipientName = scanner.nextLine();
        String giftDescription = scanner.nextLine();
        int giftValue = scanner.nextInt();
        
        // TODO: gift description を含む GiftBox<String> を作成する
        // TODO: wrap() メソッドを使用してそれを包む
        // TODO: getStatus() を使用してそのステータスを出力する
        
        // TODO: gift value を含む GiftBox<Integer> を作成する
        // TODO: これは包まずに、そのステータスを出力する(unwrapped と表示されるはずです)
        
        // TODO: 受取人と gift description をペアにする Registry<String, String> を作成する
        // TODO: getEntry() を使用してレジストリエントリを出力する
    }
}
quiz icon腕試し

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

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