Java-generics-quick-guide

提供:Dev Guides
移動先:案内検索

Javaジェネリック-概要

整数配列、文字列配列、または順序付けをサポートする任意のタイプの配列内の要素を並べ替えることができる単一の並べ替えメソッドを作成できれば便利です。

Javaジェネリックメソッドとジェネリッククラスを使用すると、プログラマは、単一のメソッド宣言で関連メソッドのセットを、単一のクラス宣言で関連タイプのセットをそれぞれ指定できます。

ジェネリックはまた、プログラマがコンパイル時に無効な型をキャッチできるコンパイル時の型安全性も提供します。

Javaジェネリックコンセプトを使用して、オブジェクトの配列を並べ替えるためのジェネリックメソッドを記述し、整数配列、ダブル配列、ストリング配列などでジェネリックメソッドを呼び出して、配列要素を並べ替えます。

Java Generics-環境設定

ローカル環境のセットアップ

JUnitはJavaのフレームワークなので、最初の要件はJDKをマシンにインストールすることです。

システム要件

JDK 1.5 or above.
Memory No minimum requirement.
Disk Space No minimum requirement.
Operating System No minimum requirement.

ステップ1:マシンでのJavaインストールの検証

まず、コンソールを開き、作業しているオペレーティングシステムに基づいてjavaコマンドを実行します。

OS Task Command
Windows Open Command Console c:\> java -version
Linux Open Command Terminal $ java -version
Mac Open Terminal machine:< joseph$ java -version

すべてのオペレーティングシステムの出力を確認しましょう-

OS Output
Windows

java version "1.6.0_21"

Java(TM)SEランタイム環境(ビルド1.6.0_21-b07)

Java HotSpot(TM)Client VM(ビルド17.0-b17、混合モード、共有)

Linux

java version "1.6.0_21"

Java(TM)SEランタイム環境(ビルド1.6.0_21-b07)

Java HotSpot(TM)Client VM(ビルド17.0-b17、混合モード、共有)

Mac

java version "1.6.0_21"

Java(TM)SEランタイム環境(ビルド1.6.0_21-b07)

Java HotSpot(TM)64ビットサーバーVM(ビルド17.0-b17、混合モード、共有)

システムにJavaがインストールされていない場合は、次のリンクからJavaソフトウェア開発キット(SDK)をダウンロードしてください。https://www.oracle.com/technetwork/java/javase/downloads/indexl [https://www .oracle.com]。 このチュートリアルのインストールバージョンとしてJava 1.6.0_21を想定しています。

ステップ2:JAVA環境を設定する

*JAVA_HOME* 環境変数を設定して、Javaがマシンにインストールされているベースディレクトリの場所を指すようにします。 例えば。
OS Output
Windows Set the environment variable JAVA_HOME to C:\Program Files\Java\jdk1.6.0_21
Linux export JAVA_HOME =/usr/local/java-current
Mac export JAVA_HOME =/Library/Java/Home

Javaコンパイラの場所をシステムパスに追加します。

OS Output
Windows Append the string C:\Program Files\Java\jdk1.6.0_21\bin *at the end of the system variable, Path*.
Linux export PATH = $PATH:$JAVA_HOME/bin/
Mac not required

上記の説明に従って、コマンド java -version を使用してJavaのインストールを確認します。

Javaジェネリック-クラス

ジェネリッククラス宣言は、クラス名の後に型パラメーターセクションが続くことを除いて、非ジェネリッククラス宣言のように見えます。

ジェネリッククラスの型パラメーターセクションには、コンマで区切られた1つ以上の型パラメーターを含めることができます。 これらのクラスは、1つ以上のパラメーターを受け入れるため、パラメーター化クラスまたはパラメーター化タイプと呼ばれます。

構文

public class Box<T> {
   private T t;
}

どこで

  • Box -Boxはジェネリッククラスです。
  • T -ジェネリッククラスに渡されるジェネリック型パラメーター。 任意のオブジェクトを取ることができます。
  • t -ジェネリック型Tのインスタンス。

説明

Tは、ジェネリッククラスBoxに渡される型パラメーターであり、Boxオブジェクトの作成時に渡す必要があります。

任意のエディターを使用して、次のJavaプログラムを作成します。

GenericsTester.java

package com.finddevguides;

public class GenericsTester {
   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("Hello World"));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }
}

これにより、次の結果が生成されます。

出力

Integer Value :10
String Value :Hello World

型パラメーターの命名規則

