💎 ジェネリクス (Generics) の応用

ジェネリクスは、クラスやメソッドが扱うデータ型を、実際の利用時まで特定せず、抽象化しておくための仕組みです。これにより、コードの再利用性を高めながら、型の安全性を確保できます。

1. ジェネリクス導入のメリット

ジェネリクスが導入される前は、コレクションに何でも格納できましたが、以下の問題がありました。

問題点ジェネリクス導入後の解決
型キャストの煩雑さ格納したデータを取り出す際に、必ず目的の型へキャスト(型変換)する必要があった。
型の安全性誤った型のデータが格納されてもコンパイル時に検出できず、実行時エラー(ClassCastException)の原因となった。

Google スプレッドシートにエクスポート

💻 利用例(型の安全性の確保)

Java

import java.util.ArrayList;
import java.util.List;

// ジェネリクスを使用 (String型のみ格納可能)
List<String> names = new ArrayList<>();
names.add("Alice");
// names.add(123); // コンパイルエラー!String型以外は格納できない

// 取り出す際もキャスト不要
String name = names.get(0); 

🛠️ 2. ジェネリクスを使ったメソッドの作成

コレクションだけでなく、自分で作成するメソッドでもジェネリクスを定義して利用できます。これにより、任意のデータ型に対応できる汎用的なメソッドを作成できます。

基本の書式

メソッドの戻り値の型の前に、<T>(または他の任意の文字)を使って型パラメータを宣言します。慣習として、T (Type)、E (Element)、K (Key)、V (Value)がよく使われます。

Java

public class GenericMethodExample {
    
    // <T>を宣言することで、このメソッドが任意の型 T を扱えると示す
    public static <T> void printElement(T element) {
        System.out.println("要素の型: " + element.getClass().getName());
        System.out.println("値: " + element);
    }
    
    public static void main(String[] args) {
        // TがString型として推論される
        printElement("Hello, Generics!"); 
        
        // TがInteger型として推論される
        printElement(100); 
        
        // TがDouble型として推論される
        printElement(3.14);
    }
}

このprintElementメソッドは、StringIntegerDoubleのどの型に対しても、同じコードで安全に対応できます。

3. ジェネリクスと境界 (Bounds)

ジェネリクスで使える型を制限したい場合(例: 数値型だけを扱いたい場合)、extendsキーワードを使って**境界(Bounds)**を設定できます。

Java

// Tは Numberクラスかそのサブクラス(Integer, Doubleなど)に限定する
public static <T extends Number> double getDoubleValue(T num) {
    return num.doubleValue();
}

このメソッドは、Stringなどの数値ではない型を渡されるとコンパイルエラーになりますが、IntegerDoubleであれば安全に処理できます。