ジェネリクス
ジェネリクスにより、コードはより安全で柔軟になります。このレッスンでは、Javaジェネリクスの使用方法を学びます。
ジェネリクスとは
ジェネリクスは、クラスやメソッドが複数の型で動作できるようにするパラメータ化された型です。
ジェネリクスの利点
| 利点 | 説明 |
|---|---|
| 型安全性 | コンパイル時の型チェック |
| コード再利用 | 1つのコードが複数の型で動作 |
| キャスト排除 | 手動の型変換が不要 |
ジェネリッククラス
構文
JAVA
public class クラス名<T> {
// Tを型として使用
}
例:ジェネリッククラス
JAVA
public class Box<T> {
private T value;
public void set(T value) {
this.value = value;
}
public T get() {
return value;
}
public static void main(String[] args) {
// Integerを格納
Box<Integer> intBox = new Box<>();
intBox.set(100);
int num = intBox.get(); // キャスト不要
// Stringを格納
Box<String> strBox = new Box<>();
strBox.set("Hello");
String str = strBox.get();
System.out.println("Integer Box: " + num);
System.out.println("String Box: " + str);
}
}
複数のジェネリックパラメータ
JAVA
public 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; }
@Override
public String toString() {
return key + "=" + value;
}
public static void main(String[] args) {
Pair<String, Integer> pair = new Pair<>("Alice", 95);
System.out.println(pair); // Alice=95
}
}
ジェネリックメソッド
構文
JAVA
public static <T> メソッド名(パラメータ) {
// Tを使用
}
例:ジェネリックメソッド
JAVA
public class GenericMethod {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.print(element + " ");
}
System.out.println();
}
public static <T> T getFirst(T[] array) {
if (array == null || array.length == 0) {
return null;
}
return array[0];
}
public static <T extends Comparable<T>> T getMax(T[] array) {
T max = array[0];
for (T element : array) {
if (element.compareTo(max) > 0) {
max = element;
}
}
return max;
}
public static void main(String[] args) {
Integer[] nums = {1, 2, 3, 4, 5};
String[] names = {"Alice", "Bob", "Charlie"};
printArray(nums); // 1 2 3 4 5
printArray(names); // Alice Bob Charlie
System.out.println("最初の数: " + getFirst(nums)); // 1
System.out.println("最初の名前: " + getFirst(names)); // Alice
System.out.println("最大値: " + getMax(nums)); // 5
System.out.println("最大の名前: " + getMax(names)); // Charlie
}
}
ジェネリックインターフェース
ジェネリックインターフェースの定義
JAVA
public interface Repository<T> {
void add(T item);
T get(int index);
List<T> getAll();
}
ジェネリックインターフェースの実装
JAVA
public class ArrayListRepository<T> implements Repository<T> {
private List<T> list = new ArrayList<>();
@Override
public void add(T item) {
list.add(item);
}
@Override
public T get(int index) {
return list.get(index);
}
@Override
public List<T> getAll() {
return new ArrayList<>(list);
}
public static void main(String[] args) {
Repository<String> repo = new ArrayListRepository<>();
repo.add("Alice");
repo.add("Bob");
System.out.println(repo.getAll()); // [Alice, Bob]
}
}
ジェネリックワイルドカード
ワイルドカードなし ?
JAVA
public static void printList(List<?> list) {
for (Object item : list) {
System.out.println(item);
}
}
上限ワイルドカード ? extends T
JAVA
// Numberとそのサブクラスのみ受け入れ
public static double sum(List<? extends Number> list) {
double total = 0;
for (Number num : list) {
total += num.doubleValue();
}
return total;
}
下限ワイルドカード ? super T
JAVA
// Integerとそのスーパークラスのみ受け入れ
public static void addNumbers(List<? super Integer> list) {
list.add(1);
list.add(2);
list.add(3);
}
例:ワイルドカード
JAVA
import java.util.ArrayList;
import java.util.List;
public class WildcardDemo {
// ワイルドカードなし
public static void printList(List<?> list) {
for (Object item : list) {
System.out.print(item + " ");
}
System.out.println();
}
// 上限ワイルドカード
public static double sum(List<? extends Number> list) {
double total = 0;
for (Number num : list) {
total += num.doubleValue();
}
return total;
}
public static void main(String[] args) {
List<Integer> ints = new ArrayList<>();
ints.add(1);
ints.add(2);
ints.add(3);
List<String> strs = new ArrayList<>();
strs.add("Alice");
strs.add("Bob");
printList(ints); // 1 2 3
printList(strs); // Alice Bob
System.out.println("合計: " + sum(ints)); // 6.0
}
}
型消去
Javaジェネリクスは型消去によって実装されています。ジェネリック情報はコンパイル後に消去されます。
JAVA
// コンパイル前
List<String> list = new ArrayList<>();
list.add("Hello");
// コンパイル後(型消去)
List list = new ArrayList();
list.add("Hello");
String str = (String) list.get(0); // 自動キャスト
⚠️ 注意: 実行時にジェネリック型情報を取得することはできません。
List<String>とList<Integer>は実行時には同じ型です。
ジェネリック制約
例:境界付きジェネリクス
JAVA
// TはComparableを実装する必要がある
public static <T extends Comparable<T>> T min(T a, T b) {
return a.compareTo(b) <= 0 ? a : b;
}
// TはNumberのサブクラスである必要がある
public static <T extends Number> double doubleValue(T num) {
return num.doubleValue();
}
一般的なジェネリック名
| 名前 | 意味 |
|---|---|
T |
Type(型) |
E |
Element(要素) |
K |
Key(キー) |
V |
Value(値) |
N |
Number(数値) |
❓ よくある質問
Q なぜジェネリクスを使用するのですか?
A 型安全性、コード再利用、キャスト排除のためです。
Q ジェネリクスはプリミティブ型をサポートしていますか?
A いいえ、ラッパークラスを使用してください。例えば、
List<int>はList<Integer>にする必要があります。Q
<?>と<Object>の違いは何ですか?A
<?>は不明な型で、読み取り専用です。<Object>はObject型で、読み書き可能です。📖 まとめ
- ジェネリクスにより、コードはより安全で柔軟になる
- ジェネリッククラス、ジェネリックメソッド、ジェネリックインターフェース
- ワイルドカード:
?、? extends、? super - 型消去:コンパイル後にジェネリック情報が消去される
📝 演習
- ジェネリックコンテナ: ジェネリックなStackクラスを実装
- ジェネリックメソッド: 配列をListに変換するジェネリックメソッドを実装
- 境界付きジェネリクス: 2つのオブジェクトを比較するメソッドを実装
次のレッスン
次のレッスンでは、実践:コレクション応用を学びます — コレクション知識を適用します。