慣例により、型パラメーターは通常のクラス名またはインターフェイス名で簡単に区別できるように、単一の大文字として名前が付けられます。 以下は、一般的に使用される型パラメータ名のリストです-

  • E -要素。主にJavaコレクションフレームワークで使用されます。
  • K -キー。主にマップのキーのパラメータータイプを表すために使用されます。
  • V -値。主にマップの値のパラメータータイプを表すために使用されます。
  • N -数値。主に数値を表すために使用されます。
  • T -タイプ。主に最初のジェネリックタイプパラメータを表すために使用されます。
  • S -タイプ。主に2番目の汎用タイプパラメータを表すために使用されます。
  • U -タイプ。主に3番目の汎用タイプパラメータを表すために使用されます。
  • V -タイプ。主に4番目のジェネリックタイプパラメーターを表すために使用されます。

次の例では、上記の概念を紹介します。

任意のエディターを使用して、次のJavaプログラムを作成します。

GenericsTester.java

package com.finddevguides;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer, String> box = new Box<Integer, String>();
      box.add(Integer.valueOf(10),"Hello World");
      System.out.printf("Integer Value :%d\n", box.getFirst());
      System.out.printf("String Value :%s\n", box.getSecond());

      Pair<String, Integer> pair = new Pair<String, Integer>();
      pair.addKeyValue("1", Integer.valueOf(10));
      System.out.printf("(Pair)Integer Value :%d\n", pair.getValue("1"));

      CustomList<Box> list = new CustomList<Box>();
      list.addItem(box);
      System.out.printf("(CustomList)Integer Value :%d\n", list.getItem(0).getFirst());
   }
}

class Box<T, S> {
   private T t;
   private S s;

   public void add(T t, S s) {
      this.t = t;
      this.s = s;
   }

   public T getFirst() {
      return t;
   }

   public S getSecond() {
      return s;
   }
}

class Pair<K,V>{
   private Map<K,V> map = new HashMap<K,V>();

   public void addKeyValue(K key, V value) {
      map.put(key, value);
   }

   public V getValue(K key) {
      return map.get(key);
   }
}

class CustomList<E>{
   private List<E> list = new ArrayList<E>();

   public void addItem(E value) {
      list.add(value);
   }

   public E getItem(int index) {
      return list.get(index);
   }
}

これにより、次の結果が生成されます。

出力

Integer Value :10
String Value :Hello World
(Pair)Integer Value :10
(CustomList)Integer Value :10

Javaジェネリック-型推論

型の推論は、メソッド呼び出しとその対応する宣言を調べて型引数をチェックおよび決定するJavaコンパイラの機能を表します。 推論アルゴリズムは引数の型をチェックし、可能な場合は、割り当てられた型が返されます。 推論アルゴリズムは、すべての型パラメーターをフルフィルできる特定の型を見つけようとします。

コンパイラは、未確認の変換警告を生成します。この場合、型推論は使用されません。

構文

Box<Integer> integerBox = new Box<>();

どこで

  • Box -Boxはジェネリッククラスです。
  • <> -ダイアモンド演算子は型推論を示します。

説明

コンパイラーは、ダイヤモンド演算子を使用して、パラメーターのタイプを決定します。 この演算子は、Java SE 7バージョン以降で利用可能です。

任意のエディターを使用して、次のJavaプログラムを作成します。

GenericsTester.java

package com.finddevguides;

public class GenericsTester {
   public static void main(String[] args) {
     //type inference
      Box<Integer> integerBox = new Box<>();
     //unchecked conversion warning
      Box<String> stringBox = new Box<String>();

      integerBox.add(new Integer(10));
      stringBox.add(new String("Hello World"));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }
}

これにより、次の結果が生成されます。

出力

Integer Value :10
String Value :Hello World

Javaジェネリック-メソッド

異なる型の引数で呼び出すことができる単一のジェネリックメソッド宣言を作成できます。 ジェネリックメソッドに渡される引数のタイプに基づいて、コンパイラは各メソッド呼び出しを適切に処理します。 以下は、ジェネリックメソッドを定義するルールです-

  • すべてのジェネリックメソッド宣言には、メソッドの戻り型(次の例では<E>)の前に山括弧(<および>)で区切られた型パラメーターセクションがあります。
  • 各型パラメーターセクションには、カンマで区切られた1つ以上の型パラメーターが含まれています。 型パラメーターとも呼ばれる型変数は、一般的な型名を指定する識別子です。
  • 型パラメーターを使用して戻り型を宣言し、ジェネリックメソッドに渡される引数の型のプレースホルダーとして機能することができます。これは実際の型引数として知られています。
  • ジェネリックメソッドの本体は、他のメソッドの本体と同様に宣言されます。 型パラメーターは参照型のみを表すことができ、プリミティブ型(int、double、charなど)は表すことができないことに注意してください。

