Java学習整理のJava汎用型

10512 ワード

一、一つの困惑
汎用型はよく見たり使用したりしますが、ここではまず自分の困惑を記録します.次の2つの汎用型の方法にはどんな違いがありますか.
public void dosomething(T t){
		
}
public <T> void dosomething(T t){
		
}

次の例を見ればわかります.
class Fruit
{
	public String toString()
	{
		return "Fruit";
	}
}

class Apple extends Fruit
{
	public String toString()
	{
		return "Apple";
	}
}

class Dog
{
	public String toString()
	{
		return "Dog";
	}
}

class Show<T>
{

	void show_1(T t)
	{
		System.out.println("show_1  " + t.toString());
	}

	<E> void show_2(E e)
	{
		System.out.println("show_2  " + e.toString());
	}

	<T> void show_3(T t)
	{
		System.out.println("show_3  " + t.toString());
	}

	public static void main(String[] args)
	{
		Show<Fruit> o = new Show<Fruit>();
		Fruit f = new Fruit();
		Apple a = new Apple();
		Dog d = new Dog();
		System.out.println("show_1   ________________________");
		o.show_1(f);
		o.show_1(a);
		/*
		* o.show_1(d);          ,        。   Show<T>         T Fruit,
		*      T   Fruit     ,       Person;
		*/
		System.out.println("show_2   ________________________");
		o.show_2(f);
		o.show_2(a);
		/*
		* o.show_2(d);   。     void    <E>,       <T>       ,     Fruit     ,       。
		*/
		o.show_2(d);
		System.out.println("show_3   ________________________");
		o.show_3(f);
		o.show_3(a);
		/*
		* o.show_3(d);   。     void    <T>,       <T>       ,     Fruit     ,       。
		*/
		o.show_3(d);
		/*  :       <T>(<E>,<R>      ,         ),                  ,                   ,
		           <T>(<E>,<R>      ,          )                 。*/
	}
}

二、いくつかの概念
とJAVA 5.0の新しいコンセプト.それらの外見のため多くの人がそれらの用途を誤解した:1.まずTに継承されたすべてのクラスの集合と誤解しがちですが、これは大間違いで、見たことがあるか、リストを使ったことがあると信じていますでしょ?どうして私が集合と理解したのは間違っているのですか?1つの集合として理解される場合、なぜリストで表さないのですか?だからは集合ではなく、Tのある種類の意味であり、覚えておくと一種、単一の一種、問題が来て、どちらも不確定で不確実性をもたらすためadd()で元素を加えることは不可能である.なぜadd(T)がだめだと思う?だってはTのある種類であり,子類を入れることができる容器は必ずしもスーパークラスに入れるとは限らず,つまりTを入れることは不可能である.自分の理解では、Tタイプのサブクラスが入ってくるため、コンパイラは実行時に何が入ってくるのか分からない.またJavaでは親が直接子クラスにコピーする参照は許されず、強制タイプ変換してもだめ(もちろん、AがBの親であればA a=new B()であり、この場合はB B B=(B)aでよい)、だからここでaddは何も追加できません.すべてのタイプを表すnullを除いて.次のコードはこの点を説明しています.
package com.lxq.generics;

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

class Fruit{  
}  
class Apple extends Fruit{  
}  
class RedApple extends Apple{  
}  
public class TestGenerics
{
	public static void main(String[] args){
		TestGenerics tg=new TestGenerics();
		tg.test1(new ArrayList<Fruit>());
		tg.test2(new ArrayList<RedApple>());
		
	}
	public  void test1(List<? super Apple> apples){
		/*      apples new ArrayList<Fruit>(),       
		apples.add(new Fruit());
		apples.add((Apple) new  Fruit());*/
		apples.add(new Apple());
		apples.add(new RedApple());
	}
	public  void test2(List<? extends Apple> apples){
		/*      apples new ArrayList<RedApple>(),       
		apples.add(new Apple());
		apples.add(new RedApple());
		apples.add(new Object());*/
		apples.add(null);

	}
}

