Java学習整理のJava汎用型
10512 ワード
一、一つの困惑
汎用型はよく見たり使用したりしますが、ここではまず自分の困惑を記録します.次の2つの汎用型の方法にはどんな違いがありますか.
次の例を見ればわかります.
二、いくつかの概念
と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を除いて.次のコードはこの点を説明しています.
2.ここは使いやすいですが、こんなに制限が多いのは、ここではTクラスを下限とするある種類、簡単に言えばTクラスのスーパークラスという意味です.しかし、なぜadd(T)ができるのでしょうか.ある種類の容器に入れることができるので、必ずそのサブクラス、多態の概念を入れることができます.
自分の理解:,では最後に入ってきたのはTの親やTであり,本例ではAppleクラスやAppleの親であるため,Fruitクラスを追加するとFruitクラスをAppleタイプに付与する参照(伝わるのはAppleタイプかもしれないよ)が現れる.
消去
汎用性の最も挑戦的な面は消去(erasure)かもしれないが、これはJava言語で汎用性を実現する最下位技術である.消去とは、コンパイラがクラスファイルを生成するときに、パラメトリッククラスの大量のタイプ情報を基本的に放棄することを意味します.コンパイラは、プログラマが汎用的に現れる前に手動で作ったように、その強制タイプ変換でコードを生成します.
違いは、コンパイラが汎用性がなければ検証されないタイプのセキュリティ制約を多数検証し始めたことです.消去による汎用的な意味の実現は重要であり,初見も混乱している.異なるタイプであるため、ListをListに割り当てることはできませんが、
しかし、ListとListタイプの変数は同じクラスです!この点を理解するには、次のコードを評価してください.
コンパイラは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
汎用型はよく見たり使用したりしますが、ここではまず自分の困惑を記録します.次の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つの集合として理解される場合、なぜリスト
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<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のバイトコードが生成されると、そのタイプのパラメータのトレースはほとんど残りません.
汎用クラスのバイトコードが生成されると、コンパイラはタイプパラメータの消去でタイプパラメータを置き換えます.無制限タイプパラメータ(
複数の制限を持つタイプのパラメータについて、その最左の制限を持つ消去を使用します.生成されたバイトコードをチェックすると、List
マルチリミット
1つのタイプのパラメータには複数の制限があります.これは、ComparableとSerializableのようなタイプのパラメータをコンストレイントする場合に便利です.複数の制限の構文は、AND記号で制限を区切ります.
class C
ワイルドカード・タイプには、上限または下限の1つの制限があります.指定したタイプのパラメータには、1つ以上の上限があります.複数の制限を持つタイプのパラメータは、その各制限にアクセスする方法およびドメインに使用できます.
タイプパラメータとタイプ実パラメータ
パラメトリッククラスの定義では、Collection
interface Collection
では、名前V(Collectionインタフェース全体に使用可能)をタイプパラメータと呼ぶ.csの宣言では、Stringの2回の使用はタイプ実パラメータ(Collection
タイプパラメータがいつ使用できるかについては、いくつかの制限があります.ほとんどの場合、タイプパラメータは、実際のタイプ定義を使用できる任意の場所で使用できます.しかし、例外がある.オブジェクトまたは配列は作成できません.静的コンテキストまたは例外を処理するコンテキストでは使用できません.親タイプ(class Foo
同様に、タイプ実パラメータとして使用できるタイプについても、いくつかの制限がある.タイプインスタンスは、参照タイプ(基本タイプではありません)、ワイルドカード、タイプパラメータ、または他のパラメータ化タイプのインスタンス化でなければなりません.したがって、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
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