次の例は、単一のGenericメソッドを使用して異なるタイプの配列を印刷する方法を示しています-

public class GenericMethodTest {
  //generic method printArray
   public static < E > void printArray( E[] inputArray ) {
     //Display array elements
      for(E element : inputArray) {
         System.out.printf("%s ", element);
      }
      System.out.println();
   }

   public static void main(String args[]) {
     //Create arrays of Integer, Double and 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);  //pass an Integer array

      System.out.println("\nArray doubleArray contains:");
      printArray(doubleArray);  //pass a Double array

      System.out.println("\nArray characterArray contains:");
      printArray(charArray);  //pass a Character array
   }
}

これは、次の結果を生成します-

出力

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

Javaジェネリック-複数の型パラメーター

Genericクラスには、複数のタイプのパラメーターを含めることができます。 次の例では、上記の概念を紹介します。

任意のエディターを使用して、次のJavaプログラムを作成します。

GenericsTester.java

package com.finddevguides;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer, String> box = new Box<Integer, String>();
      box.add(Integer.valueOf(10),"Hello World");
      System.out.printf("Integer Value :%d\n", box.getFirst());
      System.out.printf("String Value :%s\n", box.getSecond());

      Box<String, String> box1 = new Box<String, String>();
      box1.add("Message","Hello World");
      System.out.printf("String Value :%s\n", box1.getFirst());
      System.out.printf("String Value :%s\n", box1.getSecond());
   }
}

class Box<T, S> {
   private T t;
   private S s;

   public void add(T t, S s) {
      this.t = t;
      this.s = s;
   }

   public T getFirst() {
      return t;
   }

   public S getSecond() {
      return s;
   }
}

これにより、次の結果が生成されます。

出力

Integer Value :10
String Value :Hello World
String Value :Message
String Value :Hello World

Javaジェネリック-パラメーター化された型

ジェネリッククラスは、型パラメーターをパラメーター化された型に置き換えることができるパラメーター化された型を持つことができます。 次の例では、上記の概念を紹介します。

任意のエディターを使用して、次のJavaプログラムを作成します。

GenericsTester.java

package com.finddevguides;

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


public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer, List<String>> box
         = new Box<Integer, List<String>>();

      List<String> messages = new ArrayList<String>();

      messages.add("Hi");
      messages.add("Hello");
      messages.add("Bye");

      box.add(Integer.valueOf(10),messages);
      System.out.printf("Integer Value :%d\n", box.getFirst());
      System.out.printf("String Value :%s\n", box.getSecond());


   }
}

class Box<T, S> {
   private T t;
   private S s;

   public void add(T t, S s) {
      this.t = t;
      this.s = s;
   }

   public T getFirst() {
      return t;
   }

   public S getSecond() {
      return s;
   }
}

これにより、次の結果が生成されます。

出力

Integer Value :10
String Value :[Hi, Hello, Bye]

Javaジェネリック-生の型

生の型は、その型引数が作成中に渡されない場合、ジェネリッククラスまたはインターフェイスのオブジェクトです。 次の例では、上記の概念を紹介します。

任意のエディターを使用して、次のJavaプログラムを作成します。

GenericsTester.java

package com.finddevguides;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> box = new Box<Integer>();

      box.set(Integer.valueOf(10));
      System.out.printf("Integer Value :%d\n", box.getData());


      Box rawBox = new Box();

     //No warning
      rawBox = box;
      System.out.printf("Integer Value :%d\n", rawBox.getData());

     //Warning for unchecked invocation to set(T)
      rawBox.set(Integer.valueOf(10));
      System.out.printf("Integer Value :%d\n", rawBox.getData());

     //Warning for unchecked conversion
      box = rawBox;
      System.out.printf("Integer Value :%d\n", box.getData());
   }
}

class Box<T> {
   private T t;

   public void set(T t) {
      this.t = t;
   }

   public T getData() {
      return t;
   }
}

これにより、次の結果が生成されます。

出力

Integer Value :10
Integer Value :10
Integer Value :10
Integer Value :10

Java Generics-制限された型パラメーター

型パラメータに渡すことができる型の種類を制限したい場合があるかもしれません。 たとえば、数値を操作するメソッドは、Numberまたはそのサブクラスのインスタンスのみを受け入れたい場合があります。 これが境界型パラメータの目的です。

制限された型パラメーターを宣言するには、型パラメーターの名前をリストし、その後にextendsキーワードとその上限をリストします。

次の例は、extends(クラスなど)または「implements」(インターフェイスなど)を意味する一般的な意味でのextendの使用方法を示しています。 この例は、3つのComparableオブジェクトのうち最大のものを返す汎用メソッドです-