2.ここは使いやすいですが、こんなに制限が多いのは、ここではTクラスを下限とするある種類、簡単に言えばTクラスのスーパークラスという意味です.しかし、なぜadd(T)ができるのでしょうか.ある種類の容器に入れることができるので、必ずそのサブクラス、多態の概念を入れることができます.
自分の理解:,では最後に入ってきたのはTの親やTであり,本例ではAppleクラスやAppleの親であるため,Fruitクラスを追加するとFruitクラスをAppleタイプに付与する参照(伝わるのはAppleタイプかもしれないよ)が現れる.
消去
汎用性の最も挑戦的な面は消去(erasure)かもしれないが、これはJava言語で汎用性を実現する最下位技術である.消去とは、コンパイラがクラスファイルを生成するときに、パラメトリッククラスの大量のタイプ情報を基本的に放棄することを意味します.コンパイラは、プログラマが汎用的に現れる前に手動で作ったように、その強制タイプ変換でコードを生成します.
違いは、コンパイラが汎用性がなければ検証されないタイプのセキュリティ制約を多数検証し始めたことです.消去による汎用的な意味の実現は重要であり,初見も混乱している.異なるタイプであるため、ListをListに割り当てることはできませんが、
しかし、ListとListタイプの変数は同じクラスです!この点を理解するには、次のコードを評価してください.
List<Number> l1=new ArrayList<Number>();
List<Integer> l2=new ArrayList<Integer>();
System.out.println(l1.getClass());
System.out.println(l2.getClass());
System.out.println(l1.getClass()==l2.getClass());

コンパイラはArrayListのクラスを1つだけ生成します.ArrayListのバイトコードが生成されると、そのタイプのパラメータのトレースはほとんど残りません.
汎用クラスのバイトコードが生成されると、コンパイラはタイプパラメータの消去でタイプパラメータを置き換えます.無制限タイプパラメータ()では、消去はObjectです.上限タイプパラメータ()では、その消去はその上限(この例ではComparable)の消去です.
複数の制限を持つタイプのパラメータについて、その最左の制限を持つ消去を使用します.生成されたバイトコードをチェックすると、ListとListのコードの違いは言えません.タイプ制限TはバイトコードにおいてTの上限に置き換えられ、この上限は一般的にObjectである.
 
マルチリミット
1つのタイプのパラメータには複数の制限があります.これは、ComparableとSerializableのようなタイプのパラメータをコンストレイントする場合に便利です.複数の制限の構文は、AND記号で制限を区切ります.
class C & Serializable>
ワイルドカード・タイプには、上限または下限の1つの制限があります.指定したタイプのパラメータには、1つ以上の上限があります.複数の制限を持つタイプのパラメータは、その各制限にアクセスする方法およびドメインに使用できます.
タイプパラメータとタイプ実パラメータ
パラメトリッククラスの定義では、CollectionのVなどのプレースホルダ名をタイプパラメータ(type parameter)と呼び、メソッド定義の形式パラメータに似ています.パラメトリッククラスの変数の宣言では、宣言で指定したタイプ値をタイプ実パラメータ(type argument)と呼び、メソッド呼び出しの実際のパラメータに似ています.しかし、実際には両者を通称「タイプパラメータ」と呼ぶことが多い.定義は次のとおりです.
interface Collection{...}と宣言:Collectioncs=new HashSet();
では、名前V(Collectionインタフェース全体に使用可能)をタイプパラメータと呼ぶ.csの宣言では、Stringの2回の使用はタイプ実パラメータ(Collectionに1回、HashSetに1回)である.
タイプパラメータがいつ使用できるかについては、いくつかの制限があります.ほとんどの場合、タイプパラメータは、実際のタイプ定義を使用できる任意の場所で使用できます.しかし、例外がある.オブジェクトまたは配列は作成できません.静的コンテキストまたは例外を処理するコンテキストでは使用できません.親タイプ(class Fooextends T)としては使用できません.instanceof式では使用できません.クラス定数としては使用できません.
同様に、タイプ実パラメータとして使用できるタイプについても、いくつかの制限がある.タイプインスタンスは、参照タイプ(基本タイプではありません)、ワイルドカード、タイプパラメータ、または他のパラメータ化タイプのインスタンス化でなければなりません.したがって、List(参照タイプ)、Listを定義できます.(ワイルドカード)またはList>(その他のパラメトリックタイプのインスタンス化).タイプパラメータTを持つパラメトリックタイプの定義では、List(タイプパラメータ)を宣言することもできます.
三、いくつかの例
はんけいじょうきょうかい
flist 1コンパイルエラー、互換性のないパラメータタイプ、集合はAppleがFruitから継承しているが、汎用パラメータは宣言時に与えられた後に制限されているため、具体的な初期化インスタンスに従って動的に変更できないと考え、この問題を解決するために汎用子を導入した」?宣言List,初期化はFruitとそのサブクラスであればよいのでflist 3とflist 4が正しい.
package com.lxq.generics;

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

class Fruit{  
}  
class Apple extends Fruit{  
}  
class RedApple extends Apple{  
}  
class Orange extends Fruit{  
}  

