Javaオブジェクト向けシリーズ[v 1.0.0][汎用ステップ]


汎用とは、クラス、インタフェース、メソッドを定義する際にタイプパラメータを使用することを許可することであり、このタイプパラメータ(または汎用と呼ばれる)は、変数の宣言、オブジェクトの作成、メソッドの呼び出し時に動的に指定(すなわち、実際のタイプパラメータをタイプ実パラメータと呼ぶこともできる)Java 5が集合フレームワーク内のすべてのインタフェースとクラスを書き換え、これらのインタフェース、クラスに汎用サポートを追加し、これにより、集合変数を宣言したり、集合オブジェクトを作成したりするときに、タイプの実パラメータを入力できます.Java 5書き換え後のListインタフェース、Iteratorインタフェース、Mapのコードクリップ:
//               ,     E
public interface List<E>
{
	//     ,E       
	//         E      
	void add(E x);
	Iterator<E> iterator();
	...
}
//               ,     E
public interface Iterator<E>
{
	//      E          
	E next();
	boolean hasNext();
	...
}
//               ,      K、V
public interface Map<K, V>
{
	//      K、V          
	Set<K> keySet();
	V put(K key, V value);
	...
}

3つのインタフェース宣言は比較的簡単で、カッコの中の内容を除いて、これは汎用的な実質です:インタフェース、クラスを定義する時に汎用的なパラメータを宣言することを許可して、汎用的なパラメータは全体のインタフェース、クラスの体内でタイプとして使用することができます.このような汎用パラメータは、通常のタイプを使用できるほとんどの場所で使用できます.それ以外に、Iterator iterator(); Set keySet();メソッドは、戻り値タイプがIterator、Setであることを宣言します.これは、Interator、Setとは異なるデータ型であり、彼らのサブクラスとして見ることができることを示しています.例えば、Listタイプを使用する場合、E形パラメータがStringタイプの実パラメータに入力されると、新しいタイプ:Listタイプが生成され、ListがEがStringに全て置き換えられた特殊なListセルフインタフェースとして想像できる
// List       
public interface ListString extends List
{
	//    E      String    
	void add(String x);
	Iterator<String> iterator();
	...
}

プログラムは1つのListインタフェースしか定義していないが、実際に使用すると無数のListインタフェースを生成することができ、Eのために異なるタイプの実パラメータを入力すれば、システムは複数の新しいListサブインタフェースに汎用宣言のタイプを含み、変数を定義し、オブジェクトを作成する際に1つのタイプの実パラメータを入力することができ、無数の複数の論理上のサブクラスを動的に生成することができる.しかし、この種は物理的に存在しません.
汎用クラス、インタフェースの作成
任意のクラス、インタフェースに汎用宣言を追加できます.
import static java.lang.System.*;

//   Apple         
public class Apple<T>
{
	//   T        
	private T info;
	public Apple(){}
	//        T        
	public Apple(T info)
	{
		this.info = info;
	}
	public void setInfo(T info)
	{
		this.info = info;
	}
	public T getInfo()
	{
		return this.info;
	}
	public static void main(String[] args)
	{
		//     T    String,          String
		Apple<String> a1 = new Apple<>("  ");
		out.println(a1.getInfo());
		//     T    Double,          Double double
		Apple<Double> a2 = new Apple<>(5.67);
		out.println(a2.getInfo());
	}
}

コードには汎用宣言付きのAppleクラスが定義されており、この汎用パラメータが実用的な意味を持つかどうかにかかわらず、Appleクラスを使用するとTパラメータに実用的なタイプを伝えることができ、Apple、Apple...のような形式の複数の論理サブクラスを生成することができ、物理的には存在しない.汎用宣言付きのカスタムクラスを作成し、そのクラスのコンストラクタを定義する場合、コンストラクタ名は元のクラス名であり、汎用宣言を追加しないでください.例えば、Appleクラス定義コンストラクタではありません.そのコンストラクタ名は依然としてAppleであり、Appleではありません.このコンストラクタを呼び出すときはAppleの形式を使用することができます.もちろん、Tシェイプに実際のタイプパラメータを入力する必要があります.
汎型派生子類
汎用宣言付きインタフェース、親クラスを作成した後、そのインタフェースに実装クラスを作成したり、親クラスから子クラスを派遣したりすることができます.これらのインタフェース、親クラスを使用する場合、汎用パラメータを含めることはできません.エラーの例:
//    A  Apple ,Apple        
public class A extends Apple<T>{ }

クラス、インタフェース、メソッドを定義するときに汎用パラメータを宣言できます.クラス、インタフェース、メソッドを使用する場合は、汎用パラメータの実際のタイプを使用する必要があります.Appleクラスからサブクラスを派生したい場合は、次のことができます.
//   Apple   T    String  
public class A extends Apple<String>

メソッドを呼び出すときは、すべてのデータ・パラメータ値を入力する必要があります.呼び出しメソッドとは異なり、クラス、インタフェースを使用するときに、汎用パラメータではなく実際のタイプのパラメータを入力することもできます.次のコードも正しいです.
//   Apple  ,   T           
public class A extends Apple

このような用法は汎用型の形式を省略して元のタイプ(raw type)と呼ぶが,Appleクラスから子クラスを派遣すると,AppleクラスのTタイプを使用するすべての場所がStringタイプに置き換えられる,すなわちその子クラスがString getInfo()とvoid setInfo(String info)の2つの方法に継承される
public class A1 extends Apple<String>
{
	//           ,   
	//    Apple        
	public String getInfo()
	{
		return "  " + super.getInfo();
	}
	/*
	//         ,               
	public Object getInfo()
	{
		return "  ";
	}
	*/
}

Appleクラスを使用しているときに実際のタイプ(つまり元のタイプを使用)が入力されていない場合、Javaコンパイラは、チェックされていない操作や安全ではない操作が使用されていることを警告する可能性があります.この警告メッセージの詳細を表示したい場合は、マイクロjavacコマンドで-Xlint:uncheckedオプションを追加することで実現できます.実際のタイプが入力されていない場合は、AppleクラスのTパラメータをObjectタイプとして処理します.
public class A2 extends Apple
{
	//        
	public String getInfo()
	{
		// super.getInfo()      Object  ,
		//    toString()   String  
		return super.getInfo().toString();
	}
}

汎用宣言付きインタフェースを作成する実装クラスは、これとほぼ同じです.
プロファイルクラス
ArrayListクラスはArrayListのサブクラスとして扱うことができるが、実際にはArrayListクラスは、集合要素としてStringオブジェクトしか追加できない特殊なArrayListクラスであることは確かであるが、実際にはシステムがArrayListのために新しいclassファイルを生成しておらず、ArrayListを新しいクラスとして扱うこともない
//   List   List  
List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
//   getClass()              
System.out.println(strList.getClass() == intList.getClass());

結果はtrueを返し、汎用の世紀型パラメータが何であるかにかかわらず、実行時に常に同じクラス(class)があり、メモリにもメモリ空間が1つしかないため、静的メソッド、静的初期化ブロック、静的変数の宣言および初期化では汎用パラメータの使用は許可されません.
public class R<T>
{
	//       ,                
	//	static T info;
	T age;
	public void foo(T msg){}
	//       ,                
	//	public static void bar(T msg){}

}

システムが本格的に汎用クラスを生成しないため、すべてのinstanceof演算子の後で汎用クラスを使用することはできません.以下はエラーの例です.
java.util.Collection<String> cs - new java.util.ArrayList<>();
//           :instanceof          
if (cs instanceof java.util.ArrayList<String>{...})