public class MaximumTest {
  //determines the largest of three Comparable objects

   public static <T extends Comparable<T>> T maximum(T x, T y, T z) {
      T max = x;  //assume x is initially the largest

      if(y.compareTo(max) > 0) {
         max = y;  //y is the largest so far
      }

      if(z.compareTo(max) > 0) {
         max = z;  //z is the largest now
      }
      return max;  //returns the largest object
   }

   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

Javaジェネリック-複数の境界

型パラメーターには複数の境界を設定できます。

構文

public static <T extends Number & Comparable<T>> T maximum(T x, T y, T z)

どこで

  • maximum -最大値は一般的な方法です。
  • T -ジェネリックメソッドに渡されるジェネリック型パラメーター。 任意のオブジェクトを取ることができます。

説明

Tは、ジェネリッククラスBoxに渡される型パラメーターであり、Numberクラスのサブタイプである必要があり、Comparableインターフェイスを実装する必要があります。 クラスがバインドされて渡される場合、インターフェイスの前に最初に渡される必要があります。そうでないと、コンパイル時エラーが発生します。

任意のエディターを使用して、次のJavaプログラムを作成します。

package com.finddevguides;

public class GenericsTester {
   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 ));
   }

   public static <T extends Number
      & Comparable<T>> T maximum(T x, T y, T z) {
      T max = x;
      if(y.compareTo(max) > 0) {
         max = y;
      }

      if(z.compareTo(max) > 0) {
         max = z;
      }
      return max;
   }

  //Compiler throws error in case of below declaration
  /* public static <T extends Comparable<T>
      & Number> T maximum1(T x, T y, T z) {
      T max = x;
      if(y.compareTo(max) > 0) {
         max = y;
      }

      if(z.compareTo(max) > 0) {
         max = z;
      }
      return max;
   }*/
}

これは、次の結果を生成します-

出力

Max of 3, 4 and 5 is 5

Max of 6.6,8.8 and 7.7 is 8.8

Javaジェネリック-リスト

Javaは、Listインターフェイスで一般的なサポートを提供しています。

構文

List<T> list = new ArrayList<T>();

どこで

  • list -リストインターフェイスのオブジェクト。
  • T -リスト宣言中に渡されるジェネリック型パラメーター。

説明

Tは、ジェネリックインターフェイスListおよびその実装クラスArrayListに渡される型パラメーターです。

任意のエディターを使用して、次のJavaプログラムを作成します。

package com.finddevguides;

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

public class GenericsTester {
   public static void main(String[] args) {

      List<Integer> integerList = new ArrayList<Integer>();

      integerList.add(Integer.valueOf(10));
      integerList.add(Integer.valueOf(11));

      List<String> stringList = new ArrayList<String>();

      stringList.add("Hello World");
      stringList.add("Hi World");


      System.out.printf("Integer Value :%d\n", integerList.get(0));
      System.out.printf("String Value :%s\n", stringList.get(0));

      for(Integer data: integerList) {
         System.out.printf("Integer Value :%d\n", data);
      }

      Iterator<String> stringIterator = stringList.iterator();

      while(stringIterator.hasNext()) {
         System.out.printf("String Value :%s\n", stringIterator.next());
      }
   }
}

これは、次の結果を生成します-

出力

Integer Value :10
String Value :Hello World
Integer Value :10
Integer Value :11
String Value :Hello World
String Value :Hi World

Javaジェネリック-セット

JavaはSetインターフェースで一般的なサポートを提供しています。

構文

Set<T> set = new HashSet<T>();

どこで

  • set -Set Interfaceのオブジェクト。
  • T -セット宣言中に渡されるジェネリック型パラメーター。

説明

Tは、ジェネリックインターフェイスSetおよびその実装クラスHashSetに渡される型パラメーターです。

任意のエディターを使用して、次のJavaプログラムを作成します。

package com.finddevguides;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class GenericsTester {
   public static void main(String[] args) {

      Set<Integer> integerSet = new HashSet<Integer>();

      integerSet.add(Integer.valueOf(10));
      integerSet.add(Integer.valueOf(11));

      Set<String> stringSet = new HashSet<String>();

      stringSet.add("Hello World");
      stringSet.add("Hi World");


      for(Integer data: integerSet) {
         System.out.printf("Integer Value :%d\n", data);
      }

      Iterator<String> stringIterator = stringSet.iterator();

      while(stringIterator.hasNext()) {
         System.out.printf("String Value :%s\n", stringIterator.next());
      }
   }
}

これは、次の結果を生成します-

出力