public class TestGenerics
{
	//Type mismatch: cannot convert from ArrayList<Apple> to List<Fruit>
	//List<Fruit> flist1 = new ArrayList<Apple>();  
	List<Fruit> flist2 = new ArrayList<Fruit>();  
	List<? extends Fruit> flist3 = new ArrayList<Apple>();  
	List<? extends Fruit> flist4 = new ArrayList<Fruit>();  
}

汎用下限
通過受信を制限したList要素はAppleの親のみであるが、形参のタイプは実際にはListであるため、apples.add((Apple)new Fruit()である.タイプ変換が必要です.クラスが汎用クラス、すなわち宣言時にClassnameである場合、汎用下の境界ではですがは使用できません.つまりsuper以前は汎用ワイルドカードのみで、
package com.lxq.generics;

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

class Fruit{  
}  
class Apple extends Fruit{  
}  
class RedApple extends Apple{  
}  
class Orange extends Fruit{  
}  

public class TestGenerics{
	public static void main(String[] args){
		TestGenerics tg=new TestGenerics();
		tg.test(new ArrayList<Fruit>());
	}
	public  void test(List<? super Apple> apples){
		//apples.add((Apple) new Fruit());      Fruit cannot be cast to Apple
		apples.add(new Apple());
		apples.add(new RedApple());
	}
}

境界なしワイルドカード
汎用型のワイルドカードは境界を指定しなくてもよいが,境界のないワイルドカードは不確定パラメータのタイプを意味し,コンパイル時に汎用型の消しゴムタイプ情報はObjectタイプと考えられる.次のようになります.
package com.lxq.generics;

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

public class UnboundedWildcard{
	static List list1;
	static List<?> list2;
	static List<? extends Object> list3;
	static void assign1(List list){
		list1 = list;
		list2 = list;
		list3 = list; //        
	} 
	static void assign2(List<?> list){
		list1 = list;
		list2 = list;
		list3 = list;
	}
	static void assign3(List<? extends Object> list){
		list1 = list;
		list2 = list;
		list3 = list;
	}
	public static void main(String[] args){
		assign1(new ArrayList());
		assign2(new ArrayList());
		assign3(new ArrayList()); //        
		assign1(new ArrayList<String>());
		assign2(new ArrayList<String>());
		assign3(new ArrayList<String>()); 
		List<?> wildList = new ArrayList();
		assign1(wildList);
		assign2(wildList);
		assign3(wildList); 
	}
}

ListとList違いは、リストは元のタイプのリストであり、コンパイル時のタイプチェックを必要とせずに任意のObjectタイプのオブジェクトを格納することができます.ListListと等価で、元のタイプのListではなく、特定のタイプが格納されていますが、どのタイプなのかはまだ確定していません.コンパイル時にタイプチェックが必要です.従って、Listの効率はListよりも高い高いです.
四、いくつかの注意点
汎用インタフェースの実装に関する注意事項
汎用型はコンパイル中にパラメータタイプ情報を消去するため、1つのクラスでは汎用パラメータで区別された複数のインタフェースを実現できず、クラスHourlyはコンパイルできない.汎用型消去のため、PayableとPayableはコンパイル時に同じタイプのPayableであるため、1つのインタフェースを2回同時に実現できない.
interface Payable<T>{
}
class Employee implements Payable<Employee>{
}
class Hourly extends Employee implements Payable<Hourly>{
}

汎用メソッドのリロードに関する注意事項
汎用型はコンパイル時にパラメータタイプを消去するため、パラメータタイプでメソッドのリロードを行う汎用型では特に注意が必要であり、コンパイルを通過することはできない.汎用型消去タイプ情報では、上記の2つのメソッドのパラメータはいずれもObjectタイプと見なされ、パラメータタイプを使用して上記の2つのメソッドを区別することはできないため、リロードできない.
public class GenericMethod<W,T>{
<span style="white-space:pre">	</span>void f(List<T> t) {
<span style="white-space:pre">	</span>}
<span style="white-space:pre">	</span>void f(List<W> v){
<span style="white-space:pre">	</span>}
}

参考原文:
Java汎用-タイプ消去http://blog.csdn.net/caihaijiang/article/details/6403349 Java汎用http://www.cnblogs.com/friends-wf/p/3582841.html 汎用ワイルドカードとhttp://hi.baidu.com/augustus_blog/item/d 9331 b 3469 b 65 a 1 d 9 dc 65 e 69「Javaプログラミング思想」学習ノート7-汎用プログラミング基礎http://blog.csdn.net/chjttony/article/details/6785221 『Javaプログラミング思想』学習ノート8——汎用プログラミング高級http://blog.csdn.net/chjttony/article/details/6801406