Java ジェネリクス

Java ジェネリクスは JDK 5 で導入された新機能です。ジェネリクスはコンパイル時の型安全性チェックメカニズムを提供し、このメカニズムによりプログラマはコンパイル時に不正な型を検出できます。

ジェネリクスの本質はパラメータ化された型です。つまり、操作されるデータ型がパラメータとして指定されます。

次のような要件があると仮定します: 整数配列、文字列配列、さらには他のあらゆる型の配列をソートできるソートメソッドを書くには、どうすれば実装できるでしょうか?

答えは次を使用することですJava ジェネリクス。

Java ジェネリクスの概念を使用すると、オブジェクト配列をソートするジェネリックメソッドを書くことができます。そして、そのジェネリックメソッドを呼び出して、整数配列、浮動小数点数配列、文字列配列などをソートできます。


ジェネリックメソッド

呼び出し時に異なる型のパラメータを受け取ることができるジェネリックメソッドを書くことができます。ジェネリックメソッドに渡されるパラメータの型に応じて、コンパイラは各メソッド呼び出しを適切に処理します。

以下はジェネリックメソッドを定義するためのルールです:

  • すべてのジェネリックメソッド宣言には型パラメータ宣言部分(山かっこで区切られる)があり、その型パラメータ宣言部分はメソッドの戻り値の型の前にあります(以下の例の<E>)。
  • 各型パラメータ宣言部分には1つ以上の型パラメータが含まれ、パラメータ間はカンマで区切られます。ジェネリックパラメータは型変数とも呼ばれ、ジェネリック型の名前を指定するための識別子です。
  • 型パラメータは戻り値の型を宣言するために使用でき、ジェネリックメソッドが受け取る実際のパラメータ型のプレースホルダとしても使用できます。
  • ジェネリックメソッド本体の宣言は他のメソッドと同じです。型パラメータは参照型のみを表すことができ、基本型(int、double、charなど)は使用できないことに注意してください。

java のジェネリックマーカー:

  • E- Element(コレクション内で使用。コレクションには要素が格納されるため)
  • T- Type(Java クラス)
  • K- Key(キー)
  • V- Value(値)
  • N- Number(数値型)
  • ?- 不特定の java 型を表す

例

以下の例は、ジェネリックメソッドを使用して異なる型の配列要素を出力する方法を示しています:

例

public class GenericMethodTest { //ジェネリックメソッド printArray public static < E > 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( "整数配列の要素:" ); printArray( intArray ); //整数配列を渡す System.out.println( "\n倍精度型配列の要素:" ); printArray( doubleArray ); //倍精度型配列を渡す System.out.println( "\n文字型配列の要素:" ); printArray( charArray ); //文字型配列を渡す } }

上記のコードをコンパイルして実行すると、結果は次のようになります:

整型数组元素为:
1 2 3 4 5 

双精度型数组元素为:
1.1 2.2 3.3 4.4 

字符型数组元素为:
H E L L O 

境界のある型パラメータ:

場合によっては、型パラメータに渡すことを許可される型の範囲を制限したいことがあります。例えば、数値を操作するメソッドが Number または Number のサブクラスのインスタンスのみを受け入れたい場合などです。これが境界のある型パラメータの目的です。

境界のある型パラメータを宣言するには、まず型パラメータの名前をリストし、その後に extends キーワード、最後にその上限を続けます。

例

以下の例は、"extends" が一般的な意味での "extends"(クラス)または "implements"(インターフェース)としてどのように使用されるかを示しています。この例のジェネリックメソッドは、3つの比較可能なオブジェクトの最大値を返します。

例

public class MaximumTest { //3つの値を比較して最大値を返す public static <T extends Comparable<T>> 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( "%d、%d、%d の中で最大の数は %d\n\n", 3, 4, 5, maximum( 3, 4, 5 ) ); System.out.printf( "%.1f、%.1f、%.1f の中で最大の数は %.1f\n\n", 6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) ); System.out.printf( "%s、%s、%s の中で最大の数は %s\n","pear", "apple", "orange", maximum( "pear", "apple", "orange" ) ); } }

上記のコードをコンパイルして実行すると、結果は次のようになります:

3, 4 和 5 中最大的数为 5

6.6, 8.8 和 7.7 中最大的数为 8.8

pear, apple 和 orange 中最大的数为 pear

ジェネリッククラス

ジェネリッククラスの宣言は非ジェネリッククラスの宣言と似ていますが、クラス名の後に型パラメータ宣言部分が追加される点が異なります。

ジェネリックメソッドと同様に、ジェネリッククラスの型パラメータ宣言部分にも1つ以上の型パラメータが含まれ、パラメータ間はカンマで区切られます。ジェネリックパラメータは型変数とも呼ばれ、ジェネリック型の名前を指定するための識別子です。1つ以上のパラメータを受け取るため、これらのクラスはパラメータ化されたクラスまたはパラメータ化された型と呼ばれます。

例

以下の例は、ジェネリッククラスを定義する方法を示しています:

例

public class Box<T> { private T t; public void add(T t) { this.t = t; } public T get() { return t; } public static void main(String[] args) { Box<Integer> integerBox = new Box<Integer>(); Box<String> stringBox = new Box<String>(); integerBox.add(new Integer(10)); stringBox.add(new String("サンプル")); System.out.printf("整数値: %d\n\n", integerBox.get()); System.out.printf("文字列: %s\n", stringBox.get()); } }

上記のコードをコンパイルして実行すると、結果は次のようになります:

整型值为 :10

字符串为 :サンプル

型ワイルドカード

1、型ワイルドカードは通常、?を使用して具体的な型パラメータの代わりとします。例えばList<?>論理的にはList<String>,List<Integer>など、すべてのList<具体的な型引数>の親クラスです。

例

import java.util.*; public class GenericTest { public static void main(String[] args) { List<String> name = new ArrayList<String>(); List<Integer> age = new ArrayList<Integer>(); List<Number> number = new ArrayList<Number>(); name.add("icon"); age.add(18); number.add(314); getData(name); getData(age); getData(number); } public static void getData(List<?> data) { System.out.println("data :" + data.get(0)); } }

出力結果:

data :icon
data :18
data :314

解説:なぜならgetData()メソッドのパラメータはList<?>型なので、name,age,numberはすべてこのメソッドの実引数として使用できます。これがワイルドカードの役割です。

2、型ワイルドカードの上限は Listのような形で定義されます。このように定義すると、ワイルドカードのジェネリック値は Number およびその下位のサブクラス型のみを受け入れます。

例

import java.util.*; public class GenericTest { public static void main(String[] args) { List<String> name = new ArrayList<String>(); List<Integer> age = new ArrayList<Integer>(); List<Number> number = new ArrayList<Number>(); name.add("icon"); age.add(18); number.add(314); //getUperNumber(name);//1 getUperNumber(age);//2 getUperNumber(number);//3 } public static void getData(List<?> data) { System.out.println("data :" + data.get(0)); } public static void getUperNumber(List<? extends Number> data) { System.out.println("data :" + data.get(0)); } }

出力結果:

data :18
data :314

解説:に//1の箇所でエラーが発生します。なぜならgetUperNumber()メソッド内のパラメータはすでにパラメータのジェネリック上限をNumberに限定しているため、ジェネリックがStringはこの範囲内にないため、エラーになります。

3、型ワイルドカードの下限はList<? super Number>のような形で定義され、型はNumberおよびその上位の親クラス型のみを受け入れることを表します。例えばObject型のインスタンスです。

その他の拡張