Integer Value :10
Integer Value :11
String Value :Hello World
String Value :Hi World

Java Generics-マップ

Javaは、Mapインターフェイスで一般的なサポートを提供しています。

構文

Set<T> set = new HashSet<T>();

どこで

  • set -Set Interfaceのオブジェクト。
  • T -セット宣言中に渡されるジェネリック型パラメーター。

説明

Tは、ジェネリックインターフェイスSetおよびその実装クラスHashSetに渡される型パラメーターです。

任意のエディターを使用して、次のJavaプログラムを作成します。

package com.finddevguides;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class GenericsTester {
   public static void main(String[] args) {

      Map<Integer,Integer> integerMap
         = new HashMap<Integer,Integer>();

      integerMap.put(1, 10);
      integerMap.put(2, 11);

      Map<String,String> stringMap = new HashMap<String,String>();

      stringMap.put("1", "Hello World");
      stringMap.put("2","Hi World");


      System.out.printf("Integer Value :%d\n", integerMap.get(1));
      System.out.printf("String Value :%s\n", stringMap.get("1"));

     //iterate keys.
      Iterator<Integer> integerIterator   = integerMap.keySet().iterator();

      while(integerIterator.hasNext()) {
         System.out.printf("Integer Value :%d\n", integerIterator.next());
      }

     //iterate values.
      Iterator<String> stringIterator   = stringMap.values().iterator();

      while(stringIterator.hasNext()) {
         System.out.printf("String Value :%s\n", stringIterator.next());
      }
   }
}

これは、次の結果を生成します-

出力

Integer Value :10
String Value :Hello World
Integer Value :1
Integer Value :2
String Value :Hello World
String Value :Hi World

Javaジェネリック-上限のあるワイルドカード

疑問符(?)はワイルドカードを表し、ジェネリックの不明なタイプを表します。 型パラメータに渡すことができる型の種類を制限したい場合があるかもしれません。 たとえば、数値を操作するメソッドは、Numberまたはそのサブクラスのインスタンスのみを受け入れたい場合があります。

上限のあるワイルドカードパラメーターを宣言するには、?をリストし、extendsキーワードを続けて、その上限を続けます。

次の例は、extendsを使用して上限のワイルドカードを指定する方法を示しています。

package com.finddevguides;

import java.util.Arrays;
import java.util.List;

public class GenericsTester {

   public static double sum(List<? extends Number> numberlist) {
      double sum = 0.0;
      for (Number n : numberlist) sum += n.doubleValue();
      return sum;
   }

   public static void main(String args[]) {
      List<Integer> integerList = Arrays.asList(1, 2, 3);
      System.out.println("sum = " + sum(integerList));

      List<Double> doubleList = Arrays.asList(1.2, 2.3, 3.5);
      System.out.println("sum = " + sum(doubleList));
   }
}

これは、次の結果を生成します-

出力

sum = 6.0
sum = 7.0

Java Generics-無制限のワイルドカード

疑問符(?)はワイルドカードを表し、ジェネリックの不明なタイプを表します。 Objectクラスで提供される機能を使用してメソッドを実装できる場合、またはコードがtypeパラメーターから独立している場合、オブジェクトを使用できる場合があります。

Unbounded Wildcardパラメーターを宣言するには、? のみ。

次の例は、extendedを使用して無制限のワイルドカードを指定する方法を示しています。

package com.finddevguides;

import java.util.Arrays;
import java.util.List;

public class GenericsTester {
   public static void printAll(List<?> list) {
      for (Object item : list)
         System.out.println(item + " ");
   }

   public static void main(String args[]) {
      List<Integer> integerList = Arrays.asList(1, 2, 3);
      printAll(integerList);
      List<Double> doubleList = Arrays.asList(1.2, 2.3, 3.5);
      printAll(doubleList);
   }
}

これは、次の結果を生成します-

出力

1
2
3
1.2
2.3
3.5

Javaジェネリック-下限のワイルドカード

疑問符(?)はワイルドカードを表し、ジェネリックの不明なタイプを表します。 型パラメータに渡すことができる型の種類を制限したい場合があるかもしれません。 たとえば、数値を操作するメソッドは、IntegerまたはNumberのようなスーパークラスのインスタンスのみを受け入れたい場合があります。

下限のワイルドカードパラメータを宣言するには、?をリストし、その後にsuperキーワード、その下限を続けます。

次の例は、superを使用して下限のワイルドカードを指定する方法を示しています。

package com.finddevguides;

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

public class GenericsTester {

   public static void addCat(List<? super Cat> catList) {
      catList.add(new RedCat());
      System.out.println("Cat Added");
   }

