Javaジェネリクス完全解説: 型安全性・汎用メソッド/クラス・境界型パラメータの活用
Integer配列、String配列、または順序をサポートするあらゆる型の配列の要素をソートできる単一のソートメソッドを書けたら便利です。
Javaジェネリクスメソッドとジェネリクスクラスを使用すると、プログラマは単一のメソッド宣言で関連するメソッドのセットを、または単一のクラス宣言で関連する型のセットをそれぞれ指定できます。
ジェネリクスはコンパイル時型安全性も提供し、プログラマが無効な型をコンパイル時に検出できるようにします。
Javaジェネリクスの概念を使用すると、オブジェクト配列をソートするジェネリックメソッドを書き、Integer配列、Double配列、String配列などでそのジェネリックメソッドを呼び出して配列要素をソートできます。
ジェネリックメソッド
異なる型の引数で呼び出せる単一のジェネリックメソッド宣言を書けます。ジェネリックメソッドに渡される引数の型に基づいて、コンパイラは各メソッド呼び出しを適切に処理します。ジェネリックメソッドを定義するルールは以下の通りです。
- すべてのジェネリックメソッド宣言には、メソッドの戻り値の型の前に角括弧(
<と>)で区切られた型パラメータセクションがある(次の例では<E>)。 - 各型パラメータセクションには、カンマで区切られた1つ以上の型パラメータが含まれる。型パラメータは型変数とも呼ばれ、ジェネリック型名を指定する識別子。
- 型パラメータは戻り値の型を宣言するために使用でき、ジェネリックメソッドに渡される引数の型のプレースホルダーとして機能する。これらは実在型引数と呼ばれる。
- ジェネリックメソッドの本体は他のメソッドと同様に宣言される。型パラメータは参照型のみを表現でき、プリミティブ型(
int、double、charなど)は表現できないことに注意。
例: 単一のジェネリックメソッドで異なる型の配列を出力
public class GenericMethodTest {
// ジェネリックメソッド printArray
public static void printArray(E[] inputArray) {
// 配列要素を表示
for (E element : inputArray) {
System.out.printf("%s ", element);
}
System.out.println();
}
public static void main(String args[]) {
// Integer、Double、Characterの配列を作成
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println("Array integerArray contains:");
printArray(intArray); // Integer配列を渡す
System.out.println("\nArray doubleArray contains:");
printArray(doubleArray); // Double配列を渡す
System.out.println("\nArray characterArray contains:");
printArray(charArray); // Character配列を渡す
}
}
出力
Array integerArray contains: 1 2 3 4 5
Array doubleArray contains: 1.1 2.2 3.3 4.4
Array characterArray contains: H E L L O
境界型パラメータ
型パラメータに渡せる型の種類を制限したい場合があります。例えば数値を操作するメソッドはNumberまたはそのサブクラスのインスタンスのみを受け入れたいかもしれません。これが境界型パラメータの目的です。
境界型パラメータを宣言するには、型パラメータ名に続けてextendsキーワード、その上限を記述します。
例: 3つのComparableオブジェクトから最大値を返すジェネリックメソッド
以下の例は、クラスの場合は「extends」、インターフェースの場合は「implements」の一般的な意味でextendsが使用されることを示しています。
public class MaximumTest {
// 3つのComparableオブジェクトのうち最大のものを決定
public static > T maximum(T x, T y, T z) {
T max = x; // 最初はxが最大と仮定
if (y.compareTo(max) > 0) {
max = y; // yがこれまでで最大
}
if (z.compareTo(max) > 0) {
max = z; // zが現在最大
}
return max; // 最大のオブジェクトを返す
}
public static void main(String args[]) {
System.out.printf("Max of %d, %d and %d is %d\n\n", 3, 4, 5, maximum(3, 4, 5));
System.out.printf("Max of %.1f,%.1f and %.1f is %.1f\n\n", 6.6, 8.8, 7.7, maximum(6.6, 8.8, 7.7));
System.out.printf("Max of %s, %s and %s is %s\n", "pear", "apple", "orange", maximum("pear", "apple", "orange"));
}
}
出力
Max of 3, 4 and 5 is 5
Max of 6.6,8.8 and 7.7 is 8.8
Max of pear, apple and orange is pear
ジェネリッククラス
ジェネリッククラス宣言は、クラス名に型パラメータセクションが続く点を除いて、非ジェネリッククラス宣言と似ています。
ジェネリックメソッドと同様に、ジェネリッククラスの型パラメータセクションにはカンマで区切られた1つ以上の型パラメータを含められます。これらのクラスは、1つ以上のパラメータを受け取るため、パラメータ化クラスまたはパラメータ化型と呼ばれます。
例: ジェネリッククラスの定義
public class Box {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return t;
}
public static void main(String[] args) {
Box integerBox = new Box();
Box stringBox = new Box();
integerBox.add(new Integer(10));
stringBox.add(new String("Hello World"));
System.out.printf("Integer Value :%d\n\n", integerBox.get());
System.out.printf("String Value :%s\n", stringBox.get());
}
}
出力
Integer Value :10
String Value :Hello World
Java
- Javaプリミティブデータ型:例付き完全ガイド
- Java演算子をマスター:種類、構文、実践例
- Javaコメント:種類、使い方、ベストプラクティス
- Java拡張forループをマスター:構文、例、ベストプラクティス
- Java文字列をマスター:生成、メソッド、ベストプラクティス
- Javaインターフェース完全ガイド:概念、実装、ベストプラクティス
- Javaのtry-with-resourcesをマスターする:自動リソース管理の仕組みと活用法
- Javaアノテーション完全ガイド:種類、配置場所、実践例
- Javaアサーション完全ガイド:バグ早期発見とコード信頼性の向上
- Java型キャスト完全ガイド:拡大変換から縮小変換まで
- Javaジェネリクス完全ガイド:再利用可能で型安全なコードの構築