   public static void main(String[] args) {
      List<Animal> animalList= new ArrayList<Animal>();
      List<Cat> catList= new ArrayList<Cat>();
      List<RedCat> redCatList= new ArrayList<RedCat>();
      List<Dog> dogList= new ArrayList<Dog>();

     //add list of super class Animal of Cat class
      addCat(animalList);

     //add list of Cat class
      addCat(catList);

     //compile time error
     //can not add list of subclass RedCat of Cat class
     //addCat(redCatList);

     //compile time error
     //can not add list of subclass Dog of Superclass Animal of Cat class
     //addCat.addMethod(dogList);
   }
}
class Animal {}

class Cat extends Animal {}

class RedCat extends Cat {}

class Dog extends Animal {}

これは、次の結果を生成します-

Cat Added
Cat Added

Java Generics-ワイルドカード使用のガイドライン

ワイルドカードは3つの方法で使用できます-

  • 上限ワイルドカード-? Typeを拡張します。
  • 下限ワイルドカード-? スーパータイプ。
  • 無制限のワイルドカード-?

条件に最適なワイルドカードのタイプを決定するために、最初にメソッドに渡されるパラメーターのタイプを in および out パラメーターとして分類しましょう。

  • in variable -in変数はコードにデータを提供します。 たとえば、copy(src、dest)。 ここで、srcはコピーされるデータである変数のように機能します。
  • * out変数*-out変数は、コードによって更新されたデータを保持します。 たとえば、copy(src、dest)。 ここで、destはコピーされたデータを持つ変数のように機能します。

ワイルドカードのガイドライン。

  • 上限ワイルドカード-変数が in カテゴリの場合、ワイルドカードを使用してextendsキーワードを使用します。
  • 下限ワイルドカード-変数が out カテゴリの場合、ワイルドカード付きのスーパーキーワードを使用します。
  • 非バインドワイルドカード-オブジェクトクラスメソッドを使用して変数にアクセスできる場合は、非バインドワイルドカードを使用します。
  • ワイルドカードなし-コードが in および out カテゴリの両方の変数にアクセスしている場合、ワイルドカードを使用しないでください。

次の例は、上記の概念を示しています。

package com.finddevguides;

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

public class GenericsTester {

  //Upper bound wildcard
  //in category
   public static void deleteCat(List<? extends Cat> catList, Cat cat) {
      catList.remove(cat);
      System.out.println("Cat Removed");
   }

  //Lower bound wildcard
  //out category
   public static void addCat(List<? super RedCat> catList) {
      catList.add(new RedCat("Red Cat"));
      System.out.println("Cat Added");
   }

  //Unbounded wildcard
  //Using Object method toString()
   public static void printAll(List<?> list) {
      for (Object item : list)
         System.out.println(item + " ");
   }

   public static void main(String[] args) {

      List<Animal> animalList= new ArrayList<Animal>();
      List<RedCat> redCatList= new ArrayList<RedCat>();

     //add list of super class Animal of Cat class
      addCat(animalList);
     //add list of Cat class
      addCat(redCatList);
      addCat(redCatList);

     //print all animals
      printAll(animalList);
      printAll(redCatList);

      Cat cat = redCatList.get(0);
     //delete cat
      deleteCat(redCatList, cat);
      printAll(redCatList);
   }
}

class Animal {
   String name;
   Animal(String name) {
      this.name = name;
   }
   public String toString() {
      return name;
   }
}

class Cat extends Animal {
   Cat(String name) {
      super(name);
   }
}

class RedCat extends Cat {
   RedCat(String name) {
      super(name);
   }
}

class Dog extends Animal {
   Dog(String name) {
      super(name);
   }
}

これは、次の結果を生成します-

Cat Added
Cat Added
Cat Added
Red Cat
Red Cat
Red Cat
Cat Removed
Red Cat

Javaジェネリック-型消去

ジェネリックは、コンパイル時の厳密な型チェックに使用され、ジェネリックプログラミングを提供します。 一般的な動作を実装するために、Javaコンパイラは型消去を適用します。 型消去は、コンパイラがジェネリックパラメータを実際のクラスまたはブリッジメソッドに置き換えるプロセスです。 型消去では、コンパイラは余分なクラスが作成されず、実行時のオーバーヘッドがないことを保証します。

タイプ消去ルール

  • バインドされた型パラメーターが使用されている場合、ジェネリック型の型パラメーターをバインドされた型に置き換えます。
  • 制限のない型パラメーターが使用されている場合、ジェネリック型の型パラメーターをオブジェクトに置き換えます。
  • 型キャストを挿入して、型の安全性を維持します。
  • ブリッジメソッドを生成して、拡張ジェネリック型のポリモーフィズムを維持します。

Javaジェネリック-バインドされた型の消去

Javaコンパイラは、バウンド型パラメーターが使用されている場合、ジェネリック型の型パラメーターをバウンドに置き換えます。

package com.finddevguides;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<Double> doubleBox = new Box<Double>();

      integerBox.add(new Integer(10));
      doubleBox.add(new Double(10.0));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("Double Value :%s\n", doubleBox.get());
   }
}

class Box<T extends Number> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }
}

この場合、JavaコンパイラはTをNumberクラスに置き換え、型を消去した後、コンパイラは次のコードのバイトコードを生成します。

package com.finddevguides;

public class GenericsTester {
   public static void main(String[] args) {
      Box integerBox = new Box();
      Box doubleBox = new Box();

      integerBox.add(new Integer(10));
      doubleBox.add(new Double(10.0));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("Double Value :%s\n", doubleBox.get());
   }
}

class Box {
   private Number t;

   public void add(Number t) {
      this.t = t;
   }

   public Number get() {
      return t;
   }
}

どちらの場合でも、結果は同じです-

出力

Integer Value :10
Double Value :10.0

Javaジェネリック-制限のない型の消去

Javaコンパイラは、無制限の型パラメーターが使用されている場合、ジェネリック型の型パラメーターをObjectに置き換えます。

package com.finddevguides;

public class GenericsTester {
   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("Hello World"));

      System.out.printf("Integer Value :%d\n", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }
}

この場合、JavaコンパイラはTをObjectクラスに置き換え、型を消去した後、コンパイラは次のコードのバイトコードを生成します。

package com.finddevguides;

public class GenericsTester {
   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", integerBox.get());
      System.out.printf("String Value :%s\n", stringBox.get());
   }
}

class Box {
   private Object t;

   public void add(Object t) {
      this.t = t;
   }

   public Object get() {
      return t;
   }
}

どちらの場合でも、結果は同じです-

出力

Integer Value :10
String Value :Hello World

Javaジェネリック-ジェネリックメソッドの消去

Javaコンパイラは、無制限の型パラメーターが使用されている場合はジェネリック型の型パラメーターをObjectに、メソッドパラメーターとして使用されている場合は型に置き換えます。

package com.finddevguides;

public class GenericsTester {
   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("Hello World"));

      printBox(integerBox);
      printBox1(stringBox);
   }

   private static <T extends Box> void printBox(T box) {
      System.out.println("Integer Value :" + box.get());
   }

   private static <T> void printBox1(T box) {
      System.out.println("String Value :" + ((Box)box).get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }
}

この場合、JavaコンパイラはTをObjectクラスに置き換え、型を消去した後、コンパイラは次のコードのバイトコードを生成します。

package com.finddevguides;

public class GenericsTester {
   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"));

      printBox(integerBox);
      printBox1(stringBox);
   }

  //Bounded Types Erasure
   private static void printBox(Box box) {
      System.out.println("Integer Value :" + box.get());
   }

  //Unbounded Types Erasure
   private static void printBox1(Object box) {
      System.out.println("String Value :" + ((Box)box).get());
   }
}

class Box {
   private Object t;

   public void add(Object t) {
      this.t = t;
   }

   public Object get() {
      return t;
   }
}

どちらの場合でも、結果は同じです-

出力

Integer Value :10
String Value :Hello World

Javaジェネリック-プリミティブ型なし

ジェネリックを使用すると、プリミティブ型を型パラメーターとして渡すことはできません。 以下の例では、ボックスクラスにintプリミティブ型を渡すと、コンパイラーは文句を言います。 同じことを緩和するには、intプリミティブ型の代わりにIntegerオブジェクトを渡す必要があります。

package com.finddevguides;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();

     //compiler errror
     //ReferenceType
     //- Syntax error, insert "Dimensions" to complete
      ReferenceType
     //Box<int> stringBox = new Box<int>();

      integerBox.add(new Integer(10));
      printBox(integerBox);
   }

   private static void printBox(Box box) {
      System.out.println("Value: " + box.get());
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }
}

これは、次の結果を生成します-

出力

Value: 10

Java Generics-インスタンスなし

型パラメーターを使用して、メソッド内でオブジェクトをインスタンス化することはできません。

public static <T> void add(Box<T> box) {
  //compiler error
  //Cannot instantiate the type T
  //T item = new T();
  //box.add(item);
}

このような機能を実現するには、リフレクションを使用します。

public static <T> void add(Box<T> box, Class<T> clazz)
   throws InstantiationException, IllegalAccessException{
   T item = clazz.newInstance();  //OK
   box.add(item);
   System.out.println("Item added.");
}

package com.finddevguides;

public class GenericsTester {
   public static void main(String[] args)
      throws InstantiationException, IllegalAccessException {
      Box<String> stringBox = new Box<String>();
      add(stringBox, String.class);
   }

   public static <T> void add(Box<T> box) {
     //compiler error
     //Cannot instantiate the type T
     //T item = new T();
     //box.add(item);
   }

   public static <T> void add(Box<T> box, Class<T> clazz)
      throws InstantiationException, IllegalAccessException{
      T item = clazz.newInstance();  //OK
      box.add(item);
      System.out.println("Item added.");
   }
}

class Box<T> {
   private T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }
}

これは、次の結果を生成します-

Item added.

Java Generics-静的フィールドなし

ジェネリックを使用すると、型パラメーターを静的にすることはできません。 静的変数はオブジェクト間で共有されるため、コンパイラは使用する型を決定できません。 静的型パラメーターが許可されている場合は、次の例を検討してください。

package com.finddevguides;

public class GenericsTester {
   public static void main(String[] args) {
      Box<Integer> integerBox = new Box<Integer>();
      Box<String> stringBox = new Box<String>();

      integerBox.add(new Integer(10));
      printBox(integerBox);
   }

   private static void printBox(Box box) {
      System.out.println("Value: " + box.get());
   }
}

class Box<T> {
  //compiler error
   private static T t;

   public void add(T t) {
      this.t = t;
   }

   public T get() {
      return t;
   }
}

stringBoxとintegerBoxの両方にスター型の静的型変数があるため、その型を判別できません。 したがって、静的型パラメーターは許可されません。

Javaジェネリック-キャストなし

パラメータ化された型へのキャストは、無制限のワイルドカードによってパラメータ化されない限り許可されません。

Box<Integer> integerBox = new Box<Integer>();
Box<Number> numberBox = new Box<Number>();
//Compiler Error: Cannot cast from Box<Number> to Box<Integer>
integerBox = (Box<Integer>)numberBox;

同じことを実現するには、無制限のワイルドカードを使用できます。

private static void add(Box<?> box) {
   Box<Integer> integerBox = (Box<Integer>)box;
}

Javaジェネリック-instanceOfなし

コンパイラは型消去を使用するため、ランタイムは型パラメーターを追跡しないため、実行時にBox <Integer>とBox <String>の違いはinstanceOf演算子を使用して検証できません。

Box<Integer> integerBox = new Box<Integer>();

//Compiler Error:
//Cannot perform instanceof check against
//parameterized type Box<Integer>.
//Use the form Box<?> instead since further
//generic type information will be erased at runtime
if(integerBox instanceof Box<Integer>) { }

Javaジェネリック-配列なし

パラメーター化された型の配列は許可されていません。

//Cannot create a generic array of Box<Integer>
Box<Integer>[] arrayOfLists = new Box<Integer>[2];

コンパイラは型消去を使用するため、型パラメーターはObjectに置き換えられ、ユーザーは配列に任意の型のオブジェクトを追加できます。 また、実行時に、コードはArrayStoreExceptionをスローできません。

//compiler error, but if it is allowed
Object[] stringBoxes = new Box<String>[];

//OK
stringBoxes[0] = new Box<String>();

//An ArrayStoreException should be thrown,
//but the runtime can't detect it.
stringBoxes[1] = new Box<Integer>();

Java Generics-例外なし

ジェネリッククラスは、Throwableクラスを直接または間接的に拡張することはできません。

//The generic class Box<T> may not subclass java.lang.Throwable
class Box<T> extends Exception {}

//The generic class Box<T> may not subclass java.lang.Throwable
class Box1<T> extends Throwable {}

メソッドは、型パラメーターのインスタンスをキャッチできません。

public static <T extends Exception, J>
   void execute(List<J> jobs) {
      try {
         for (J job : jobs) {}

        //compile-time error
        //Cannot use the type parameter T in a catch block
      } catch (T e) {
        //...
   }
}

型パラメーターはthrows句で許可されます。

class Box<T extends Exception>  {
   private int t;

   public void add(int t) throws T {
      this.t = t;
   }

   public int get() {
      return t;
   }
}

Java Generics-オーバーロードなし

クラスは、型消去後に同じシグネチャを持つことができる2つのオーバーロードされたメソッドを持つことはできません。

class Box  {
  //Compiler error
  //Erasure of method print(List<String>)
  //is the same as another method in type Box
   public void print(List<String> stringList) { }
   public void print(List<Integer> integerList) { }
}